normalize all *Pem *Key* etc to keypair and *Keypair

This commit is contained in:
AJ ONeal 2016-08-01 20:00:04 -04:00
parent 2c7b7e1bcf
commit ee48f4a477
3 changed files with 33 additions and 44 deletions

View File

@ -77,7 +77,9 @@ LeCore.registerNewAccount(options, cb) // returns "regr" registration dat
{ newRegUrl: '<url>' // no defaults, specify acmeUrls.newAuthz { newRegUrl: '<url>' // no defaults, specify acmeUrls.newAuthz
, email: '<email>' // valid email (server checks MX records) , email: '<email>' // valid email (server checks MX records)
, accountPrivateKeyPem: '<ASCII PEM>' // callback to allow user interaction for tosUrl , accountKeypair: { // privateKeyPem or privateKeyJwt
privateKeyPem: '<ASCII PEM>'
}
, agreeToTerms: fn (tosUrl, cb) {} // must specify agree=tosUrl to continue (or falsey to end) , agreeToTerms: fn (tosUrl, cb) {} // must specify agree=tosUrl to continue (or falsey to end)
} }
@ -87,8 +89,12 @@ LeCore.getCertificate(options, cb) // returns (err, pems={ key, cert,
{ newAuthzUrl: '<url>' // specify acmeUrls.newAuthz { newAuthzUrl: '<url>' // specify acmeUrls.newAuthz
, newCertUrl: '<url>' // specify acmeUrls.newCert , newCertUrl: '<url>' // specify acmeUrls.newCert
, domainPrivateKeyPem: '<ASCII PEM>' , domainKeypair: {
, accountPrivateKeyPem: '<ASCII PEM>' privateKeyPem: '<ASCII PEM>'
}
, accountKeypair: {
privateKeyPem: '<ASCII PEM>'
}
, domains: ['example.com'] , domains: ['example.com']
, setChallenge: fn (hostname, key, val, cb) , setChallenge: fn (hostname, key, val, cb)
@ -118,19 +124,6 @@ LeCore.Acme // Signs requests with JWK
acme.post(url, body, cb) // POST with signature acme.post(url, body, cb) // POST with signature
acme.parseLinks(link) // (internal) parses 'link' header acme.parseLinks(link) // (internal) parses 'link' header
acme.getNonce(url, cb) // (internal) HEAD request to get 'replay-nonce' strings acme.getNonce(url, cb) // (internal) HEAD request to get 'replay-nonce' strings
// Note: some of these are not async,
// but they will be soon. Don't rely
// on their API yet.
// Crypto Helpers
LeCore.leCrypto
generateRsaKeypair(bitLen, exponent, cb); // returns { privateKeyPem, privateKeyJwk, publicKeyPem, publicKeyMd5 }
thumbprint(lePubKey) // generates public key thumbprint
generateSignature(lePrivKey, bodyBuf, nonce) // generates a signature
privateJwkToPems(jwk) // { n: '...', e: '...', iq: '...', ... } to PEMs
privatePemToJwk // PEM to JWK (see line above)
importPemPrivateKey(privateKeyPem) // (internal) returns abstract private key
``` ```
For testing and development, you can also inject the dependencies you want to use: For testing and development, you can also inject the dependencies you want to use:
@ -161,16 +154,18 @@ This is how you **register an ACME account** and **get an HTTPS certificate**
'use strict'; 'use strict';
var LeCore = require('letiny-core'); var LeCore = require('letiny-core');
var RSA = require('rsa-compat').RSA;
var email = 'user@example.com'; // CHANGE TO YOUR EMAIL var email = 'user@example.com'; // CHANGE TO YOUR EMAIL
var domains = 'example.com'; // CHANGE TO YOUR DOMAIN var domains = 'example.com'; // CHANGE TO YOUR DOMAIN
var acmeDiscoveryUrl = LeCore.stagingServerUrl; // CHANGE to production, when ready var acmeDiscoveryUrl = LeCore.stagingServerUrl; // CHANGE to production, when ready
var accountPrivateKeyPem = null; var accountKeypair = null; // { privateKeyPem: null, privateKeyJwk: null };
var domainPrivateKeyPem = null; var domainKeypair = null; // same as above
var acmeUrls = null; var acmeUrls = null;
LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) { RSA.generateKeypair(2048, 65537, function (err, keypair) {
accountKeypair = keypair;
// ... // ...
LeCore.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) { LeCore.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) {
// ... // ...
@ -182,7 +177,7 @@ function runDemo() {
LeCore.registerNewAccount( LeCore.registerNewAccount(
{ newRegUrl: acmeUrls.newReg { newRegUrl: acmeUrls.newReg
, email: email , email: email
, accountPrivateKeyPem: accountPrivateKeyPem , accountKeypair: accountKeypair
, agreeToTerms: function (tosUrl, done) { , agreeToTerms: function (tosUrl, done) {
// agree to the exact version of these terms // agree to the exact version of these terms
@ -195,8 +190,8 @@ function runDemo() {
{ newAuthzUrl: acmeUrls.newAuthz { newAuthzUrl: acmeUrls.newAuthz
, newCertUrl: acmeUrls.newCert , newCertUrl: acmeUrls.newCert
, domainPrivateKeyPem: domainPrivateKeyPem , domainKeypair: domainKeypair
, accountPrivateKeyPem: accountPrivateKeyPem , accountKeypair: accountKeypair
, domains: domains , domains: domains
, setChallenge: challengeStore.set , setChallenge: challengeStore.set
@ -323,4 +318,4 @@ MPL 2.0
All of the code is available under the MPL-2.0. All of the code is available under the MPL-2.0.
Some of the files are original work not modified from `letiny` Some of the files are original work not modified from `letiny`
and are made available under MIT as well (check file headers). and are made available under MIT and Apache-2.0 as well (check file headers).

View File

@ -19,9 +19,6 @@ function _toStandardBase64(str) {
module.exports.create = function (deps) { module.exports.create = function (deps) {
var request=deps.request; var request=deps.request;
//var importPemPrivateKey = deps.leCrypto.importPemPrivateKey;
//var thumbprinter = deps.leCrypto.thumbprint;
//var generateCsr = deps.leCrypto.generateCsr || deps.leCrypto.generateCSR;
var Acme = deps.Acme; var Acme = deps.Acme;
var RSA = deps.RSA; var RSA = deps.RSA;
@ -164,7 +161,7 @@ module.exports.create = function (deps) {
} }
challenge=httpChallenges[0]; challenge=httpChallenges[0];
thumbprint=RSA.thumbprint(state.accountKeyPair); thumbprint=RSA.thumbprint(state.accountKeypair);
keyAuthorization=challenge.token+'.'+thumbprint; keyAuthorization=challenge.token+'.'+thumbprint;
state.responseUrl=challenge.uri; state.responseUrl=challenge.uri;
@ -226,7 +223,7 @@ module.exports.create = function (deps) {
} }
function getCertificate() { function getCertificate() {
var csr=RSA.generateCsrWeb64(state.certPrivateKey, state.validatedDomains); var csr=RSA.generateCsrWeb64(RSA.exportPrivateKeyPem(state.certKeypair), state.validatedDomains);
log('Requesting certificate...'); log('Requesting certificate...');
state.acme.post(state.newCertUrl, { state.acme.post(state.newCertUrl, {
resource:'new-cert', resource:'new-cert',
@ -325,7 +322,7 @@ module.exports.create = function (deps) {
var cert; var cert;
try { try {
cert=certBufferToPem(state.certificate); cert = certBufferToPem(state.certificate);
} catch(e) { } catch(e) {
console.error(e.stack); console.error(e.stack);
//cb(new Error("Could not write output files. Please check permissions!")); //cb(new Error("Could not write output files. Please check permissions!"));
@ -335,7 +332,7 @@ module.exports.create = function (deps) {
cb(null, { cb(null, {
cert: cert cert: cert
, key: state.certPrivateKeyPem , key: RSA.exportPrivateKeyPem(state.certKeypair)
, ca: state.caCert , ca: state.caCert
}); });
} }
@ -360,11 +357,11 @@ module.exports.create = function (deps) {
if (!options.newCertUrl) { if (!options.newCertUrl) {
return handleErr(new Error("options.newCertUrl must be the new certificate url")); return handleErr(new Error("options.newCertUrl must be the new certificate url"));
} }
if (!options.accountPrivateKeyPem) { if (!options.accountKeypair) {
return handleErr(new Error("options.accountPrivateKeyPem must be an ascii private key pem")); return handleErr(new Error("options.accountKeypair must be an object with `privateKeyPem` and/or `privateKeyJwk`"));
} }
if (!options.domainPrivateKeyPem) { if (!options.domainKeypair) {
return handleErr(new Error("options.domainPrivateKeyPem must be an ascii private key pem")); return handleErr(new Error("options.domainKeypair must be an object with `privateKeyPem` and/or `privateKeyJwk`"));
} }
if (!options.setChallenge) { if (!options.setChallenge) {
return handleErr(new Error("options.setChallenge must be function(hostname, challengeKey, tokenValue, done) {}")); return handleErr(new Error("options.setChallenge must be function(hostname, challengeKey, tokenValue, done) {}"));
@ -378,11 +375,9 @@ module.exports.create = function (deps) {
state.domains = options.domains.slice(0); // copy array state.domains = options.domains.slice(0); // copy array
try { try {
state.accountKeyPem=options.accountPrivateKeyPem; state.acme = new Acme(state.accountKeypair);
state.accountKeyPair=RSA.import({ privateKeyPem: state.accountKeyPem }); state.accountKeypair = options.accountKeypair;
state.acme=new Acme(state.accountKeyPair); state.certKeypair = options.domainKeypair;
state.certPrivateKeyPem=options.domainPrivateKeyPem;
state.certPrivateKey=RSA.import({ privateKeyPem: state.certPrivateKeyPem });
} catch(err) { } catch(err) {
return handleErr(err, 'Failed to parse privateKey'); return handleErr(err, 'Failed to parse privateKey');
} }

View File

@ -103,8 +103,8 @@ module.exports.create = function (deps) {
var state = {}; var state = {};
if (!options.accountPrivateKeyPem) { if (!options.accountKeypair) {
return handleErr(new Error("options.accountPrivateKeyPem must be an ascii private key pem")); return handleErr(new Error("options.accountKeypair must be an object with `privateKeyPem` and/or `privateKeyJwk`"));
} }
if (!options.agreeToTerms) { if (!options.agreeToTerms) {
cb(new Error("options.agreeToTerms must be function (tosUrl, fn => (err, true))")); cb(new Error("options.agreeToTerms must be function (tosUrl, fn => (err, true))"));
@ -119,9 +119,8 @@ module.exports.create = function (deps) {
return; return;
} }
state.accountKeyPem=options.accountPrivateKeyPem; state.accountKeypair = options.accountKeypair;
state.accountKeyPair=RSA.import({ privateKeyPem: state.accountKeyPem }); state.acme=new Acme(state.accountKeypair);
state.acme=new Acme(state.accountKeyPair);
register(); register();
} }