partial implementations

This commit is contained in:
AJ ONeal 2016-07-30 16:00:08 -04:00
parent 2aced3a54d
commit 0ebedd4e74
4 changed files with 328 additions and 0 deletions

76
lib/key-utils.js Normal file
View File

@ -0,0 +1,76 @@
// 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 utils = {
fromStandardB64: function(x) {
return x.replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,"");
},
toStandardB64: function(x) {
var b64 = x.replace(/-/g, "+").replace(/_/g, "/").replace(/=/g, "");
switch (b64.length % 4) {
case 2: b64 += "=="; break;
case 3: b64 += "="; break;
}
return b64;
},
b64enc: function(buffer) {
return utils.fromStandardB64(buffer.toString("base64"));
},
b64dec: function(str) {
return new Buffer(utils.toStandardB64(str), "base64");
},
isB64String: function(x) {
return ("string" === typeof x) && !x.match(/[^a-zA-Z0-9_-]/);
},
fieldsPresent: function(fields, object) {
for (var i in fields) {
if (!(fields[i] in object)) {
return false;
}
}
return true;
},
validSignature: function(sig) {
return (("object" === typeof sig) &&
("alg" in sig) && ("string" === typeof sig.alg) &&
("nonce" in sig) && utils.isB64String(sig.nonce) &&
("sig" in sig) && utils.isB64String(sig.sig) &&
("jwk" in sig) && utils.validJWK(sig.jwk));
},
validJWK: function(jwk) {
return (("object" === typeof jwk) && ("kty" in jwk) && (
((jwk.kty === "RSA")
&& ("n" in jwk) && utils.isB64String(jwk.n)
&& ("e" in jwk) && utils.isB64String(jwk.e)) ||
((jwk.kty === "EC")
&& ("crv" in jwk)
&& ("x" in jwk) && utils.isB64String(jwk.x)
&& ("y" in jwk) && utils.isB64String(jwk.y))
) && !("d" in jwk));
},
// A simple, non-standard fingerprint for a JWK,
// just so that we don't have to store objects
keyFingerprint: function(jwk) {
switch (jwk.kty) {
case "RSA": return jwk.n;
case "EC": return jwk.crv + jwk.x + jwk.y;
}
throw "Unrecognized key type";
}
};
module.exports = utils;

44
lib/node.js Normal file
View File

