moving to rsa-compat
This commit is contained in:
		
							parent
							
								
									55707a417d
								
							
						
					
					
						commit
						7504268047
					
				| @ -94,7 +94,7 @@ LeCore.getCertificate(options, cb)            // returns (err, pems={ key, cert, | ||||
|     , setChallenge: fn (hostname, key, val, cb) | ||||
|     , removeChallenge: fn (hostname, key, cb) | ||||
|     } | ||||
|      | ||||
| 
 | ||||
| // Discovery URLs | ||||
| LeCore.getAcmeUrls(acmeDiscoveryUrl, cb)      // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert}) | ||||
| ``` | ||||
| @ -138,7 +138,7 @@ For testing and development, you can also inject the dependencies you want to us | ||||
| ```javascript | ||||
| LeCore = LeCore.create({ | ||||
|   request: require('request') | ||||
| , leCrypto: rquire('./lib/letsencrypt-forge') | ||||
| , RSA: rquire('rsa-compat').RSA | ||||
| }); | ||||
| 
 | ||||
| // now uses node `request` (could also use jQuery or Angular in the browser) | ||||
|  | ||||
| @ -1,122 +0,0 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var crypto = require('crypto'); | ||||
| var forge = require('node-forge'); | ||||
| 
 | ||||
| function binstrToB64(binstr) { | ||||
|   return new Buffer(binstr, 'binary').toString('base64'); | ||||
| } | ||||
| 
 | ||||
| function b64ToBinstr(b64) { | ||||
|   return new Buffer(b64, 'base64').toString('binary'); | ||||
| } | ||||
| 
 | ||||
