moving to rsa-compat
This commit is contained in:
parent
55707a417d
commit
7504268047
|
@ -94,7 +94,7 @@ LeCore.getCertificate(options, cb) // returns (err, pems={ key, cert,
|
||||||
, setChallenge: fn (hostname, key, val, cb)
|
, setChallenge: fn (hostname, key, val, cb)
|
||||||
, removeChallenge: fn (hostname, key, cb)
|
, removeChallenge: fn (hostname, key, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discovery URLs
|
// Discovery URLs
|
||||||
LeCore.getAcmeUrls(acmeDiscoveryUrl, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
|
LeCore.getAcmeUrls(acmeDiscoveryUrl, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
|
||||||
```
|
```
|
||||||
|
@ -138,7 +138,7 @@ For testing and development, you can also inject the dependencies you want to us
|
||||||
```javascript
|
```javascript
|
||||||
LeCore = LeCore.create({
|
LeCore = LeCore.create({
|
||||||
request: require('request')
|
request: require('request')
|
||||||
, leCrypto: rquire('./lib/letsencrypt-forge')
|
, RSA: rquire('rsa-compat').RSA
|
||||||
});
|
});
|
||||||
|
|
||||||
// now uses node `request` (could also use jQuery or Angular in the browser)
|
// now uses node `request` (could also use jQuery or Angular in the browser)
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*!
|
|
||||||
* letiny-core
|
|
||||||
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
|
|
||||||
* Apache-2.0 OR MIT (and hence also MPL 2.0)
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var crypto = require('crypto');
|
|
||||||
var forge = require('node-forge');
|
|
||||||
|
|
||||||
function binstrToB64(binstr) {
|
|
||||||
return new Buffer(binstr, 'binary').toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
function b64ToBinstr(b64) {
|
|
||||||
return new Buffer(b64, 'base64').toString('binary');
|
|
||||||
}
|
|
||||||
|
|
||||||
function privatePemToJwk(forgePrivkey) {
|
|
||||||
//var forgePrivkey = forge.pki.privateKeyFromPem(privkeyPem);
|
|
||||||
|
|
||||||
// required in node.js 4.2.2 (but not io.js 1.6.3)
|
|
||||||
Object.keys(forgePrivkey).forEach(function (k) {
|
|
||||||
var val = forgePrivkey[k];
|
|
||||||
if (val && val.toByteArray) {
|
|
||||||
forgePrivkey[k] = val.toByteArray();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
kty: "RSA"
|
|
||||||
, n: binstrToB64(forgePrivkey.n)
|
|
||||||
, e: binstrToB64(forgePrivkey.e)
|
|
||||||
, d: binstrToB64(forgePrivkey.d)
|
|
||||||
, p: binstrToB64(forgePrivkey.p)
|
|
||||||
, q: binstrToB64(forgePrivkey.q)
|
|
||||||
, dp: binstrToB64(forgePrivkey.dP)
|
|
||||||
, dq: binstrToB64(forgePrivkey.dQ)
|
|
||||||
, qi: binstrToB64(forgePrivkey.qInv)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function toForgePrivateKey(forgePrivkey) {
|
|
||||||
return forge.pki.rsa.setPrivateKey(
|
|
||||||
b64ToBinstr(forgePrivkey.n)
|
|
||||||
, b64ToBinstr(forgePrivkey.e)
|
|
||||||
, b64ToBinstr(forgePrivkey.d)
|
|
||||||
, b64ToBinstr(forgePrivkey.p)
|
|
||||||
, b64ToBinstr(forgePrivkey.q)
|
|
||||||
, b64ToBinstr(forgePrivkey.dp)
|
|
||||||
, b64ToBinstr(forgePrivkey.dq)
|
|
||||||
, b64ToBinstr(forgePrivkey.qi)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING: with forge this takes 20+ minutes on a Raspberry Pi!!!
|
|
||||||
// It takes SEVERAL seconds even on a nice macbook pro
|
|
||||||
function generateRsaKeypair(bitlen, exp, cb) {
|
|
||||||
var pki = forge.pki;
|
|
||||||
var keypair = pki.rsa.generateKeyPair({ bits: bitlen || 2048, e: exp || 65537 });
|
|
||||||
var pems = {
|
|
||||||
publicKeyPem: pki.publicKeyToPem(keypair.publicKey) // ascii PEM: ----BEGIN...
|
|
||||||
, privateKeyPem: pki.privateKeyToPem(keypair.privateKey) // ascii PEM: ----BEGIN...
|
|
||||||
};
|
|
||||||
|
|
||||||
// for account id
|
|
||||||
pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// for compat with python client account id
|
|
||||||
pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// json { n: ..., e: ..., iq: ..., etc }
|
|
||||||
pems.privateKeyJwk = privatePemToJwk(keypair.privateKey);
|
|
||||||
// deprecate
|
|
||||||
pems.privateKeyJson = pems.privateKeyJwk;
|
|
||||||
|
|
||||||
// TODO thumbprint
|
|
||||||
|
|
||||||
cb(null, pems);
|
|
||||||
}
|
|
||||||
|
|
||||||
function privateJwkToPems(pkj, cb) {
|
|
||||||
var pki = forge.pki;
|
|
||||||
|
|
||||||
Object.keys(pkj).forEach(function (key) {
|
|
||||||
pkj[key] = new Buffer(pkj[key], 'base64');
|
|
||||||
});
|
|
||||||
|
|
||||||
var priv;
|
|
||||||
var pubPem;
|
|
||||||
|
|
||||||
try {
|
|
||||||
priv = toForgePrivateKey(
|
|
||||||
pkj.n // modulus
|
|
||||||
, pkj.e // exponent
|
|
||||||
, pkj.p
|
|
||||||
, pkj.q
|
|
||||||
, pkj.dp
|
|
||||||
, pkj.dq
|
|
||||||
, pkj.qi
|
|
||||||
, pkj.d
|
|
||||||
);
|
|
||||||
} catch(e) {
|
|
||||||
cb(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pubPem = pki.publicKeyToPem(priv.publicKey);
|
|
||||||
cb(null, {
|
|
||||||
publicKeyPem: pubPem // ascii PEM: ----BEGIN...
|
|
||||||
, privateKeyPem: pki.privateKeyToPem(priv.privateKey) // ascii PEM: ----BEGIN...
|
|
||||||
// json { n: ..., e: ..., iq: ..., etc }
|
|
||||||
, privateKeyJwt: pkj
|
|
||||||
// deprecate
|
|
||||||
, privateKeyJson: pkj
|
|
||||||
// I would have chosen sha1 or sha2... but whatever
|
|
||||||
, publicKeyMd5: crypto.createHash('md5').update(pubPem).digest('hex')
|
|
||||||
, publicKeySha256: crypto.createHash('sha256').update(pubPem).digest('hex')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.generateRsaKeypair = generateRsaKeypair;
|
|
||||||
module.exports.privateJwkToPems = privateJwkToPems;
|
|
||||||
module.exports.privatePemToJwk = privatePemToJwk;
|
|
|
@ -1,368 +0,0 @@
|
||||||
// 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 crypto = require("crypto");
|
|
||||||
var tls = require("tls");
|
|
||||||
var forge = require("node-forge");
|
|
||||||
var util = require("./acme-util.js");
|
|
||||||
|
|
||||||
var TOKEN_SIZE = 16;
|
|
||||||
//var NONCE_SIZE = 16;
|
|
||||||
|
|
||||||
function bytesToBuffer(bytes) {
|
|
||||||
return new Buffer(forge.util.bytesToHex(bytes), "hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
function bufferToBytes(buf) {
|
|
||||||
return forge.util.hexToBytes(buf.toString("hex"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function bytesToBase64(bytes) {
|
|
||||||
return util.b64enc(bytesToBuffer(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
function base64ToBytes(base64) {
|
|
||||||
return bufferToBytes(util.b64dec(base64));
|
|
||||||
}
|
|
||||||
|
|
||||||
function bnToBase64(bn) {
|
|
||||||
var hex = bn.toString(16);
|
|
||||||
if (hex.length % 2 === 1) { hex = "0" + hex; }
|
|
||||||
return util.b64enc(new Buffer(hex, "hex"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function base64ToBn(base64) {
|
|
||||||
return new forge.jsbn.BigInteger(util.b64dec(base64).toString("hex"), 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
function importPrivateKey(privateKey) {
|
|
||||||
return forge.pki.rsa.setPrivateKey(
|
|
||||||
base64ToBn(privateKey.n),
|
|
||||||
base64ToBn(privateKey.e), base64ToBn(privateKey.d),
|
|
||||||
base64ToBn(privateKey.p), base64ToBn(privateKey.q),
|
|
||||||
base64ToBn(privateKey.dp),base64ToBn(privateKey.dq),
|
|
||||||
base64ToBn(privateKey.qi));
|
|
||||||
}
|
|
||||||
|
|
||||||
function importPublicKey(publicKey) {
|
|
||||||
return forge.pki.rsa.setPublicKey(
|
|
||||||
base64ToBn(publicKey.n),
|
|
||||||
base64ToBn(publicKey.e));
|
|
||||||
}
|
|
||||||
|
|
||||||
function exportPrivateKey(privateKey) {
|
|
||||||
return {
|
|
||||||
"kty": "RSA",
|
|
||||||
"n": bnToBase64(privateKey.n),
|
|
||||||
"e": bnToBase64(privateKey.e),
|
|
||||||
"d": bnToBase64(privateKey.d),
|
|
||||||
"p": bnToBase64(privateKey.p),
|
|
||||||
"q": bnToBase64(privateKey.q),
|
|
||||||
"dp": bnToBase64(privateKey.dP),
|
|
||||||
"dq": bnToBase64(privateKey.dQ),
|
|
||||||
"qi": bnToBase64(privateKey.qInv)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function exportPublicKey(publicKey) {
|
|
||||||
return {
|
|
||||||
"kty": "RSA",
|
|
||||||
"n": bnToBase64(publicKey.n),
|
|
||||||
"e": bnToBase64(publicKey.e)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// A note on formats:
|
|
||||||
// * Keys are always represented as JWKs
|
|
||||||
// * Signature objects are in ACME format
|
|
||||||
// * Certs and CSRs are base64-encoded
|
|
||||||
module.exports = {
|
|
||||||
///// RANDOM STRINGS
|
|
||||||
|
|
||||||
randomString: function(nBytes) {
|
|
||||||
return bytesToBase64(forge.random.getBytesSync(nBytes));
|
|
||||||
},
|
|
||||||
|
|
||||||
randomSerialNumber: function() {
|
|
||||||
return forge.util.bytesToHex(forge.random.getBytesSync(4));
|
|
||||||
},
|
|
||||||
|
|
||||||
newToken: function() {
|
|
||||||
return this.randomString(TOKEN_SIZE);
|
|
||||||
},
|
|
||||||
|
|
||||||
///// SHA-256
|
|
||||||
|
|
||||||
sha256: function(buf) {
|
|
||||||
return crypto.createHash('sha256').update(buf).digest('hex');
|
|
||||||
},
|
|
||||||
|
|
||||||
///// KEY PAIR MANAGEMENT
|
|
||||||
|
|
||||||
generateKeyPair: function(bits) {
|
|
||||||
var keyPair = forge.pki.rsa.generateKeyPair({bits: bits, e: 0x10001});
|
|
||||||
return {
|
|
||||||
privateKey: exportPrivateKey(keyPair.privateKey),
|
|
||||||
publicKey: exportPublicKey(keyPair.publicKey)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
|
|
||||||
certificateRequestToPem: function(csr) {
|
|
||||||
var derReq = base64ToBytes(csr);
|
|
||||||
var c = forge.pki.certificateFromAsn1(forge.asn1.fromDer(derReq));
|
|
||||||
return forge.pki.certificateRequestToPem(c);
|
|
||||||
},
|
|
||||||
|
|
||||||
thumbprint: function(publicKey) {
|
|
||||||
// Only handling RSA keys
|
|
||||||
var input = bytesToBuffer('{"e":"'+ publicKey.e + '","kty":"RSA","n":"'+ publicKey.n +'"}');
|
|
||||||
return util.b64enc(crypto.createHash('sha256').update(input).digest());
|
|
||||||
},
|
|
||||||
|
|
||||||
///// SIGNATURE GENERATION / VERIFICATION
|
|
||||||
|
|
||||||
generateSignature: function(keyPair, payload, nonce) {
|
|
||||||
var privateKey = importPrivateKey(keyPair.privateKey);
|
|
||||||
|
|
||||||
// Compute JWS signature
|
|
||||||
var protectedHeader = "";
|
|
||||||
if (nonce) {
|
|
||||||
protectedHeader = JSON.stringify({nonce: nonce});
|
|
||||||
}
|
|
||||||
var protected64 = util.b64enc(new Buffer(protectedHeader));
|
|
||||||
var payload64 = util.b64enc(payload);
|
|
||||||
var signatureInputBuf = new Buffer(protected64 + "." + payload64);
|
|
||||||
var signatureInput = bufferToBytes(signatureInputBuf);
|
|
||||||
var md = forge.md.sha256.create();
|
|
||||||
md.update(signatureInput);
|
|
||||||
var sig = privateKey.sign(md);
|
|
||||||
|
|
||||||
return {
|
|
||||||
header: {
|
|
||||||
alg: "RS256",
|
|
||||||
jwk: keyPair.publicKey,
|
|
||||||
},
|
|
||||||
protected: protected64,
|
|
||||||
payload: payload64,
|
|
||||||
signature: util.b64enc(bytesToBuffer(sig)),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
verifySignature: function(jws) {
|
|
||||||
var key;
|
|
||||||
|
|
||||||
if (jws.protected) {
|
|
||||||
if (!jws.header) {
|
|
||||||
jws.header = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log(jws.protected);
|
|
||||||
var protectedJSON = util.b64dec(jws.protected).toString();
|
|
||||||
console.log(protectedJSON);
|
|
||||||
var protectedObj = JSON.parse(protectedJSON);
|
|
||||||
for (key in protectedObj) {
|
|
||||||
jws.header[key] = protectedObj[key];
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log("error unmarshaling json: "+e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumes validSignature(sig)
|
|
||||||
if (!jws.header.jwk || (jws.header.jwk.kty !== "RSA")) {
|
|
||||||
// Unsupported key type
|
|
||||||
console.log("Unsupported key type");
|
|
||||||
return false;
|
|
||||||
} else if (!jws.header.alg || !jws.header.alg.match(/^RS/)) {
|
|
||||||
// Unsupported algorithm
|
|
||||||
console.log("Unsupported alg: "+jws.header.alg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute signature input
|
|
||||||
var protected64 = (jws.protected)? jws.protected : "";
|
|
||||||
var payload64 = (jws.payload)? jws.payload : "";
|
|
||||||
var signatureInputBuf = new Buffer(protected64 + "." + payload64);
|
|
||||||
var signatureInput = bufferToBytes(signatureInputBuf);
|
|
||||||
|
|
||||||
// Compute message digest
|
|
||||||
var md;
|
|
||||||
switch (jws.header.alg) {
|
|
||||||
case "RS1": md = forge.md.sha1.create(); break;
|
|
||||||
case "RS256": md = forge.md.sha256.create(); break;
|
|
||||||
case "RS384": md = forge.md.sha384.create(); break;
|
|
||||||
case "RS512": md = forge.md.sha512.create(); break;
|
|
||||||
default: return false; // Unsupported algorithm
|
|
||||||
}
|
|
||||||
md.update(signatureInput);
|
|
||||||
|
|
||||||
// Import the key and signature
|
|
||||||
var publicKey = importPublicKey(jws.header.jwk);
|
|
||||||
var sig = bufferToBytes(util.b64dec(jws.signature));
|
|
||||||
|
|
||||||
return publicKey.verify(md.digest().bytes(), sig);
|
|
||||||
},
|
|
||||||
|
|
||||||
///// CSR GENERATION / VERIFICATION
|
|
||||||
|
|
||||||
generateCSR: function(keyPair, names) {
|
|
||||||
var privateKey = importPrivateKey(keyPair.privateKey);
|
|
||||||
var publicKey = importPublicKey(keyPair.publicKey);
|
|
||||||
|
|
||||||
// Create and sign the CSR
|
|
||||||
var csr = forge.pki.createCertificationRequest();
|
|
||||||
csr.publicKey = publicKey;
|
|
||||||
csr.setSubject([{ name: 'commonName', value: names[0] }]);
|
|
||||||
|
|
||||||
var sans = [];
|
|
||||||
var i;
|
|
||||||
|
|
||||||
for (i in names) {
|
|
||||||
sans.push({ type: 2, value: names[i] });
|
|
||||||
}
|
|
||||||
csr.setAttributes([{
|
|
||||||
name: 'extensionRequest',
|
|
||||||
extensions: [{name: 'subjectAltName', altNames: sans}]
|
|
||||||
}]);
|
|
||||||
|
|
||||||
csr.sign(privateKey, forge.md.sha256.create());
|
|
||||||
|
|
||||||
// Convert CSR -> DER -> Base64
|
|
||||||
var der = forge.asn1.toDer(forge.pki.certificationRequestToAsn1(csr));
|
|
||||||
return util.b64enc(bytesToBuffer(der));
|
|
||||||
},
|
|
||||||
|
|
||||||
verifiedCommonName: function(csr_b64) {
|
|
||||||
var der = bufferToBytes(util.b64dec(csr_b64));
|
|
||||||
var csr = forge.pki.certificationRequestFromAsn1(forge.asn1.fromDer(der));
|
|
||||||
|
|
||||||
if (!csr.verify()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i=0; i<csr.subject.attributes.length; ++i) {
|
|
||||||
if (csr.subject.attributes[i].name === "commonName") {
|
|
||||||
return csr.subject.attributes[i].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
///// CERTIFICATE GENERATION
|
|
||||||
|
|
||||||
// 'ca' parameter includes information about the CA
|
|
||||||
// {
|
|
||||||
// distinguishedName: /* forge-formatted DN */
|
|
||||||
// keyPair: {
|
|
||||||
// publicKey: /* JWK */
|
|
||||||
// privateKey: /* JWK */
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
generateCertificate: function(ca, serialNumber, csr_b64) {
|
|
||||||
var der = bufferToBytes(util.b64dec(csr_b64));
|
|
||||||
var csr = forge.pki.certificationRequestFromAsn1(forge.asn1.fromDer(der));
|
|
||||||
|
|
||||||
// Extract the public key and common name
|
|
||||||
var publicKey = csr.publicKey;
|
|
||||||
var commonName = null;
|
|
||||||
for (var i=0; i<csr.subject.attributes.length; ++i) {
|
|
||||||
if (csr.subject.attributes[i].name === "commonName") {
|
|
||||||
commonName = csr.subject.attributes[i].value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!commonName) { return false; }
|
|
||||||
|
|
||||||
// Create the certificate
|
|
||||||
var cert = forge.pki.createCertificate();
|
|
||||||
cert.publicKey = publicKey;
|
|
||||||
cert.serialNumber = serialNumber;
|
|
||||||
|
|
||||||
// 1-year validity
|
|
||||||
cert.validity.notBefore = new Date();
|
|
||||||
cert.validity.notAfter = new Date();
|
|
||||||
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
||||||
|
|
||||||
cert.setSubject([{ name: "commonName", value: commonName }]);
|
|
||||||
cert.setIssuer(ca.distinguishedName);
|
|
||||||
cert.setExtensions([
|
|
||||||
{ name: "basicConstraints", cA: false },
|
|
||||||
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
||||||
{ name: "extKeyUsage", serverAuth: true },
|
|
||||||
{ name: "subjectAltName", altNames: [{ type: 2, value: commonName }] }
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Import signing key and sign
|
|
||||||
var privateKey = importPrivateKey(ca.keyPair.privateKey);
|
|
||||||
cert.sign(privateKey);
|
|
||||||
|
|
||||||
// Return base64-encoded DER
|
|
||||||
der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert));
|
|
||||||
return bytesToBuffer(der);
|
|
||||||
},
|
|
||||||
|
|
||||||
generateDvsniCertificate: function(keyPair, nonceName, zName) {
|
|
||||||
var cert = forge.pki.createCertificate();
|
|
||||||
cert.publicKey = importPublicKey(keyPair.publicKey);
|
|
||||||
cert.serialNumber = '01';
|
|
||||||
cert.validity.notBefore = new Date();
|
|
||||||
cert.validity.notAfter = new Date();
|
|
||||||
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
||||||
cert.setSubject([{ name: "commonName", value: nonceName }]);
|
|
||||||
cert.setIssuer([{ name: "commonName", value: nonceName }]);
|
|
||||||
cert.setExtensions([
|
|
||||||
{ name: "basicConstraints", cA: false },
|
|
||||||
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
||||||
{ name: "extKeyUsage", serverAuth: true },
|
|
||||||
{ name: "subjectAltName", altNames: [
|
|
||||||
{ type: 2, value: nonceName },
|
|
||||||
{ type: 2, value: zName }
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
cert.sign(importPrivateKey(keyPair.privateKey));
|
|
||||||
|
|
||||||
// Return base64-encoded DER, as above
|
|
||||||
var der = forge.asn1.toDer(forge.pki.certificateToAsn1(cert));
|
|
||||||
return util.b64enc(bytesToBuffer(der));
|
|
||||||
},
|
|
||||||
|
|
||||||
///// TLS CONTEXT GENERATION
|
|
||||||
|
|
||||||
createContext: function(keyPair, cert) {
|
|
||||||
var privateKey = importPrivateKey(keyPair.privateKey);
|
|
||||||
var derCert = bufferToBytes(util.b64dec(cert));
|
|
||||||
var realCert = forge.pki.certificateFromAsn1(forge.asn1.fromDer(derCert));
|
|
||||||
return tls.createSecureContext({
|
|
||||||
key: forge.pki.privateKeyToPem(privateKey),
|
|
||||||
cert: forge.pki.certificateToPem(realCert)
|
|
||||||
}).context;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
/*!
|
|
||||||
* letiny-core
|
|
||||||
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
|
|
||||||
* Apache-2.0 OR MIT (and hence also MPL 2.0)
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {};
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*!
|
|
||||||
* letiny-core
|
|
||||||
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
|
|
||||||
* Apache-2.0 OR MIT (and hence also MPL 2.0)
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var crypto = require('crypto');
|
|
||||||
var ursa = require('ursa');
|
|
||||||
var forge = require('node-forge');
|
|
||||||
|
|
||||||
function binstrToB64(binstr) {
|
|
||||||
return new Buffer(binstr, 'binary').toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
function b64ToBinstr(b64) {
|
|
||||||
return new Buffer(b64, 'base64').toString('binary');
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function privatePemToJwk(privkeyPem) {
|
|
||||||
var forgePrivkey = forge.pki.privateKeyFromPem(privkeyPem);
|
|
||||||
|
|
||||||
// required in node.js 4.2.2 (but not io.js 1.6.3)
|
|
||||||
Object.keys(forgePrivkey).forEach(function (k) {
|
|
||||||
var val = forgePrivkey[k];
|
|
||||||
if (val && val.toByteArray) {
|
|
||||||
forgePrivkey[k] = val.toByteArray();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
kty: "RSA"
|
|
||||||
, n: binstrToB64(forgePrivkey.n)
|
|
||||||
, e: binstrToB64(forgePrivkey.e)
|
|
||||||
, d: binstrToB64(forgePrivkey.d)
|
|
||||||
, p: binstrToB64(forgePrivkey.p)
|
|
||||||
, q: binstrToB64(forgePrivkey.q)
|
|
||||||
, dp: binstrToB64(forgePrivkey.dP)
|
|
||||||
, dq: binstrToB64(forgePrivkey.dQ)
|
|
||||||
, qi: binstrToB64(forgePrivkey.qInv)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRsaKeypair(bitlen, exp, cb) {
|
|
||||||
var keypair = ursa.generatePrivateKey(bitlen || 2048, exp || 6553);
|
|
||||||
var pems = {
|
|
||||||
publicKeyPem: keypair.toPublicPem().toString('ascii') // ascii PEM: ----BEGIN...
|
|
||||||
, privateKeyPem: keypair.toPrivatePem().toString('ascii') // ascii PEM: ----BEGIN...
|
|
||||||
};
|
|
||||||
|
|
||||||
// for account id
|
|
||||||
pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// for compat with python client account id
|
|
||||||
pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// json { n: ..., e: ..., iq: ..., etc }
|
|
||||||
pems.privateKeyJwk = privatePemToJwk(pems.privateKeyPem);
|
|
||||||
pems.privateKeyJson = pems.privateKeyJwk;
|
|
||||||
|
|
||||||
// TODO thumbprint
|
|
||||||
|
|
||||||
cb(null, pems);
|
|
||||||
}
|
|
||||||
|
|
||||||
function privateJwkToPems(pkj, cb) {
|
|
||||||
Object.keys(pkj).forEach(function (key) {
|
|
||||||
pkj[key] = new Buffer(pkj[key], 'base64');
|
|
||||||
});
|
|
||||||
|
|
||||||
var priv;
|
|
||||||
var pems;
|
|
||||||
|
|
||||||
try {
|
|
||||||
priv = ursa.createPrivateKeyFromComponents(
|
|
||||||
pkj.n // modulus
|
|
||||||
, pkj.e // exponent
|
|
||||||
, pkj.p
|
|
||||||
, pkj.q
|
|
||||||
, pkj.dp
|
|
||||||
, pkj.dq
|
|
||||||
, pkj.qi
|
|
||||||
, pkj.d
|
|
||||||
);
|
|
||||||
} catch(e) {
|
|
||||||
cb(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pems = {
|
|
||||||
privateKeyPem: priv.toPrivatePem().toString('ascii')
|
|
||||||
, publicKeyPem: priv.toPublicPem().toString('ascii')
|
|
||||||
};
|
|
||||||
|
|
||||||
// for account id
|
|
||||||
pems.publicKeySha256 = crypto.createHash('sha256').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// for compat with python client account id
|
|
||||||
pems.publicKeyMd5 = crypto.createHash('md5').update(pems.publicKeyPem).digest('hex');
|
|
||||||
// json { n: ..., e: ..., iq: ..., etc }
|
|
||||||
pems.privateKeyJwk = privatePemToJwk(pems.privateKeyPem);
|
|
||||||
pems.privateKeyJson = pems.privateKeyJwk;
|
|
||||||
|
|
||||||
cb(null, pems);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.generateRsaKeypair = generateRsaKeypair;
|
|
||||||
module.exports.privateJwkToPems = privateJwkToPems;
|
|
||||||
module.exports.privatePemToJwk = privatePemToJwk;
|
|
Loading…
Reference in New Issue