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
, 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)
}
@ -87,8 +89,12 @@ LeCore.getCertificate(options, cb) // returns (err, pems={ key, cert,
{ newAuthzUrl: '<url>' // specify acmeUrls.newAuthz
, newCertUrl: '<url>' // specify acmeUrls.newCert
, domainPrivateKeyPem: '<ASCII PEM>'
, accountPrivateKeyPem: '<ASCII PEM>'
, domainKeypair: {
privateKeyPem: '<ASCII PEM>'
}
, accountKeypair: {
privateKeyPem: '<ASCII PEM>'
}
, domains: ['example.com']
, 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.parseLinks(link) // (internal) parses 'link' header
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:
@ -161,16 +154,18 @@ This is how you **register an ACME account** and **get an HTTPS certificate**
'use strict';
var LeCore = require('letiny-core');
var RSA = require('rsa-compat').RSA;
var email = 'user@example.com'; // CHANGE TO YOUR EMAIL
var domains = 'example.com'; // CHANGE TO YOUR DOMAIN
var acmeDiscoveryUrl = LeCore.stagingServerUrl; // CHANGE to production, when ready
var accountPrivateKeyPem = null;
var domainPrivateKeyPem = null;
var accountKeypair = null; // { privateKeyPem: null, privateKeyJwk: null };
var domainKeypair = null; // same as above
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) {
// ...
@ -182,7 +177,7 @@ function runDemo() {
LeCore.registerNewAccount(
{ newRegUrl: acmeUrls.newReg
, email: email
, accountPrivateKeyPem: accountPrivateKeyPem
, accountKeypair: accountKeypair
, agreeToTerms: function (tosUrl, done) {
// agree to the exact version of these terms
@ -195,8 +190,8 @@ function runDemo() {
{ newAuthzUrl: acmeUrls.newAuthz
, newCertUrl: acmeUrls.newCert
, domainPrivateKeyPem: domainPrivateKeyPem
, accountPrivateKeyPem: accountPrivateKeyPem
, domainKeypair: domainKeypair
, accountKeypair: accountKeypair
, domains: domains
, setChallenge: challengeStore.set
@ -323,4 +318,4 @@ 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`
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) {
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 RSA = deps.RSA;
@ -164,7 +161,7 @@ module.exports.create = function (deps) {
}
challenge=httpChallenges[0];
thumbprint=RSA.thumbprint(state.accountKeyPair);
thumbprint=RSA.thumbprint(state.accountKeypair);
keyAuthorization=challenge.token+'.'+thumbprint;
state.responseUrl=challenge.uri;
@ -226,7 +223,7 @@ module.exports.create = function (deps) {
}
function getCertificate() {
var csr=RSA.generateCsrWeb64(state.certPrivateKey, state.validatedDomains);
var csr=RSA.generateCsrWeb64(RSA.exportPrivateKeyPem(state.certKeypair), state.validatedDomains);
log('Requesting certificate...');
state.acme.post(state.newCertUrl, {
resource:'new-cert',
@ -335,7 +332,7 @@ module.exports.create = function (deps) {
cb(null, {
cert: cert
, key: state.certPrivateKeyPem
, key: RSA.exportPrivateKeyPem(state.certKeypair)
, ca: state.caCert
});
}
@ -360,11 +357,11 @@ module.exports.create = function (deps) {
if (!options.newCertUrl) {
return handleErr(new Error("options.newCertUrl must be the new certificate url"));
}
if (!options.accountPrivateKeyPem) {
return handleErr(new Error("options.accountPrivateKeyPem must be an ascii private key pem"));
if (!options.accountKeypair) {
return handleErr(new Error("options.accountKeypair must be an object with `privateKeyPem` and/or `privateKeyJwk`"));
}
if (!options.domainPrivateKeyPem) {
return handleErr(new Error("options.domainPrivateKeyPem must be an ascii private key pem"));
if (!options.domainKeypair) {
return handleErr(new Error("options.domainKeypair must be an object with `privateKeyPem` and/or `privateKeyJwk`"));
}
if (!options.setChallenge) {
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
try {
state.accountKeyPem=options.accountPrivateKeyPem;
state.accountKeyPair=RSA.import({ privateKeyPem: state.accountKeyPem });
state.acme=new Acme(state.accountKeyPair);
state.certPrivateKeyPem=options.domainPrivateKeyPem;
state.certPrivateKey=RSA.import({ privateKeyPem: state.certPrivateKeyPem });
state.acme = new Acme(state.accountKeypair);
state.accountKeypair = options.accountKeypair;
state.certKeypair = options.domainKeypair;
} catch(err) {
return handleErr(err, 'Failed to parse privateKey');
}

View File

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