@ -0,0 +1,44 @@
/*!
* rsa-compat
* Copyright(c) 2016 AJ ONeal <aj@daplie.com> https://daplie.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
var cryptoc = {};
var rsaExtra = require('./rsa-extra');
var rsaForge = require('./rsa-forge');
var ursac;
try {
ursac = require('./rsa-ursa');
} catch(e) {
ursac = {};
// 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)
}
// order of crypto precdence is
// * native
// * ursa
// * forge extra (the new one aimed to be less-forgey)
// * forge (fallback)
Object.keys(ursac).forEach(function (key) {
if (!cryptoc[key]) {
cryptoc[key] = ursac[key];
}
});
Object.keys(rsaExtra).forEach(function (key) {
if (!cryptoc[key]) {
cryptoc[key] = rsaExtra[key];
}
});
Object.keys(rsaForge).forEach(function (key) {
if (!cryptoc[key]) {
cryptoc[key] = rsaForge[key];
}
});
module.exports.cryptoc = cryptoc;

120
lib/rsa-ursa.js Normal file
View File

@ -0,0 +1,120 @@
'use strict';
var ursa = require('ursa');
function notToJson() {
return undefined;
}
var ursac = {
//
// to components
//
_privateJwkToComponents: function (jwk) {
var components = [];
[ 'n', 'e', 'p', 'q', 'dp', 'dq', 'qi', 'd' ].forEach(function (key) {
components.push(new Buffer(jwk[key], 'base64'));
});
return components;
}
, _publicJwkToComponents: function (jwk) {
var components = [];
[ 'n', 'e' ].forEach(function (key) {
components.push(new Buffer(jwk[key], 'base64'));
});
return components;
}
//
// Generate New Keypair
//
, generateKeypair: function (bitlen, exp, options, cb) {
var keypair = ursa.generatePrivateKey(bitlen || 2048, exp || 6553);
keypair.toJSON = notToJson;
cb(null, {
_ursa: keypair
});
}
//
// Export Public / Private PEMs
//
, exportPrivateKeyPem: function (keypair) {
if (keypair.privateKeyPem) {
return keypair.privateKeyPem;
}
if (keypair._ursa) {
return keypair._ursa.toPrivatePem().toString('ascii');
}
if (keypair.privateKeyJwk) {
keypair._ursa = ursa.createPrivateKeyFromComponents.apply(
ursa
, ursac._privateJwkToComponents(keypair.privateKeyJwk)
);
keypair._ursa.toJSON = notToJson;
return keypair._ursa.toPrivatePem().toString('ascii');
}
throw new Error("None of privateKeyPem, _ursa, or privateKeyJwk found. No way to export private key PEM");
}
, exportPublicKeyPem: function (keypair) {
if (keypair.publicKeyPem) {
return keypair.publicKeyPem;
}
if (keypair._ursa || keypair._ursaPublic) {
return (keypair._ursa || keypair._ursaPublic).toPublicPem().toString('ascii');
}
if (keypair.publicKeyJwk) {
keypair._ursaPublic = ursa.createPublicKeyFromComponents.apply(
ursa
, ursac._publicJwkToComponents(keypair.publicKeyJwk)
);
keypair._ursaPublic.toJSON = notToJson;
return keypair._ursa.toPublicPem().toString('ascii');
}
if (keypair.privateKeyJwk) {
keypair._ursa = ursa.createPrivateKeyFromComponents.apply(
ursa
, ursac._privateJwkToComponents(keypair.privateKeyJwk)
);
keypair._ursa.toJSON = notToJson;
return keypair._ursa.toPublicPem().toString('ascii');
}
if (keypair.privateKeyPem) {
keypair._ursa = ursa.createPrivateKey(keypair.privateKeyPem);
keypair._ursa.toJSON = notToJson;
return keypair._ursa.toPublicPem().toString('ascii');
}
throw new Error("None of publicKeyPem, _ursa, publicKeyJwk, privateKeyPem, or privateKeyJwk found. No way to export public key PEM");
}
//, exportPrivateKeyJwk: NOT IMPLEMENTED HERE
//, exportPublicKeyJwk: NOT IMPLEMENTED HERE
};
return ursac;

88
node.js Normal file
View File

@ -0,0 +1,88 @@
/*!
* rsa-compat
* Copyright(c) 2016 AJ ONeal <aj@daplie.com> https://daplie.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
var RSA = {};
var NOBJ = {};
function create(deps) {
var crypto = require('crypto');
deps = deps || {};
deps.NOBJ = {};
deps.RSA = RSA;
RSA.utils = require('./lib/key-utils.js');
RSA.utils._bytesToBuffer = function (bytes) {
var forge = require("node-forge");
return new Buffer(forge.util.bytesToHex(bytes), "hex");
};
RSA._internal = require('./lib/node').create(deps);
RSA.thumbprint = function (jwk) {
jwk = jwk.privateKeyJwk || jwk.publicKeyJwk || jwk;
if (!jwk.e || !jwk.n) {
throw new Error("You must provide an RSA jwk with 'e' and 'n' (the public components)");
}
var input = RSA.utils._bytesToBuffer('{"e":"'+ jwk.e + '","kty":"RSA","n":"'+ jwk.n +'"}');
return RSA.util.b64enc(crypto.createHash('sha256').update(input).digest());
};
RSA.generateKeypair = function (length, exponent, options, cb) {
var keypair = {
privateKeyPem: undefined
, publicKeyPem: undefined
, privateKeyJwk: undefined
, publicKeyJwk: undefined
, _ursa: undefined
, _forge: undefined
};
options = options || NOBJ;
RSA._internal.generateKeypair(length, exponent, options, function (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
};
*/
}
}
if (options.pem) {
keypair.privateKeyPem = RSA._internal.exportPrivatePem(keys);
if (options.public) {
keypair.publicKeyPem = RSA._internal.exportPublicPem(keys);
}
}
if (options.thumprint) {
keypair.thumbprint = RSA.thumbprint(keypair.privateKeyJwk /*|| keypair.publicKeyJwk*/);
}
if (options.internal) {
//keypair._ursa = undefined;
//keypair._forge = undefined;
keypair._ursa = keys._ursa;
keypair._forge = keys._forge;
}
cb(null, keypair);
return;
});
};
return RSA;
}
module.exports.RSA = create(/*require('./lib/node')*/);
//module.exports.RSA.create = create;