| function privatePemToJwk(forgePrivkey) { | ||||
|   //var forgePrivkey = forge.pki.privateKeyFromPem(privkeyPem);
 | ||||
| 
 | ||||
|   // required in node.js 4.2.2 (but not io.js 1.6.3)
 | ||||
|   Object.keys(forgePrivkey).forEach(function (k) { | ||||
|     var val = forgePrivkey[k]; | ||||
|     if (val && val.toByteArray) { | ||||
|       forgePrivkey[k] = val.toByteArray(); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   return { | ||||
|     kty: "RSA" | ||||
|   , n: binstrToB64(forgePrivkey.n) | ||||
|   , e: binstrToB64(forgePrivkey.e) | ||||
|   , d: binstrToB64(forgePrivkey.d) | ||||
|   , p: binstrToB64(forgePrivkey.p) | ||||
|   , q: binstrToB64(forgePrivkey.q) | ||||
|   , dp: binstrToB64(forgePrivkey.dP) | ||||
|   , dq: binstrToB64(forgePrivkey.dQ) | ||||
|   , qi: binstrToB64(forgePrivkey.qInv) | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function toForgePrivateKey(forgePrivkey) { | ||||
|   return forge.pki.rsa.setPrivateKey( | ||||
|     b64ToBinstr(forgePrivkey.n) | ||||
|   , b64ToBinstr(forgePrivkey.e) | ||||
|   , b64ToBinstr(forgePrivkey.d) | ||||
|   , b64ToBinstr(forgePrivkey.p) | ||||
|   , b64ToBinstr(forgePrivkey.q) | ||||
|   , b64ToBinstr(forgePrivkey.dp) | ||||
|   , b64ToBinstr(forgePrivkey.dq) | ||||
|   , b64ToBinstr(forgePrivkey.qi) | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| // WARNING: with forge this takes 20+ minutes on a Raspberry Pi!!!
 | ||||
| // It takes SEVERAL seconds even on a nice macbook pro
 | ||||
| function generateRsaKeypair(bitlen, exp, cb) { | ||||
|   var pki = forge.pki; | ||||
|   var keypair = pki.rsa.generateKeyPair({ bits: bitlen || 2048, e: exp || 65537 }); | ||||
|   var pems = { | ||||
|     publicKeyPem: pki.publicKeyToPem(keypair.publicKey)     // ascii PEM: ----BEGIN...
 | ||||
|   , privateKeyPem: pki.privateKeyToPem(keypair.privateKey)  // ascii PEM: ----BEGIN...
 | ||||
|   }; | ||||
| 
 | ||||
|   // for account id
 | ||||
|   pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex'); | ||||
|   // for compat with python client account id
 | ||||
|   pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex'); | ||||
|   // json { n: ..., e: ..., iq: ..., etc }
 | ||||
|   pems.privateKeyJwk = privatePemToJwk(keypair.privateKey); | ||||
|   // deprecate
 | ||||
|   pems.privateKeyJson = pems.privateKeyJwk; | ||||
| 
 | ||||
|   // TODO thumbprint
 | ||||
| 
 | ||||
|   cb(null, pems); | ||||
| } | ||||
| 
 | ||||
| function privateJwkToPems(pkj, cb) { | ||||
|   var pki = forge.pki; | ||||
| 
 | ||||
|   Object.keys(pkj).forEach(function (key) { | ||||
|     pkj[key] = new Buffer(pkj[key], 'base64'); | ||||
|   }); | ||||
| 
 | ||||
|   var priv; | ||||
|   var pubPem; | ||||
| 
 | ||||
|   try { | ||||
|     priv = toForgePrivateKey( | ||||
|       pkj.n // modulus
 | ||||
|     , pkj.e // exponent
 | ||||
|     , pkj.p | ||||
|     , pkj.q | ||||
|     , pkj.dp | ||||
|     , pkj.dq | ||||
|     , pkj.qi | ||||
|     , pkj.d | ||||
|     ); | ||||
|   } catch(e) { | ||||
|     cb(e); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   pubPem = pki.publicKeyToPem(priv.publicKey); | ||||
|   cb(null, { | ||||
|     publicKeyPem: pubPem                                  // ascii PEM: ----BEGIN...
 | ||||
|   , privateKeyPem: pki.privateKeyToPem(priv.privateKey)   // ascii PEM: ----BEGIN...
 | ||||
|     // json { n: ..., e: ..., iq: ..., etc }
 | ||||
|   , privateKeyJwt: pkj | ||||
|     // deprecate
 | ||||
|   , privateKeyJson: pkj | ||||
|     // I would have chosen sha1 or sha2... but whatever
 | ||||
|   , publicKeyMd5: crypto.createHash('md5').update(pubPem).digest('hex') | ||||
|   , publicKeySha256: crypto.createHash('sha256').update(pubPem).digest('hex') | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| module.exports.generateRsaKeypair = generateRsaKeypair; | ||||
| module.exports.privateJwkToPems = privateJwkToPems; | ||||
| module.exports.privatePemToJwk = privatePemToJwk; | ||||
| @ -1,368 +0,0 @@ | ||||
| // Copyright 2014 ISRG.  All rights reserved
 | ||||
| // This Source Code Form is subject to the terms of the Mozilla Public
 | ||||
| // License, v. 2.0. If a copy of the MPL was not distributed with this
 | ||||
| // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var crypto = require("crypto"); | ||||
| var tls = require("tls"); | ||||
| var forge = require("node-forge"); | ||||
| var util = require("./acme-util.js"); | ||||
| 
 | ||||
| var TOKEN_SIZE = 16; | ||||
| //var NONCE_SIZE = 16;
 | ||||
| 
 | ||||
| function bytesToBuffer(bytes) { | ||||
|   return new Buffer(forge.util.bytesToHex(bytes), "hex"); | ||||
| } | ||||
| 
 | ||||
| function bufferToBytes(buf) { | ||||
|   return forge.util.hexToBytes(buf.toString("hex")); | ||||
| } | ||||
| 
 | ||||
| function bytesToBase64(bytes) { | ||||
|   return util.b64enc(bytesToBuffer(bytes)); | ||||
| } | ||||
| 
 | ||||
| function base64ToBytes(base64) { | ||||
|   return bufferToBytes(util.b64dec(base64)); | ||||
| } | ||||
| 
 | ||||
| function bnToBase64(bn) { | ||||
|   var hex = bn.toString(16); | ||||
|   if (hex.length % 2 === 1) { hex = "0" + hex; } | ||||
|   return util.b64enc(new Buffer(hex, "hex")); | ||||
| } | ||||
| 
 | ||||
| function base64ToBn(base64) { | ||||
|   return new forge.jsbn.BigInteger(util.b64dec(base64).toString("hex"), 16); | ||||
| } | ||||
| 
 | ||||
| function importPrivateKey(privateKey) { | ||||
|   return forge.pki.rsa.setPrivateKey( | ||||
|              base64ToBn(privateKey.n), | ||||
|              base64ToBn(privateKey.e), base64ToBn(privateKey.d), | ||||
|              base64ToBn(privateKey.p), base64ToBn(privateKey.q), | ||||
|              base64ToBn(privateKey.dp),base64ToBn(privateKey.dq), | ||||
|              base64ToBn(privateKey.qi)); | ||||
| } | ||||
| 
 | ||||
| function importPublicKey(publicKey) { | ||||
|   return forge.pki.rsa.setPublicKey( | ||||
|              base64ToBn(publicKey.n), | ||||
|              base64ToBn(publicKey.e)); | ||||
| } | ||||
| 
 | ||||
| function exportPrivateKey(privateKey) { | ||||
|   return { | ||||
|     "kty": "RSA", | ||||
|     "n": bnToBase64(privateKey.n), | ||||
|     "e": bnToBase64(privateKey.e), | ||||
|     "d": bnToBase64(privateKey.d), | ||||
|     "p": bnToBase64(privateKey.p), | ||||
|     "q": bnToBase64(privateKey.q), | ||||
|     "dp": bnToBase64(privateKey.dP), | ||||
|     "dq": bnToBase64(privateKey.dQ), | ||||
|     "qi": bnToBase64(privateKey.qInv) | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function exportPublicKey(publicKey) { | ||||
|   return { | ||||
|     "kty": "RSA", | ||||
|     "n": bnToBase64(publicKey.n), | ||||
|     "e": bnToBase64(publicKey.e) | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| // A note on formats:
 | ||||
| // * Keys are always represented as JWKs
 | ||||
| // * Signature objects are in ACME format
 | ||||
| // * Certs and CSRs are base64-encoded
 | ||||
| module.exports = { | ||||
|    ///// RANDOM STRINGS
 | ||||
| 
 | ||||
|   randomString: function(nBytes) { | ||||
|     return bytesToBase64(forge.random.getBytesSync(nBytes)); | ||||
|   }, | ||||
| 
 | ||||
|   randomSerialNumber: function() { | ||||
|     return forge.util.bytesToHex(forge.random.getBytesSync(4)); | ||||
|   }, | ||||
| 
 | ||||
|   newToken: function() { | ||||
|     return this.randomString(TOKEN_SIZE); | ||||
|   }, | ||||
| 
 | ||||
|   ///// SHA-256
 | ||||
| 
 | ||||
|   sha256: function(buf) { | ||||
|     return crypto.createHash('sha256').update(buf).digest('hex'); | ||||
|   }, | ||||
| 
 | ||||
|   ///// KEY PAIR MANAGEMENT
 | ||||
| 
 | ||||
|   generateKeyPair: function(bits) { | ||||
|     var keyPair = forge.pki.rsa.generateKeyPair({bits: bits, e: 0x10001}); | ||||
|     return { | ||||
|       privateKey: exportPrivateKey(keyPair.privateKey), | ||||
|       publicKey: exportPublicKey(keyPair.publicKey) | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   importPemPrivateKey: function(pem) { | ||||
|     var key = forge.pki.privateKeyFromPem(pem); | ||||
|     return { | ||||
|       privateKey: exportPrivateKey(key), | ||||
|       publicKey: exportPublicKey(key) | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   importPemCertificate: function(pem) { | ||||
|     return forge.pki.certificateFromPem(pem); | ||||
|   }, | ||||
| 
 | ||||
|   privateKeyToPem: function(privateKey) { | ||||
|     var priv = importPrivateKey(privateKey); | ||||
|     return forge.pki.privateKeyToPem(priv); | ||||
|   }, | ||||
| 
 | ||||
|   certificateToPem: function(certificate) { | ||||
|     var derCert = base64ToBytes(certificate); | ||||
|     var cert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(derCert)); | ||||
|     return forge.pki.certificateToPem(cert); | ||||
|   }, | ||||
| 
 | ||||
|   certificateRequestToPem: function(csr) { | ||||
|     var derReq = base64ToBytes(csr); | ||||
|     var c = forge.pki.certificateFromAsn1(forge.asn1.fromDer(derReq)); | ||||
|     return forge.pki.certificateRequestToPem(c); | ||||
|   }, | ||||
| 
 | ||||
|   thumbprint: function(publicKey) { | ||||
|     // Only handling RSA keys
 | ||||
|     var input = bytesToBuffer('{"e":"'+ publicKey.e + '","kty":"RSA","n":"'+ publicKey.n +'"}'); | ||||
|     return util.b64enc(crypto.createHash('sha256').update(input).digest()); | ||||
|   }, | ||||
| 
 | ||||
|   ///// SIGNATURE GENERATION / VERIFICATION
 | ||||
| 
 | ||||
|   generateSignature: function(keyPair, payload, nonce) { | ||||
|     var privateKey = importPrivateKey(keyPair.privateKey); | ||||
| 
 | ||||
|     // Compute JWS signature
 | ||||
|     var protectedHeader = ""; | ||||
|     if (nonce) { | ||||
|       protectedHeader = JSON.stringify({nonce: nonce}); | ||||
|     } | ||||
|     var protected64 = util.b64enc(new Buffer(protectedHeader)); | ||||
|     var payload64 = util.b64enc(payload); | ||||
|     var signatureInputBuf = new Buffer(protected64 + "." + payload64); | ||||
|     var signatureInput = bufferToBytes(signatureInputBuf); | ||||
|     var md = forge.md.sha256.create(); | ||||
|     md.update(signatureInput); | ||||
|     var sig = privateKey.sign(md); | ||||
| 
 | ||||
|     return { | ||||
|       header: { | ||||
|         alg: "RS256", | ||||
|         jwk: keyPair.publicKey, | ||||
|       }, | ||||
|       protected: protected64, | ||||
|       payload: payload64, | ||||
|       signature: util.b64enc(bytesToBuffer(sig)), | ||||
|     }; | ||||
|   }, | ||||
| 
 | ||||
|   verifySignature: function(jws) { | ||||
|     var key; | ||||
| 
 | ||||
|     if (jws.protected) { | ||||
|       if (!jws.header) { | ||||
|         jws.header = {}; | ||||
|       } | ||||
| 
 | ||||
|       try { | ||||
|         console.log(jws.protected); | ||||
|         var protectedJSON = util.b64dec(jws.protected).toString(); | ||||
|         console.log(protectedJSON); | ||||
|         var protectedObj = JSON.parse(protectedJSON); | ||||
|         for (key in protectedObj) { | ||||
|           jws.header[key] = protectedObj[key]; | ||||
|         } | ||||
|       } catch (e) { | ||||
|         console.log("error unmarshaling json: "+e); | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // Assumes validSignature(sig)
 | ||||
|     if (!jws.header.jwk || (jws.header.jwk.kty !== "RSA")) { | ||||
|       // Unsupported key type
 | ||||
|       console.log("Unsupported key type"); | ||||
|       return false; | ||||
|     } else if (!jws.header.alg || !jws.header.alg.match(/^RS/)) { | ||||
|       // Unsupported algorithm
 | ||||
|       console.log("Unsupported alg: "+jws.header.alg); | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     // Compute signature input
 | ||||
|     var protected64 = (jws.protected)? jws.protected : ""; | ||||
|     var payload64 = (jws.payload)? jws.payload : ""; | ||||
|     var signatureInputBuf = new Buffer(protected64 + "." + payload64); | ||||
|     var signatureInput = bufferToBytes(signatureInputBuf); | ||||
| 
 | ||||
|     // Compute message digest
 | ||||
|     var md; | ||||
|     switch (jws.header.alg) { | ||||
|       case "RS1":   md = forge.md.sha1.create(); break; | ||||
|       case "RS256": md = forge.md.sha256.create(); break; | ||||
|       case "RS384": md = forge.md.sha384.create(); break; | ||||
|       case "RS512": md = forge.md.sha512.create(); break; | ||||
|       default: return false; // Unsupported algorithm
 | ||||
|     } | ||||
|     md.update(signatureInput); | ||||
| 
 | ||||
|     // Import the key and signature
 | ||||
|     var publicKey = importPublicKey(jws.header.jwk); | ||||
|     var sig = bufferToBytes(util.b64dec(jws.signature)); | ||||
| 
 | ||||
|     return publicKey.verify(md.digest().bytes(), sig); | ||||
|   }, | ||||
| 
 | ||||
|   ///// CSR GENERATION / VERIFICATION
 | ||||
| 
 | ||||
|   generateCSR: function(keyPair, names) { | ||||
|     var privateKey = importPrivateKey(keyPair.privateKey); | ||||
|     var publicKey = importPublicKey(keyPair.publicKey); | ||||
| 
 | ||||
|     // Create and sign the CSR
 | ||||
|     var csr = forge.pki.createCertificationRequest(); | ||||
|     csr.publicKey = publicKey; | ||||
|     csr.setSubject([{ name: 'commonName', value: names[0] }]); | ||||
| 
 | ||||
|     var sans = []; | ||||
|     var i; | ||||
| 
 | ||||
|     for (i in names) { | ||||
|       sans.push({ type: 2, value: names[i] }); | ||||
|     } | ||||
|     csr.setAttributes([{ | ||||
|       name: 'extensionRequest', | ||||
|       extensions: [{name: 'subjectAltName', altNames: sans}] | ||||
|     }]); | ||||
| 
 | ||||
|     csr.sign(privateKey, forge.md.sha256.create()); | ||||
| 
 | ||||
|     // Convert CSR -> DER -> Base64
 | ||||
|     var der = forge.asn1.toDer(forge.pki.certificationRequestToAsn1(csr)); | ||||
|     return util.b64enc(bytesToBuffer(der)); | ||||
|   }, | ||||
| 
 | ||||
|   verifiedCommonName: function(csr_b64) { | ||||
|     var der = bufferToBytes(util.b64dec(csr_b64)); | ||||
|     var csr = forge.pki.certificationRequestFromAsn1(forge.asn1.fromDer(der)); | ||||
| 
 | ||||
|     if (!csr.verify()) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     for (var i=0; i<csr.subject.attributes.length; ++i) { | ||||
|       if (csr.subject.attributes[i].name === "commonName") { | ||||
|         return csr.subject.attributes[i].value; | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   }, | ||||
| 
 | ||||
|   ///// CERTIFICATE GENERATION
 | ||||
| 
 | ||||
|   // 'ca' parameter includes information about the CA
 | ||||
|   // {
 | ||||
|   //   distinguishedName: /* forge-formatted DN */
 | ||||
|   //   keyPair: {
 | ||||
|   //     publicKey: /* JWK */
 | ||||
|   //     privateKey: /* JWK */
 | ||||
|   //   }
 | ||||
|   // }
 | ||||
|   generateCertificate: function(ca, serialNumber, csr_b64) { | ||||
|     var der = bufferToBytes(util.b64dec(csr_b64)); | ||||
|     var csr = forge.pki.certificationRequestFromAsn1(forge.asn1.fromDer(der)); | ||||
| 
 | ||||
|     // Extract the public key and common name
 | ||||
|     var publicKey = csr.publicKey; | ||||
|     var commonName = null; | ||||
|     for (var i=0; i<csr.subject.attributes.length; ++i) { | ||||
|       if (csr.subject.attributes[i].name === "commonName") { | ||||
|         commonName = csr.subject.attributes[i].value; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     if (!commonName) { return false; } | ||||
| 
 | ||||
|     // Create the certificate
 | ||||
|     var cert = forge.pki.createCertificate(); | ||||
|     cert.publicKey = publicKey; | ||||
|     cert.serialNumber = serialNumber; | ||||
| 
 | ||||
|     // 1-year validity
 | ||||
|     cert.validity.notBefore = new Date(); | ||||
|     cert.validity.notAfter = new Date(); | ||||
|     cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); | ||||
| 
 | ||||
|     cert.setSubject([{ name: "commonName", value: commonName }]); | ||||
|     cert.setIssuer(ca.distinguishedName); | ||||
|     cert.setExtensions([ | ||||
|       { name: "basicConstraints", cA: false }, | ||||
|       { name: "keyUsage", digitalSignature: true, keyEncipherment: true }, | ||||
|       { name: "extKeyUsage", serverAuth: true }, | ||||
|       { name: "subjectAltName", altNames: [{ type: 2, value: commonName }] } | ||||
|     ]); | ||||
| 
 | ||||
|     // Import signing key and sign
 | ||||
|     var privateKey = importPrivateKey(ca.keyPair.privateKey); | ||||
|     cert.sign(privateKey); | ||||
| 
 | ||||
|     // Return base64-encoded DER
 | ||||
|     der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)); | ||||
|     return bytesToBuffer(der); | ||||
|   }, | ||||
| 
 | ||||
|   generateDvsniCertificate: function(keyPair, nonceName, zName) { | ||||
|     var cert = forge.pki.createCertificate(); | ||||
|     cert.publicKey = importPublicKey(keyPair.publicKey); | ||||
|     cert.serialNumber = '01'; | ||||
|     cert.validity.notBefore = new Date(); | ||||
|     cert.validity.notAfter = new Date(); | ||||
|     cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); | ||||
|     cert.setSubject([{ name: "commonName", value: nonceName }]); | ||||
|     cert.setIssuer([{ name: "commonName", value: nonceName }]); | ||||
|     cert.setExtensions([ | ||||
|       { name: "basicConstraints", cA: false }, | ||||
|       { name: "keyUsage", digitalSignature: true, keyEncipherment: true }, | ||||
|       { name: "extKeyUsage", serverAuth: true }, | ||||
|       { name: "subjectAltName", altNames: [ | ||||
|           { type: 2, value: nonceName }, | ||||
|           { type: 2, value: zName } | ||||
|       ]} | ||||
|     ]); | ||||
|     cert.sign(importPrivateKey(keyPair.privateKey)); | ||||
| 
 | ||||
|     // Return base64-encoded DER, as above
 | ||||
|     var der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert)); | ||||
|     return util.b64enc(bytesToBuffer(der)); | ||||
|   }, | ||||
| 
 | ||||
|   ///// TLS CONTEXT GENERATION
 | ||||
| 
 | ||||
|   createContext: function(keyPair, cert) { | ||||
|     var privateKey = importPrivateKey(keyPair.privateKey); | ||||
|     var derCert = bufferToBytes(util.b64dec(cert)); | ||||
|     var realCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(derCert)); | ||||
|     return tls.createSecureContext({ | ||||
|       key: forge.pki.privateKeyToPem(privateKey), | ||||
|       cert: forge.pki.certificateToPem(realCert) | ||||
|     }).context; | ||||
|   } | ||||
| }; | ||||
| @ -1,8 +0,0 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| module.exports = {}; | ||||
| @ -1,108 +0,0 @@ | ||||
| /*! | ||||
|  * letiny-core | ||||
|  * Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
 | ||||
|  * Apache-2.0 OR MIT (and hence also MPL 2.0) | ||||
| */ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var crypto = require('crypto'); | ||||
| var ursa = require('ursa'); | ||||
| var forge = require('node-forge'); | ||||
| 
 | ||||
| function binstrToB64(binstr) { | ||||
|   return new Buffer(binstr, 'binary').toString('base64'); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| function b64ToBinstr(b64) { | ||||
|   return new Buffer(b64, 'base64').toString('binary'); | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| function privatePemToJwk(privkeyPem) { | ||||
|   var forgePrivkey = forge.pki.privateKeyFromPem(privkeyPem); | ||||
| 
 | ||||
|   // required in node.js 4.2.2 (but not io.js 1.6.3)
 | ||||
|   Object.keys(forgePrivkey).forEach(function (k) { | ||||
|     var val = forgePrivkey[k]; | ||||
|     if (val && val.toByteArray) { | ||||
|       forgePrivkey[k] = val.toByteArray(); | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   return { | ||||
|     kty: "RSA" | ||||
|   , n: binstrToB64(forgePrivkey.n) | ||||
|   , e: binstrToB64(forgePrivkey.e) | ||||
|   , d: binstrToB64(forgePrivkey.d) | ||||
|   , p: binstrToB64(forgePrivkey.p) | ||||
|   , q: binstrToB64(forgePrivkey.q) | ||||
|   , dp: binstrToB64(forgePrivkey.dP) | ||||
|   , dq: binstrToB64(forgePrivkey.dQ) | ||||
|   , qi: binstrToB64(forgePrivkey.qInv) | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function generateRsaKeypair(bitlen, exp, cb) { | ||||
|   var keypair = ursa.generatePrivateKey(bitlen || 2048, exp || 6553); | ||||
|   var pems = { | ||||
|     publicKeyPem: keypair.toPublicPem().toString('ascii')   // ascii PEM: ----BEGIN...
 | ||||
|   , privateKeyPem: keypair.toPrivatePem().toString('ascii') // ascii PEM: ----BEGIN...
 | ||||
|   }; | ||||
| 
 | ||||
|   // for account id
 | ||||
|   pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex'); | ||||
|   // for compat with python client account id
 | ||||
|   pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex'); | ||||
|   // json { n: ..., e: ..., iq: ..., etc }
 | ||||
|   pems.privateKeyJwk = privatePemToJwk(pems.privateKeyPem); | ||||
|   pems.privateKeyJson = pems.privateKeyJwk; | ||||
| 
 | ||||
|   // TODO thumbprint
 | ||||
| 
 | ||||
|   cb(null, pems); | ||||
| } | ||||
| 
 | ||||
| function privateJwkToPems(pkj, cb) { | ||||
|   Object.keys(pkj).forEach(function (key) { | ||||
|     pkj[key] = new Buffer(pkj[key], 'base64'); | ||||
|   }); | ||||
| 
 | ||||
|   var priv; | ||||
|   var pems; | ||||
| 
 | ||||
|   try { | ||||
|     priv = ursa.createPrivateKeyFromComponents( | ||||
|       pkj.n // modulus
 | ||||
|     , pkj.e // exponent
 | ||||
|     , pkj.p | ||||
|     , pkj.q | ||||
|     , pkj.dp | ||||
|     , pkj.dq | ||||
|     , pkj.qi | ||||
|     , pkj.d | ||||
|     ); | ||||
|   } catch(e) { | ||||
|     cb(e); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   pems = { | ||||
|     privateKeyPem: priv.toPrivatePem().toString('ascii') | ||||
|   , publicKeyPem: priv.toPublicPem().toString('ascii') | ||||
|   }; | ||||
| 
 | ||||
|   // for account id
 | ||||
|   pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex'); | ||||
|   // for compat with python client account id
 | ||||
|   pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex'); | ||||
|   // json { n: ..., e: ..., iq: ..., etc }
 | ||||
|   pems.privateKeyJwk = privatePemToJwk(pems.privateKeyPem); | ||||
|   pems.privateKeyJson = pems.privateKeyJwk; | ||||
| 
 | ||||
|   cb(null, pems); | ||||
| } | ||||
| 
 | ||||
| module.exports.generateRsaKeypair = generateRsaKeypair; | ||||
| module.exports.privateJwkToPems = privateJwkToPems; | ||||
| module.exports.privatePemToJwk = privatePemToJwk; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user