From 8fa54ad4d785b8cf1a31b4c5129ee8f988e1721b Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 30 Jul 2016 23:47:52 -0400 Subject: [PATCH] v1.0.0 --- README.md | 55 +++++------------- lib/node.js | 22 +++---- lib/rsa-extra.js | 129 ++++++++++++++++++++++++++++++++++++++++++ lib/rsa-forge.js | 5 +- lib/rsa-ursa.js | 4 +- node.js | 15 +++-- tests/generate-key.js | 16 +++++- 7 files changed, 179 insertions(+), 67 deletions(-) create mode 100644 lib/rsa-extra.js diff --git a/README.md b/README.md index 6ee60ac..d6d3dbf 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,11 @@ RSA.generateKeypair(bitlen, exp, options).then(function (keypair) { `console.log(keypair)`: ```javascript -// http://crypto.stackexchange.com/questions/6593/what-data-is-saved-in-rsa-private-key -{ publicKeyPem: '/*base64 pem-encoded string*/' -, privateKeyPem: '/*base64 pem-encoded string*/' +{ publicKeyPem: '-----BEGIN RSA PUBLIC KEY-----\n/*base64 pem-encoded string*/' +, privateKeyPem: '-----BEGIN RSA PRIVATE KEY-----\n/*base64 pem-encoded string*/' + + // http://crypto.stackexchange.com/questions/6593/what-data-is-saved-in-rsa-private-key , privateKeyJwk: { kty: "RSA" , n: '/*base64 modulus n = pq*/' @@ -47,8 +48,11 @@ RSA.generateKeypair(bitlen, exp, options).then(function (keypair) { , n: /*base64 modulus n = pq*/ , e: /base64 exponent (usually 65537)*/ } + , _ursa: /*undefined or intermediate ursa object*/ +, _ursaPublic: /*undefined or intermediate ursa object*/ , _forge: /*undefined or intermediate forge object*/ +, _forgePublic: /*undefined or intermediate forge object*/ } // NOTE: this object is JSON safe as _ursa and _forge will be ignored @@ -59,7 +63,12 @@ API --- * `RSA.generateKeypair(bitlen, exp, options, cb)` -* `RSA.importPemPrivateKey(privatePem)` +* `RSA.exportPrivatePem(keypair)` +* `RSA.exportPublicPem(keypair)` +* `RSA.exportPrivateJwk(keypair)` +* `RSA.exportPublicJwk(keypair)` + +`keypair` can be any object with any of these keys `publicKeyPem, privateKeyPem, publicKeyJwk, privateKeyJwk` ### RSA.generateKeypair(bitlen, exp, options, cb) @@ -85,41 +94,3 @@ RSA.generateKeypair(1024, 65537, { pem: false, public: false, internal: false }, , fingerprint: false // NOT IMPLEMENTED (RSA key fingerprint) } ``` - -### RSA.import(keypair, options, cb) - -Import a private key or public key as PEM, JWK, and/or internal formats - -`rsa`: -```javascript -{ publicKeyPem: '...' -, privateKeyPem: '...' -, privateKeyJwk: { /*...*/ } -, publicKeyJwk: { /*...*/ } -, _ursa: '[Object object]' -, _forge: '[Object object]' -} -``` - -`options`: -``` -// same as above, except the following are also added -{ private: true // export private key - // (as opposed to using a private key - // solely to export the public key) -} -``` - -### Other - -(the code is there, but they aren't exposed yet) - -* `toStandardB64(certbuf.toString('base64'))` -* `thumbprint(publicPem)` -* `generateCsr(privateKeyPem, ['example.com'])` - -``` - cert = toStandardB64(certbuf.toString('base64')) - cert=cert.match(/.{1,64}/g).join('\n'); - return '-----BEGIN CERTIFICATE-----\n'+cert+'\n-----END CERTIFICATE-----'; -``` diff --git a/lib/node.js b/lib/node.js index dd82436..d9b923d 100644 --- a/lib/node.js +++ b/lib/node.js @@ -8,12 +8,12 @@ var cryptoc = module.exports; var rsaExtra = require('./rsa-extra'); var rsaForge = require('./rsa-forge'); -var ursac; +var rsaUrsa; try { - ursac = require('./rsa-ursa'); + rsaUrsa = require('./rsa-ursa'); } catch(e) { - ursac = {}; + rsaUrsa = {}; // things will run a little slower on keygen, but it'll work on windows // (but don't try this on raspberry pi - 20+ MINUTES key generation) } @@ -23,15 +23,9 @@ try { // * ursa // * forge extra (the new one aimed to be less-forgey) // * forge (fallback) -Object.keys(ursac).forEach(function (key) { +Object.keys(rsaUrsa).forEach(function (key) { if (!cryptoc[key]) { - cryptoc[key] = ursac[key]; - } -}); - -Object.keys(rsaExtra).forEach(function (key) { - if (!cryptoc[key]) { - cryptoc[key] = rsaExtra[key]; + cryptoc[key] = rsaUrsa[key]; } }); @@ -40,3 +34,9 @@ Object.keys(rsaForge).forEach(function (key) { cryptoc[key] = rsaForge[key]; } }); + +Object.keys(rsaExtra).forEach(function (key) { + if (!cryptoc[key]) { + cryptoc[key] = rsaExtra[key]; + } +}); diff --git a/lib/rsa-extra.js b/lib/rsa-extra.js new file mode 100644 index 0000000..981645d --- /dev/null +++ b/lib/rsa-extra.js @@ -0,0 +1,129 @@ +'use strict'; + +//var crypto = require('crypto'); +var forge = require('node-forge'); + +function binstrToB64(binstr) { + return new Buffer(binstr, 'binary').toString('base64'); +} + +/* + 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); + }, +*/ + +var extrac = module.exports = { + // + // internals + // + _forgeToPrivateJwk: function (keypair) { + var k = keypair._forge.privateKey; + + return { + kty: "RSA" + , n: binstrToB64(k.n.toByteArray()) + , e: binstrToB64(k.e.toByteArray()) + , d: binstrToB64(k.d.toByteArray()) + , p: binstrToB64(k.p.toByteArray()) + , q: binstrToB64(k.q.toByteArray()) + , dp: binstrToB64(k.dP.toByteArray()) + , dq: binstrToB64(k.dQ.toByteArray()) + , qi: binstrToB64(k.qInv.toByteArray()) + }; + } +, _forgeToPublicJwk: function (keypair) { + var k = keypair._forge.privateKey || keypair._forge.publicKey; + return { + kty: "RSA" + , n: binstrToB64(k.n.toByteArray()) + , e: binstrToB64(k.e.toByteArray()) + }; + } + + + + // + // Export JWK + // +, exportPrivateJwk: function (keypair) { + var hasUrsaPrivate = keypair._ursa && true; + var hasPrivatePem = keypair.privateKeyPem && true; + var hasForgePrivate = keypair._forge && keypair._forge.privateKey && true; + + if (keypair.privateKeyJwk) { + return keypair.privateKeyJwk; + } + + if (!hasForgePrivate) { + if (hasUrsaPrivate && !hasPrivatePem) { + keypair.privateKeyPem = keypair._ursa.toPrivatePem().toString('ascii'); + } + + if (keypair.privateKeyPem) { + keypair._forge = { privateKey: forge.pki.privateKeyFromPem(keypair.privateKeyPem) }; + } + } + + if (keypair._forge && keypair._forge.privateKey) { + return extrac._forgeToPrivateJwk(keypair); + } + + throw new Error("None of privateKeyPem, _ursa, _forge, or privateKeyJwk found. No way to export private key Jwk"); + } +, exportPublicJwk: function (keypair) { + var hasUrsaPublic = (keypair._ursa || keypair._ursaPublic) && true; + var hasPublicPem = (keypair.privateKeyPem || keypair.publicKeyPem) && true; + var hasForgePublic = keypair._forge && true; + + if (keypair.publicKeyJwk) { + return keypair.publicKeyJwk; + } + + if (keypair.privateKeyJwk) { + return { + kty: 'RSA' + , n: keypair.privateKeyJwk.n + , e: keypair.privateKeyJwk.e + }; + } + + if (!hasForgePublic) { + if (hasUrsaPublic && !hasPublicPem) { + keypair.publicKeyPem = (keypair._ursa || keypair._ursaPublic).toPublicPem().toString('ascii'); + } + + if (keypair.publicKeyPem) { + keypair._forge = { privateKey: forge.pki.publicKeyFromPem(keypair.publicKeyPem) }; + } + } + + if (keypair._forge && keypair._forge.privateKey) { + return extrac._forgeToPublicJwk(keypair); + } + + throw new Error("None of publicKeyPem privateKeyPem, _ursa, _forge, publicKeyJwk, or privateKeyJwk found. No way to export private key Jwk"); + } + + + +}; diff --git a/lib/rsa-forge.js b/lib/rsa-forge.js index a617e99..82bcf80 100644 --- a/lib/rsa-forge.js +++ b/lib/rsa-forge.js @@ -41,6 +41,7 @@ var forgec = module.exports = { , _privateJwkToComponents: function (jwk) { var components = []; + // [ 'n', 'e', 'd', 'p', 'q', 'dP', 'dQ', 'qInv' ] [ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi' ].forEach(function (key) { components.push(new forgec._base64tobin(jwk[key])); }); @@ -76,7 +77,7 @@ var forgec = module.exports = { // // Export Public / Private PEMs // -, exportPrivateKeyPem: function (keypair) { +, exportPrivatePem: function (keypair) { if (keypair.privateKeyPem) { return keypair.privateKeyPem; } @@ -97,7 +98,7 @@ var forgec = module.exports = { throw new Error("None of privateKeyPem, _forge, or privateKeyJwk found. No way to export private key PEM"); } -, exportPublicKeyPem: function (keypair) { +, exportPublicPem: function (keypair) { if (keypair.publicKeyPem) { return keypair.publicKeyPem; } diff --git a/lib/rsa-ursa.js b/lib/rsa-ursa.js index 4011784..ce562f8 100644 --- a/lib/rsa-ursa.js +++ b/lib/rsa-ursa.js @@ -51,7 +51,7 @@ var ursac = module.exports = { // // Export Public / Private PEMs // -, exportPrivateKeyPem: function (keypair) { +, exportPrivatePem: function (keypair) { if (keypair.privateKeyPem) { return keypair.privateKeyPem; } @@ -72,7 +72,7 @@ var ursac = module.exports = { throw new Error("None of privateKeyPem, _ursa, or privateKeyJwk found. No way to export private key PEM"); } -, exportPublicKeyPem: function (keypair) { +, exportPublicPem: function (keypair) { if (keypair.publicKeyPem) { return keypair.publicKeyPem; } diff --git a/node.js b/node.js index 2b17bcc..33bac2d 100644 --- a/node.js +++ b/node.js @@ -43,18 +43,11 @@ function create(deps) { options = options || NOBJ; - RSA._internal.generateKeypair(length, exponent, options, function (keys) { + RSA._internal.generateKeypair(length, exponent, options, function (err, keys) { if (false !== options.jwk || options.thumbprint) { keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keys); if (options.public) { keypair.publicKeyJwk = RSA._internal.exportPublicJwk(keys); - /* - return { - kty: keypair.privateKeyJwk.kty - , n: keypair.privateKeyJwk.n - , e: keypair.privateKeyJwk.e - }; - */ } } @@ -81,6 +74,12 @@ function create(deps) { }); }; + RSA.exportPrivateKey = RSA._internal.exportPrivatePem; + RSA.exportPublicKey = RSA._internal.exportPublicPem; + + RSA.exportPrivateJwk = RSA._internal.exportPrivateJwk; + RSA.exportPublicJwk = RSA._internal.exportPublicJwk; + return RSA; } diff --git a/tests/generate-key.js b/tests/generate-key.js index f072b14..5e5db45 100644 --- a/tests/generate-key.js +++ b/tests/generate-key.js @@ -5,7 +5,7 @@ var RSA = require('../').RSA; console.log('RSA'); console.log(RSA); -RSA.generateKeypair(null, null, null, function (keys) { +RSA.generateKeypair(null, null, null, function (err, keys) { console.log(''); console.log('keys'); console.log(keys); @@ -33,10 +33,22 @@ RSA.generateKeypair(null, null, null, function (keys) { , internal: true // preserve internal intermediate formats (_ursa, _forge) , thumbprint: true // JWK sha256 thumbprint }; - RSA.generateKeypair(512, 65537, options, function (keys) { + RSA.generateKeypair(512, 65537, options, function (err, keys) { console.log(''); console.log('keys'); console.log(keys); + + if ( + keys.publicKeyJwk + || keys.privateKeyPem + || keys.publicKeyPem + || keys.thumbprint + || keys._ursa + || keys._forge + ) { + console.error(keys); + throw new Error("Got unexpected keys"); + } }); });