From ee48f4a4774181f6590954f35b5b49c8055dfb9f Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 1 Aug 2016 20:00:04 -0400 Subject: [PATCH] normalize all *Pem *Key* etc to keypair and *Keypair --- README.md | 41 ++++++++++++++++--------------------- lib/get-certificate.js | 27 ++++++++++-------------- lib/register-new-account.js | 9 ++++---- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index f6f645e..984238f 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ LeCore.registerNewAccount(options, cb) // returns "regr" registration dat { newRegUrl: '' // no defaults, specify acmeUrls.newAuthz , email: '' // valid email (server checks MX records) - , accountPrivateKeyPem: '' // callback to allow user interaction for tosUrl + , accountKeypair: { // privateKeyPem or privateKeyJwt + privateKeyPem: '' + } , 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: '' // specify acmeUrls.newAuthz , newCertUrl: '' // specify acmeUrls.newCert - , domainPrivateKeyPem: '' - , accountPrivateKeyPem: '' + , domainKeypair: { + privateKeyPem: '' + } + , accountKeypair: { + privateKeyPem: '' + } , 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). diff --git a/lib/get-certificate.js b/lib/get-certificate.js index 43e280f..4fdd22a 100644 --- a/lib/get-certificate.js +++ b/lib/get-certificate.js @@ -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', @@ -325,7 +322,7 @@ module.exports.create = function (deps) { var cert; try { - cert=certBufferToPem(state.certificate); + cert = certBufferToPem(state.certificate); } catch(e) { console.error(e.stack); //cb(new Error("Could not write output files. Please check permissions!")); @@ -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'); } diff --git a/lib/register-new-account.js b/lib/register-new-account.js index 5ab42ab..9c6a5a7 100644 --- a/lib/register-new-account.js +++ b/lib/register-new-account.js @@ -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(); }