167 lines
5.2 KiB
JavaScript
167 lines
5.2 KiB
JavaScript
/*!
|
|
* 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;
|
|
|
|
try {
|
|
RSA._URSA = require('ursa');
|
|
} catch(e) {
|
|
// ignore
|
|
}
|
|
|
|
RSA.utils = require('./lib/key-utils.js');
|
|
RSA.utils.toWebsafeBase64 = function (b64) {
|
|
return b64.replace(/[+]/g, "-").replace(/\//g, "_").replace(/=/g,"");
|
|
};
|
|
|
|
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 +'"}');
|
|
var base64Digest = crypto.createHash('sha256').update(input).digest('base64');
|
|
|
|
return RSA.utils.toWebsafeBase64(base64Digest);
|
|
};
|
|
|
|
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 (err, keys) {
|
|
if (false !== options.jwk || options.thumbprint) {
|
|
keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keys);
|
|
if (options.public) {
|
|
keypair.publicKeyJwk = RSA._internal.exportPublicJwk(keys);
|
|
}
|
|
}
|
|
|
|
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;
|
|
});
|
|
};
|
|
|
|
RSA.import = function (keypair/*, options*/) {
|
|
//options = options || NOBJ; // ignore
|
|
if (keypair.privateKeyJwk || keypair.privateKeyPem || keypair._ursa || (keypair._forge && keypair._forge)) {
|
|
keypair.privateKeyJwk = RSA._internal.exportPrivateJwk(keypair, { internal: true });
|
|
//keypair.privateKeyPem = RSA._internal.exportPrivatePem(keypair, { internal: true });
|
|
return keypair;
|
|
}
|
|
|
|
if (keypair.publicKeyJwk || keypair.publicKeyPem || keypair._ursaPublic || (keypair._forge && keypair._forgePublic)) {
|
|
keypair.publicKeyJwk = RSA._internal.exportPublicJwk(keypair, { internal: true });
|
|
//keypair.publicKeyPem = RSA._internal.exportPublicPem(keypair, { internal: true });
|
|
return keypair;
|
|
}
|
|
|
|
throw new Error('found neither private nor public keypair in any supported format');
|
|
};
|
|
|
|
RSA._ursaGenerateSig = function (keypair, sha256Buf) {
|
|
var sig = keypair._ursa.sign('sha256', sha256Buf);
|
|
var sig64 = RSA.utils.toWebsafeBase64(sig.toString('base64'));
|
|
|
|
return sig64;
|
|
};
|
|
RSA._forgeGenerateSig = function (keypair, sha256Buf) {
|
|
var forge = require('node-forge');
|
|
var md = forge.util.createBuffer(sha256Buf.toString('binary'), 'binary');
|
|
var sigF = keypair._forge.sign(md);
|
|
var sig64 = RSA.utils.toWebsafeBase64(
|
|
new Buffer(forge.util.bytesToHex(sigF), "hex").toString('base64')
|
|
);
|
|
|
|
return sig64;
|
|
};
|
|
RSA.generateSignatureJwk = function (keypair, payload, nonce) {
|
|
keypair = RSA._internal.import(keypair);
|
|
keypair = RSA._internal.importForge(keypair);
|
|
keypair.publicKeyJwk = RSA.exportPublicJwk(keypair);
|
|
|
|
// Compute JWS signature
|
|
var protectedHeader = "";
|
|
if (nonce) {
|
|
protectedHeader = JSON.stringify({nonce: nonce});
|
|
}
|
|
var protected64 = RSA.utils.toWebsafeBase64(new Buffer(protectedHeader).toString('base64'));
|
|
var payload64 = RSA.utils.toWebsafeBase64(payload.toString('base64'));
|
|
var raw = protected64 + "." + payload64;
|
|
var sha256Buf = crypto.createHash('sha256').update(raw).digest();
|
|
var sig64;
|
|
|
|
if (RSA._URSA) {
|
|
sig64 = RSA._ursaGenerateSig(keypair, sha256Buf);
|
|
} else {
|
|
sig64 = RSA._forgeGenerateSig(keypair, sha256Buf);
|
|
}
|
|
|
|
return {
|
|
header: {
|
|
alg: "RS256"
|
|
, jwk: keypair.publicKeyJwk
|
|
}
|
|
, protected: protected64
|
|
, payload: payload64
|
|
, signature: sig64
|
|
};
|
|
};
|
|
|
|
RSA.exportPrivateKey = RSA._internal.exportPrivatePem;
|
|
RSA.exportPublicKey = RSA._internal.exportPublicPem;
|
|
RSA.exportPrivatePem = RSA._internal.exportPrivatePem;
|
|
RSA.exportPublicPem = RSA._internal.exportPublicPem;
|
|
|
|
RSA.exportPrivateJwk = RSA._internal.exportPrivateJwk;
|
|
RSA.exportPublicJwk = RSA._internal.exportPublicJwk;
|
|
|
|
return RSA;
|
|
}
|
|
|
|
module.exports.RSA = create(/*require('./lib/node')*/);
|
|
//module.exports.RSA.create = create;
|