From 55707a417d7fbef6066f0a474272f4c4e98e6dba Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 1 Aug 2016 05:53:50 -0400 Subject: [PATCH] moving to rsa-compat --- lib/acme-client.js | 4 +- lib/get-certificate.js | 168 ++++++++++++++++++------------------ lib/node.js | 38 +------- lib/register-new-account.js | 49 +++++------ node.js | 2 - package.json | 5 +- 6 files changed, 118 insertions(+), 148 deletions(-) diff --git a/lib/acme-client.js b/lib/acme-client.js index 17e81a4..b5755d5 100644 --- a/lib/acme-client.js +++ b/lib/acme-client.js @@ -11,7 +11,7 @@ module.exports.create = function (deps) { var NOOP=function () {}; var log=NOOP; var request=require('request'); - var generateSignature=deps.leCrypto.generateSignature; + var generateSignature=deps.RSA.signJws; function Acme(privateKey) { this.privateKey=privateKey; @@ -54,7 +54,7 @@ module.exports.create = function (deps) { log('Using nonce: '+this.nonces[0]); payload=JSON.stringify(body, null, 2); jws=generateSignature( - this.privateKey, new Buffer(payload), this.nonces.shift() + this.privateKey, new Buffer(payload), this.nonces.shift() ); signed=JSON.stringify(jws, null, 2); diff --git a/lib/get-certificate.js b/lib/get-certificate.js index abe62d8..f03c679 100644 --- a/lib/get-certificate.js +++ b/lib/get-certificate.js @@ -9,53 +9,13 @@ module.exports.create = function (deps) { var request=deps.request; var toStandardB64 = deps.leUtils.toStandardB64; - var importPemPrivateKey = deps.leCrypto.importPemPrivateKey; - var thumbprinter = deps.leCrypto.thumbprint; - var generateCsr = deps.leCrypto.generateCsr || deps.leCrypto.generateCSR; + //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; function getCert(options, cb) { - var NOOP = function () {}; - var log = options.debug ? console.log : NOOP; - var state={ - validatedDomains:[] - , validAuthorizationUrls:[] - , newAuthzUrl: options.newAuthzUrl - , newCertUrl: options.newCertUrl - }; - - if (!options.newAuthzUrl) { - return handleErr(new Error("options.newAuthzUrl must be the authorization url")); - } - 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.domainPrivateKeyPem) { - return handleErr(new Error("options.domainPrivateKeyPem must be an ascii private key pem")); - } - if (!options.setChallenge) { - return handleErr(new Error("options.setChallenge must be function(hostname, challengeKey, tokenValue, done) {}")); - } - if (!options.removeChallenge) { - return handleErr(new Error("options.removeChallenge must be function(hostname, challengeKey, done) {}")); - } - if (!(options.domains && options.domains.length)) { - return handleErr(new Error("options.domains must be an array of domains such as ['example.com', 'www.example.com']")); - } - - state.domains = options.domains.slice(0); // copy array - try { - state.accountKeyPem=options.accountPrivateKeyPem; - state.accountKeyPair=importPemPrivateKey(state.accountKeyPem); - state.acme=new Acme(state.accountKeyPair); - state.certPrivateKeyPem=options.domainPrivateKeyPem; - state.certPrivateKey=importPemPrivateKey(state.certPrivateKeyPem); - } catch(err) { - return handleErr(err, 'Failed to parse privateKey'); - } function bodyToError(res, body) { var err; @@ -80,7 +40,7 @@ module.exports.create = function (deps) { if (Math.floor(res.statusCode / 100) !== 2) { err = new Error("[Error] letiny-core: not 200 ok"); err.code = "E_STATUS_CODE"; - err.type = body.type + err.type = body.type; err.description = body; err.detail = body.detail; console.error("TODO: modules which depend on this module should expose this error properly but since some of them don't, I expose it here directly:"); @@ -101,8 +61,6 @@ module.exports.create = function (deps) { return body; } - nextDomain(); - function nextDomain() { if (state.domains.length > 0) { getChallenges(state.domains.shift()); @@ -130,44 +88,11 @@ module.exports.create = function (deps) { } } - getReadyToValidate(err, res, body) + getReadyToValidate(err, res, body); }); } function getReadyToValidate(err, res, body) { - var links, authz, httpChallenges, challenge, thumbprint, keyAuthorization, challengePath; - - if (err) { - return handleErr(err); - } - - if (Math.floor(res.statusCode/100)!==2) { - return handleErr(null, 'Authorization request failed ('+res.statusCode+')'); - } - - links=Acme.parseLink(res.headers.link); - if (!links || !('next' in links)) { - return handleErr(err, 'Server didn\'t provide information to proceed (2)'); - } - - state.authorizationUrl=res.headers.location; - state.newCertUrl=links.next; - - authz=body; - - httpChallenges=authz.challenges.filter(function(x) { - return x.type==='http-01'; - }); - if (httpChallenges.length===0) { - return handleErr(null, 'Server didn\'t offer any challenge we can handle.'); - } - challenge=httpChallenges[0]; - - thumbprint=thumbprinter(state.accountKeyPair.publicKey); - keyAuthorization=challenge.token+'.'+thumbprint; - state.responseUrl=challenge.uri; - - options.setChallenge(state.domain, challenge.token, keyAuthorization, challengeDone); function challengeDone(err) { if (err) { @@ -200,6 +125,41 @@ module.exports.create = function (deps) { }); }); } + + var links, authz, httpChallenges, challenge, thumbprint, keyAuthorization; + + if (err) { + return handleErr(err); + } + + if (Math.floor(res.statusCode/100)!==2) { + return handleErr(null, 'Authorization request failed ('+res.statusCode+')'); + } + + links=Acme.parseLink(res.headers.link); + if (!links || !('next' in links)) { + return handleErr(err, 'Server didn\'t provide information to proceed (2)'); + } + + state.authorizationUrl=res.headers.location; + state.newCertUrl=links.next; + + authz=body; + + httpChallenges=authz.challenges.filter(function(x) { + return x.type==='http-01'; + }); + if (httpChallenges.length===0) { + return handleErr(null, 'Server didn\'t offer any challenge we can handle.'); + } + challenge=httpChallenges[0]; + + thumbprint=RSA.thumbprint(state.accountKeyPair); + keyAuthorization=challenge.token+'.'+thumbprint; + state.responseUrl=challenge.uri; + + options.setChallenge(state.domain, challenge.token, keyAuthorization, challengeDone); + } function ensureValidation(err, res, body, unlink) { @@ -256,7 +216,7 @@ module.exports.create = function (deps) { } function getCertificate() { - var csr=generateCsr(state.certPrivateKey, state.validatedDomains); + var csr=RSA.generateCsrWeb64(state.certPrivateKey, state.validatedDomains); log('Requesting certificate...'); state.acme.post(state.newCertUrl, { resource:'new-cert', @@ -374,6 +334,50 @@ module.exports.create = function (deps) { log(text, err, info); cb(err || new Error(text)); } + + var NOOP = function () {}; + var log = options.debug ? console.log : NOOP; + var state={ + validatedDomains:[] + , validAuthorizationUrls:[] + , newAuthzUrl: options.newAuthzUrl + , newCertUrl: options.newCertUrl + }; + + if (!options.newAuthzUrl) { + return handleErr(new Error("options.newAuthzUrl must be the authorization url")); + } + 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.domainPrivateKeyPem) { + return handleErr(new Error("options.domainPrivateKeyPem must be an ascii private key pem")); + } + if (!options.setChallenge) { + return handleErr(new Error("options.setChallenge must be function(hostname, challengeKey, tokenValue, done) {}")); + } + if (!options.removeChallenge) { + return handleErr(new Error("options.removeChallenge must be function(hostname, challengeKey, done) {}")); + } + if (!(options.domains && options.domains.length)) { + return handleErr(new Error("options.domains must be an array of domains such as ['example.com', 'www.example.com']")); + } + + 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 }); + } catch(err) { + return handleErr(err, 'Failed to parse privateKey'); + } + + nextDomain(); } function certBufferToPem(cert) { diff --git a/lib/node.js b/lib/node.js index 1962c5c..ad91461 100644 --- a/lib/node.js +++ b/lib/node.js @@ -6,43 +6,9 @@ 'use strict'; var request = require('request'); +var RSA = require('rsa-compat').RSA; var leUtils = require('./acme-util'); -var leCrypto = require('./letsencrypt-node-crypto'); -var leExtra = require('./letsencrypt-forge-extra'); -var leForge = require('./letsencrypt-forge'); -var leUrsa; - -try { - leUrsa = require('./letsencrypt-ursa'); -} catch(e) { - leUrsa = {}; - // things will run a little slower on keygen, but it'll work on windows - // (but don't try this on raspberry pi - 20+ MINUTES key generation) -} - -// order of crypto precdence is -// * native -// * ursa -// * forge extra (the new one aimed to be less-forgey) -// * forge (fallback) -Object.keys(leUrsa).forEach(function (key) { - if (!leCrypto[key]) { - leCrypto[key] = leUrsa[key]; - } -}); - -Object.keys(leExtra).forEach(function (key) { - if (!leCrypto[key]) { - leCrypto[key] = leExtra[key]; - } -}); - -Object.keys(leForge).forEach(function (key) { - if (!leCrypto[key]) { - leCrypto[key] = leForge[key]; - } -}); module.exports.request = request; -module.exports.leCrypto = leCrypto; module.exports.leUtils = leUtils; +module.exports.RSA = RSA; diff --git a/lib/register-new-account.js b/lib/register-new-account.js index b51184c..5ab42ab 100644 --- a/lib/register-new-account.js +++ b/lib/register-new-account.js @@ -9,33 +9,10 @@ module.exports.create = function (deps) { var NOOP=function () {}, log=NOOP; var request=deps.request; - var importPemPrivateKey=deps.leCrypto.importPemPrivateKey; + var RSA = deps.RSA; var Acme = deps.Acme; function registerNewAccount(options, cb) { - var state = {}; - - if (!options.accountPrivateKeyPem) { - return handleErr(new Error("options.accountPrivateKeyPem must be an ascii private key pem")); - } - if (!options.agreeToTerms) { - cb(new Error("options.agreeToTerms must be function (tosUrl, fn => (err, true))")); - return; - } - if (!options.newRegUrl) { - cb(new Error("options.newRegUrl must be the a new registration url")); - return; - } - if (!options.email) { - cb(new Error("options.email must be an email")); - return; - } - - state.accountKeyPem=options.accountPrivateKeyPem; - state.accountKeyPair=importPemPrivateKey(state.accountKeyPem); - state.acme=new Acme(state.accountKeyPair); - - register(); function register() { state.acme.post(options.newRegUrl, { @@ -123,6 +100,30 @@ module.exports.create = function (deps) { log(text, err, info); cb(err || new Error(text)); } + + var state = {}; + + if (!options.accountPrivateKeyPem) { + return handleErr(new Error("options.accountPrivateKeyPem must be an ascii private key pem")); + } + if (!options.agreeToTerms) { + cb(new Error("options.agreeToTerms must be function (tosUrl, fn => (err, true))")); + return; + } + if (!options.newRegUrl) { + cb(new Error("options.newRegUrl must be the a new registration url")); + return; + } + if (!options.email) { + cb(new Error("options.email must be an email")); + return; + } + + state.accountKeyPem=options.accountPrivateKeyPem; + state.accountKeyPair=RSA.import({ privateKeyPem: state.accountKeyPem }); + state.acme=new Acme(state.accountKeyPair); + + register(); } return registerNewAccount; diff --git a/node.js b/node.js index 80dc037..8a7c3fa 100644 --- a/node.js +++ b/node.js @@ -26,8 +26,6 @@ function create(deps) { LeCore.registerNewAccount = require('./lib/register-new-account').create(deps); LeCore.getCertificate = require('./lib/get-certificate').create(deps); - LeCore.leCrypto = deps.leCrypto; - return LeCore; } diff --git a/package.json b/package.json index dd718fd..1b9be18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "letiny-core", - "version": "1.0.5", + "version": "1.1.0", "description": "A framework for building letsencrypt clients, forked from letiny", "main": "node.js", "browser": "browser.js", @@ -30,7 +30,8 @@ ], "dependencies": { "node-forge": "^0.6.38", - "request": "^2.55.0" + "request": "^2.55.0", + "rsa-compat": "^1.0.1" }, "optionalDependencies": { "ursa": "^0.9.1"