passes basic reciprical test

This commit is contained in:
AJ ONeal 2016-08-02 16:42:44 -04:00
parent 5de4362c6c
commit fdd30294d8
6 changed files with 83 additions and 40 deletions

View File

@ -65,6 +65,7 @@ API
--- ---
* `RSA.generateKeypair(bitlen, exp, options, cb)` * `RSA.generateKeypair(bitlen, exp, options, cb)`
* `RSA.import(keypair, options)`
* `RSA.exportPrivatePem(keypair)` * `RSA.exportPrivatePem(keypair)`
* `RSA.exportPublicPem(keypair)` * `RSA.exportPublicPem(keypair)`
* `RSA.exportPrivateJwk(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) ### RSA.export*(keypair)
You put in an object like `{ privateKeyPem: '...' }` or `{ publicKeyJwk: {} }` You put in an object like `{ privateKeyPem: '...' }` or `{ publicKeyJwk: {} }`

View File

@ -1,9 +1,33 @@
'use strict'; 'use strict';
function binstrToB64Url(binstr) { // for forge
return new Buffer(binstr, 'binary').toString('base64') function _bigIntToBase64Url(fbin) {
.replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,""); 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 = { var extrac = module.exports = {
// //
@ -14,22 +38,22 @@ var extrac = module.exports = {
return { return {
kty: "RSA" kty: "RSA"
, n: binstrToB64Url(k.n.toByteArray()) , n: _bigIntToBase64Url(k.n)
, e: binstrToB64Url(k.e.toByteArray()) , e: _bigIntToBase64Url(k.e)
, d: binstrToB64Url(k.d.toByteArray()) , d: _bigIntToBase64Url(k.d)
, p: binstrToB64Url(k.p.toByteArray()) , p: _bigIntToBase64Url(k.p)
, q: binstrToB64Url(k.q.toByteArray()) , q: _bigIntToBase64Url(k.q)
, dp: binstrToB64Url(k.dP.toByteArray()) , dp: _bigIntToBase64Url(k.dP)
, dq: binstrToB64Url(k.dQ.toByteArray()) , dq: _bigIntToBase64Url(k.dQ)
, qi: binstrToB64Url(k.qInv.toByteArray()) , qi: _bigIntToBase64Url(k.qInv)
}; };
} }
, _forgeToPublicJwk: function (keypair) { , _forgeToPublicJwk: function (keypair) {
var k = keypair._forge || keypair._forgePublic; var k = keypair._forge || keypair._forgePublic;
return { return {
kty: "RSA" kty: "RSA"
, n: binstrToB64Url(k.n.toByteArray()) , n: _bigIntToBase64Url(k.n)
, e: binstrToB64Url(k.e.toByteArray()) , e: _bigIntToBase64Url(k.e)
}; };
} }

View File

@ -23,7 +23,7 @@ var forgec = module.exports = {
return b64; return b64;
} }
, _base64ToBin: function (base64) { , _base64UrlToBin: function (base64) {
var std64 = forgec._toStandardBase64(base64); var std64 = forgec._toStandardBase64(base64);
var hex = new Buffer(std64, 'base64').toString("hex"); 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', 'qInv' ]
[ 'n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi' ].forEach(function (key) { [ '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; return components;
@ -42,7 +42,7 @@ var forgec = module.exports = {
, _publicJwkToComponents: function (jwk) { , _publicJwkToComponents: function (jwk) {
var components = []; var components = [];
[ 'n', 'e' ].forEach(function (key) { [ 'n', 'e' ].forEach(function (key) {
components.push(new Buffer(jwk[key], 'base64')); components.push(forgec._base64UrlToBin(jwk[key]));
}); });
return components; return components;
@ -55,13 +55,15 @@ var forgec = module.exports = {
// //
, generateKeypair: function (bitlen, exp, options, cb) { , generateKeypair: function (bitlen, exp, options, cb) {
var fkeypair = forge.pki.rsa.generateKeyPair({ bits: bitlen || 1024, e: exp || 0x10001 }); var fkeypair = forge.pki.rsa.generateKeyPair({ bits: bitlen || 1024, e: exp || 0x10001 });
var result = {
fkeypair.toJSON = notToJson;
cb(null, {
_forge: fkeypair.privateKey _forge: fkeypair.privateKey
, _forgePublic: fkeypair.publicKey , _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 forge.pki.rsa
, forgec._privateJwkToComponents(keypair.privateKeyJwk) , forgec._privateJwkToComponents(keypair.privateKeyJwk)
); );
}
keypair._forge.toJSON = notToJson; keypair._forge.toJSON = notToJson;
}
forgec._forgeImportPublicJwk(keypair); forgec._forgeImportPublicJwk(keypair);
} }
@ -94,13 +96,15 @@ var forgec = module.exports = {
, forgec._publicJwkToComponents(keypair.publicKeyJwk || keypair.privateKeyJwk) , forgec._publicJwkToComponents(keypair.publicKeyJwk || keypair.privateKeyJwk)
); );
} }
if (keypair._forgePublic) {
keypair._forgePublic.toJSON = notToJson; keypair._forgePublic.toJSON = notToJson;
} }
}
, _forgeImportPem: function (keypair) { , _forgeImportPem: function (keypair) {
if (!keypair._forge && keypair.privateKeyPem) { if (!keypair._forge && keypair.privateKeyPem) {
keypair._forge = forge.pki.privateKeyFromPem(keypair.privateKeyPem); keypair._forge = forge.pki.privateKeyFromPem(keypair.privateKeyPem);
}
keypair._forge.toJSON = notToJson; keypair._forge.toJSON = notToJson;
}
forgec._forgeImportPublicPem(keypair); forgec._forgeImportPublicPem(keypair);
} }
@ -115,8 +119,10 @@ var forgec = module.exports = {
else if (keypair.publicKeyPem) { else if (keypair.publicKeyPem) {
keypair._forgePublic = keypair._forgePublic || forge.pki.publicKeyFromPem(keypair.publicKeyPem); keypair._forgePublic = keypair._forgePublic || forge.pki.publicKeyFromPem(keypair.publicKeyPem);
} }
if (keypair._forgePublic) {
keypair._forgePublic.toJSON = notToJson; keypair._forgePublic.toJSON = notToJson;
} }
}
, import: function (keypair) { , import: function (keypair) {
// no-op since this must be done anyway in extra // no-op since this must be done anyway in extra
return keypair; return keypair;

View File

@ -115,19 +115,22 @@ var ursac = module.exports = {
// //
// Export Public / Private PEMs // Export Public / Private PEMs
// //
, _pemBinToPem: function (pem) {
return pem.toString('ascii').replace(/[\n\r]+/g, '\r\n');
}
, exportPrivatePem: function (keypair) { , exportPrivatePem: function (keypair) {
if (keypair.privateKeyPem) { if (keypair.privateKeyPem) {
return keypair.privateKeyPem; return keypair.privateKeyPem;
} }
if (keypair._ursa) { if (keypair._ursa) {
return keypair._ursa.toPrivatePem().toString('ascii'); return ursac._pemBinToPem(keypair._ursa.toPrivatePem());
} }
if (keypair.privateKeyJwk) { if (keypair.privateKeyJwk) {
ursac._ursaImportJwk(keypair); 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"); 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) { if (keypair._ursa || keypair._ursaPublic) {
return (keypair._ursa || keypair._ursaPublic).toPublicPem().toString('ascii'); return ursac._pemBinToPem((keypair._ursa || keypair._ursaPublic).toPublicPem());
} }
if (keypair.publicKeyJwk) { if (keypair.publicKeyJwk) {
ursac._ursaImportPublicJwk(keypair); ursac._ursaImportPublicJwk(keypair);
return keypair._ursaPublic.toPublicPem().toString('ascii'); return ursac._pemBinToPem(keypair._ursaPublic.toPublicPem());
} }
if (keypair.privateKeyJwk) { if (keypair.privateKeyJwk) {
ursac._ursaImportJwk(keypair); ursac._ursaImportJwk(keypair);
return keypair._ursa.toPublicPem().toString('ascii'); return ursac._pemBinToPem(keypair._ursa.toPublicPem());
} }
if (keypair.privateKeyPem) { if (keypair.privateKeyPem) {
ursac._ursaImportPem(keypair); 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"); throw new Error("None of publicKeyPem, _ursa, publicKeyJwk, privateKeyPem, or privateKeyJwk found. No way to export public key PEM");

View File

@ -110,6 +110,8 @@ function create(deps) {
}; };
RSA.import = function (keypair/*, options*/) { RSA.import = function (keypair/*, options*/) {
keypair = RSA._internal.import(keypair, { internal: true });
keypair = RSA._internal.importForge(keypair, { internal: true });
//options = options || NOBJ; // ignore //options = options || NOBJ; // ignore
if (keypair.privateKeyJwk || keypair.privateKeyPem || keypair._ursa || keypair._forge) { if (keypair.privateKeyJwk || keypair.privateKeyPem || keypair._ursa || keypair._forge) {
keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keypair, { internal: true }); keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keypair, { internal: true });

View File

@ -28,7 +28,6 @@ try {
// PEM tests // PEM tests
// //
// //
console.log('');
console.log('JWK -> PEM ?', privkeyPemRef === refs.privPem); console.log('JWK -> PEM ?', privkeyPemRef === refs.privPem);
if (privkeyPemRef !== refs.privPem) { if (privkeyPemRef !== refs.privPem) {
// Watch out for tricky whitespaces (\n instead of \r\n, trailing \r\n, etc) // 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"); throw new Error("Failed to validate importedJwk against referencePem");
} }
console.log('');
console.log('PEM -> _ -> PEM ?', privkeyPemRef === refs.privPem); console.log('PEM -> _ -> PEM ?', privkeyPemRef === refs.privPem);
if (hasUrsa) { if (hasUrsa) {
imported = RSA.import({ privateKeyPem: privkeyPemRef }); imported = RSA.import({ privateKeyPem: privkeyPemRef });
@ -57,17 +55,13 @@ if (privkeyPemRef !== refs.privPem2) {
throw new Error("Failed to validate importedPem against referencePem"); throw new Error("Failed to validate importedPem against referencePem");
} }
console.log('');
// //
// //
// JWK tests // 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) { if (privkeyJwkRef.n !== refs.privJwk.n) {
console.log('REF:'); console.log('REF:');
console.log(privkeyJwkRef); console.log(privkeyJwkRef);
@ -76,10 +70,9 @@ if (privkeyJwkRef.n !== refs.privJwk.n) {
throw new Error("Failed to validate importedPem against referenceJwk"); 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 }); imported = RSA.import({ privateKeyJwk: privkeyJwkRef });
refs.privJwk2 = RSA.exportPrivateJwk({ _forge: imported._forge }); refs.privJwk2 = RSA.exportPrivateJwk({ _forge: imported._forge });
console.log('JWK -> _ -> JWK ?', privkeyJwkRef.n === refs.privJwk2.n);
if (privkeyJwkRef.n !== refs.privJwk2.n) { if (privkeyJwkRef.n !== refs.privJwk2.n) {
console.log('REF:'); console.log('REF:');
console.log(privkeyJwkRef); console.log(privkeyJwkRef);