From fdd30294d89c139f98f01eda64ffaa0aeea66241 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 2 Aug 2016 16:42:44 -0400 Subject: [PATCH] passes basic reciprical test --- README.md | 15 +++++++++++++ lib/rsa-extra.js | 50 ++++++++++++++++++++++++++++++++------------ lib/rsa-forge.js | 30 +++++++++++++++----------- lib/rsa-ursa.js | 15 +++++++------ node.js | 2 ++ tests/reciprocate.js | 11 ++-------- 6 files changed, 83 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index f88fd9f..0eaa983 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ API --- * `RSA.generateKeypair(bitlen, exp, options, cb)` +* `RSA.import(keypair, options)` * `RSA.exportPrivatePem(keypair)` * `RSA.exportPublicPem(keypair)` * `RSA.exportPrivateJwk(keypair)` @@ -100,6 +101,20 @@ RSA.generateKeypair(1024, 65537, { pem: false, public: false, internal: false }, } ``` +### RSA.import(keypair, options) + +Imports keypair as JWKs and internal values `_ursa` and `_forge`. + +```javascript +var keypair = RSA.import({ privateKeyPem: '...'}); + +console.log(keypair); +``` + +```javascript +{ privateKeyPem: ..., privateKeyJwk: ..., _ursa: ..., _forge: ... } +``` + ### RSA.export*(keypair) You put in an object like `{ privateKeyPem: '...' }` or `{ publicKeyJwk: {} }` diff --git a/lib/rsa-extra.js b/lib/rsa-extra.js index a5e2737..2ba7dd1 100644 --- a/lib/rsa-extra.js +++ b/lib/rsa-extra.js @@ -1,9 +1,33 @@ 'use strict'; -function binstrToB64Url(binstr) { - return new Buffer(binstr, 'binary').toString('base64') - .replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,""); +// for forge +function _bigIntToBase64Url(fbin) { + var hex = fbin.toRadix(16); + if (hex.length % 2) { + // Invalid hex string + hex = '0' + hex; + } + var buf = Buffer.from(hex, 'hex'); + var b64 = buf.toString('base64'); + var b64Url = b64.replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,""); + + return b64Url; } +/* +// I think this doesn't work because toByteArray() returns signed bytes +function _xxx_bigIntToBase64Url(fbin) { + if (!fbin.toByteArray) { + console.log('fbin'); + console.log(fbin); + } + var byteArray = fbin.toByteArray(); + var buf = Buffer.from(byteArray); + var b64 = buf.toString('base64'); + var b64Url = b64.replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,""); + + return b64Url; +} +*/ var extrac = module.exports = { // @@ -14,22 +38,22 @@ var extrac = module.exports = { return { kty: "RSA" - , n: binstrToB64Url(k.n.toByteArray()) - , e: binstrToB64Url(k.e.toByteArray()) - , d: binstrToB64Url(k.d.toByteArray()) - , p: binstrToB64Url(k.p.toByteArray()) - , q: binstrToB64Url(k.q.toByteArray()) - , dp: binstrToB64Url(k.dP.toByteArray()) - , dq: binstrToB64Url(k.dQ.toByteArray()) - , qi: binstrToB64Url(k.qInv.toByteArray()) + , n: _bigIntToBase64Url(k.n) + , e: _bigIntToBase64Url(k.e) + , d: _bigIntToBase64Url(k.d) + , p: _bigIntToBase64Url(k.p) + , q: _bigIntToBase64Url(k.q) + , dp: _bigIntToBase64Url(k.dP) + , dq: _bigIntToBase64Url(k.dQ) + , qi: _bigIntToBase64Url(k.qInv) }; } , _forgeToPublicJwk: function (keypair) { var k = keypair._forge || keypair._forgePublic; return { kty: "RSA" - , n: binstrToB64Url(k.n.toByteArray()) - , e: binstrToB64Url(k.e.toByteArray()) + , n: _bigIntToBase64Url(k.n) + , e: _bigIntToBase64Url(k.e) }; } diff --git a/lib/rsa-forge.js b/lib/rsa-forge.js index 388152b..443d6e2 100644 --- a/lib/rsa-forge.js +++ b/lib/rsa-forge.js @@ -23,7 +23,7 @@ var forgec = module.exports = { return b64; } -, _base64ToBin: function (base64) { +, _base64UrlToBin: function (base64) { var std64 = forgec._toStandardBase64(base64); var hex = new Buffer(std64, 'base64').toString("hex"); @@ -34,7 +34,7 @@ var forgec = module.exports = { // [ 'n', 'e', 'd', 'p', 'q', 'dP', 'dQ', 'qInv' ] [ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi' ].forEach(function (key) { - components.push(forgec._base64ToBin(jwk[key])); + components.push(forgec._base64UrlToBin(jwk[key])); }); return components; @@ -42,7 +42,7 @@ var forgec = module.exports = { , _publicJwkToComponents: function (jwk) { var components = []; [ 'n', 'e' ].forEach(function (key) { - components.push(new Buffer(jwk[key], 'base64')); + components.push(forgec._base64UrlToBin(jwk[key])); }); return components; @@ -55,13 +55,15 @@ var forgec = module.exports = { // , generateKeypair: function (bitlen, exp, options, cb) { var fkeypair = forge.pki.rsa.generateKeyPair({ bits: bitlen || 1024, e: exp || 0x10001 }); - - fkeypair.toJSON = notToJson; - - cb(null, { + var result = { _forge: fkeypair.privateKey , _forgePublic: fkeypair.publicKey - }); + }; + + result._forge.toJSON = notToJson; + result._forgePublic.toJSON = notToJson; + + cb(null, result); } @@ -75,8 +77,8 @@ var forgec = module.exports = { forge.pki.rsa , forgec._privateJwkToComponents(keypair.privateKeyJwk) ); + keypair._forge.toJSON = notToJson; } - keypair._forge.toJSON = notToJson; forgec._forgeImportPublicJwk(keypair); } @@ -94,13 +96,15 @@ var forgec = module.exports = { , forgec._publicJwkToComponents(keypair.publicKeyJwk || keypair.privateKeyJwk) ); } - keypair._forgePublic.toJSON = notToJson; + if (keypair._forgePublic) { + keypair._forgePublic.toJSON = notToJson; + } } , _forgeImportPem: function (keypair) { if (!keypair._forge && keypair.privateKeyPem) { keypair._forge = forge.pki.privateKeyFromPem(keypair.privateKeyPem); + keypair._forge.toJSON = notToJson; } - keypair._forge.toJSON = notToJson; forgec._forgeImportPublicPem(keypair); } @@ -115,7 +119,9 @@ var forgec = module.exports = { else if (keypair.publicKeyPem) { keypair._forgePublic = keypair._forgePublic || forge.pki.publicKeyFromPem(keypair.publicKeyPem); } - keypair._forgePublic.toJSON = notToJson; + if (keypair._forgePublic) { + keypair._forgePublic.toJSON = notToJson; + } } , import: function (keypair) { // no-op since this must be done anyway in extra diff --git a/lib/rsa-ursa.js b/lib/rsa-ursa.js index b128af9..8c9722b 100644 --- a/lib/rsa-ursa.js +++ b/lib/rsa-ursa.js @@ -115,19 +115,22 @@ var ursac = module.exports = { // // Export Public / Private PEMs // +, _pemBinToPem: function (pem) { + return pem.toString('ascii').replace(/[\n\r]+/g, '\r\n'); + } , exportPrivatePem: function (keypair) { if (keypair.privateKeyPem) { return keypair.privateKeyPem; } if (keypair._ursa) { - return keypair._ursa.toPrivatePem().toString('ascii'); + return ursac._pemBinToPem(keypair._ursa.toPrivatePem()); } if (keypair.privateKeyJwk) { ursac._ursaImportJwk(keypair); - return keypair._ursa.toPrivatePem().toString('ascii'); + return ursac._pemBinToPem(keypair._ursa.toPrivatePem()); } throw new Error("None of privateKeyPem, _ursa, or privateKeyJwk found. No way to export private key PEM"); @@ -138,25 +141,25 @@ var ursac = module.exports = { } if (keypair._ursa || keypair._ursaPublic) { - return (keypair._ursa || keypair._ursaPublic).toPublicPem().toString('ascii'); + return ursac._pemBinToPem((keypair._ursa || keypair._ursaPublic).toPublicPem()); } if (keypair.publicKeyJwk) { ursac._ursaImportPublicJwk(keypair); - return keypair._ursaPublic.toPublicPem().toString('ascii'); + return ursac._pemBinToPem(keypair._ursaPublic.toPublicPem()); } if (keypair.privateKeyJwk) { ursac._ursaImportJwk(keypair); - return keypair._ursa.toPublicPem().toString('ascii'); + return ursac._pemBinToPem(keypair._ursa.toPublicPem()); } if (keypair.privateKeyPem) { ursac._ursaImportPem(keypair); - return keypair._ursa.toPublicPem().toString('ascii'); + return ursac._pemBinToPem(keypair._ursa.toPublicPem()); } throw new Error("None of publicKeyPem, _ursa, publicKeyJwk, privateKeyPem, or privateKeyJwk found. No way to export public key PEM"); diff --git a/node.js b/node.js index 6a16c01..18ebf00 100644 --- a/node.js +++ b/node.js @@ -110,6 +110,8 @@ function create(deps) { }; RSA.import = function (keypair/*, options*/) { + keypair = RSA._internal.import(keypair, { internal: true }); + keypair = RSA._internal.importForge(keypair, { internal: true }); //options = options || NOBJ; // ignore if (keypair.privateKeyJwk || keypair.privateKeyPem || keypair._ursa || keypair._forge) { keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keypair, { internal: true }); diff --git a/tests/reciprocate.js b/tests/reciprocate.js index b412883..ec76347 100644 --- a/tests/reciprocate.js +++ b/tests/reciprocate.js @@ -28,7 +28,6 @@ try { // PEM tests // // -console.log(''); console.log('JWK -> PEM ?', privkeyPemRef === refs.privPem); if (privkeyPemRef !== refs.privPem) { // Watch out for tricky whitespaces (\n instead of \r\n, trailing \r\n, etc) @@ -39,7 +38,6 @@ if (privkeyPemRef !== refs.privPem) { throw new Error("Failed to validate importedJwk against referencePem"); } -console.log(''); console.log('PEM -> _ -> PEM ?', privkeyPemRef === refs.privPem); if (hasUrsa) { imported = RSA.import({ privateKeyPem: privkeyPemRef }); @@ -57,17 +55,13 @@ if (privkeyPemRef !== refs.privPem2) { throw new Error("Failed to validate importedPem against referencePem"); } -console.log(''); - - // // // JWK tests // // -console.log(''); -console.log('PEM -> JWK', privkeyJwkRef.n === refs.privJwk.n); +console.log('PEM -> JWK ?', privkeyJwkRef.n === refs.privJwk.n); if (privkeyJwkRef.n !== refs.privJwk.n) { console.log('REF:'); console.log(privkeyJwkRef); @@ -76,10 +70,9 @@ if (privkeyJwkRef.n !== refs.privJwk.n) { throw new Error("Failed to validate importedPem against referenceJwk"); } -console.log(''); -console.log('JWK -> _ -> JWK', privkeyJwkRef.n === refs.privJwk2.n); imported = RSA.import({ privateKeyJwk: privkeyJwkRef }); refs.privJwk2 = RSA.exportPrivateJwk({ _forge: imported._forge }); +console.log('JWK -> _ -> JWK ?', privkeyJwkRef.n === refs.privJwk2.n); if (privkeyJwkRef.n !== refs.privJwk2.n) { console.log('REF:'); console.log(privkeyJwkRef);