moving to rsa-compat
This commit is contained in:
parent
e0909ad0ca
commit
55707a417d
|
@ -11,7 +11,7 @@ module.exports.create = function (deps) {
|
||||||
var NOOP=function () {};
|
var NOOP=function () {};
|
||||||
var log=NOOP;
|
var log=NOOP;
|
||||||
var request=require('request');
|
var request=require('request');
|
||||||
var generateSignature=deps.leCrypto.generateSignature;
|
var generateSignature=deps.RSA.signJws;
|
||||||
|
|
||||||
function Acme(privateKey) {
|
function Acme(privateKey) {
|
||||||
this.privateKey=privateKey;
|
this.privateKey=privateKey;
|
||||||
|
|
|
@ -9,53 +9,13 @@
|
||||||
module.exports.create = function (deps) {
|
module.exports.create = function (deps) {
|
||||||
var request=deps.request;
|
var request=deps.request;
|
||||||
var toStandardB64 = deps.leUtils.toStandardB64;
|
var toStandardB64 = deps.leUtils.toStandardB64;
|
||||||
var importPemPrivateKey = deps.leCrypto.importPemPrivateKey;
|
//var importPemPrivateKey = deps.leCrypto.importPemPrivateKey;
|
||||||
var thumbprinter = deps.leCrypto.thumbprint;
|
//var thumbprinter = deps.leCrypto.thumbprint;
|
||||||
var generateCsr = deps.leCrypto.generateCsr || deps.leCrypto.generateCSR;
|
//var generateCsr = deps.leCrypto.generateCsr || deps.leCrypto.generateCSR;
|
||||||
var Acme = deps.Acme;
|
var Acme = deps.Acme;
|
||||||
|
var RSA = deps.RSA;
|
||||||
|
|
||||||
function getCert(options, cb) {
|
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) {
|
function bodyToError(res, body) {
|
||||||
var err;
|
var err;
|
||||||
|
@ -80,7 +40,7 @@ module.exports.create = function (deps) {
|
||||||
if (Math.floor(res.statusCode / 100) !== 2) {
|
if (Math.floor(res.statusCode / 100) !== 2) {
|
||||||
err = new Error("[Error] letiny-core: not 200 ok");
|
err = new Error("[Error] letiny-core: not 200 ok");
|
||||||
err.code = "E_STATUS_CODE";
|
err.code = "E_STATUS_CODE";
|
||||||
err.type = body.type
|
err.type = body.type;
|
||||||
err.description = body;
|
err.description = body;
|
||||||
err.detail = body.detail;
|
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:");
|
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;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDomain();
|
|
||||||
|
|
||||||
function nextDomain() {
|
function nextDomain() {
|
||||||
if (state.domains.length > 0) {
|
if (state.domains.length > 0) {
|
||||||
getChallenges(state.domains.shift());
|
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) {
|
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) {
|
function challengeDone(err) {
|
||||||
if (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) {
|
function ensureValidation(err, res, body, unlink) {
|
||||||
|
@ -256,7 +216,7 @@ module.exports.create = function (deps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCertificate() {
|
function getCertificate() {
|
||||||
var csr=generateCsr(state.certPrivateKey, state.validatedDomains);
|
var csr=RSA.generateCsrWeb64(state.certPrivateKey, state.validatedDomains);
|
||||||
log('Requesting certificate...');
|
log('Requesting certificate...');
|
||||||
state.acme.post(state.newCertUrl, {
|
state.acme.post(state.newCertUrl, {
|
||||||
resource:'new-cert',
|
resource:'new-cert',
|
||||||
|
@ -374,6 +334,50 @@ module.exports.create = function (deps) {
|
||||||
log(text, err, info);
|
log(text, err, info);
|
||||||
cb(err || new Error(text));
|
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) {
|
function certBufferToPem(cert) {
|
||||||
|
|
38
lib/node.js
38
lib/node.js
|
@ -6,43 +6,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
|
var RSA = require('rsa-compat').RSA;
|
||||||
var leUtils = require('./acme-util');
|
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.request = request;
|
||||||
module.exports.leCrypto = leCrypto;
|
|
||||||
module.exports.leUtils = leUtils;
|
module.exports.leUtils = leUtils;
|
||||||
|
module.exports.RSA = RSA;
|
||||||
|
|
|
@ -9,33 +9,10 @@
|
||||||
module.exports.create = function (deps) {
|
module.exports.create = function (deps) {
|
||||||
var NOOP=function () {}, log=NOOP;
|
var NOOP=function () {}, log=NOOP;
|
||||||
var request=deps.request;
|
var request=deps.request;
|
||||||
var importPemPrivateKey=deps.leCrypto.importPemPrivateKey;
|
var RSA = deps.RSA;
|
||||||
var Acme = deps.Acme;
|
var Acme = deps.Acme;
|
||||||
|
|
||||||
function registerNewAccount(options, cb) {
|
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() {
|
function register() {
|
||||||
state.acme.post(options.newRegUrl, {
|
state.acme.post(options.newRegUrl, {
|
||||||
|
@ -123,6 +100,30 @@ module.exports.create = function (deps) {
|
||||||
log(text, err, info);
|
log(text, err, info);
|
||||||
cb(err || new Error(text));
|
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;
|
return registerNewAccount;
|
||||||
|
|
2
node.js
2
node.js
|
@ -26,8 +26,6 @@ function create(deps) {
|
||||||
LeCore.registerNewAccount = require('./lib/register-new-account').create(deps);
|
LeCore.registerNewAccount = require('./lib/register-new-account').create(deps);
|
||||||
LeCore.getCertificate = require('./lib/get-certificate').create(deps);
|
LeCore.getCertificate = require('./lib/get-certificate').create(deps);
|
||||||
|
|
||||||
LeCore.leCrypto = deps.leCrypto;
|
|
||||||
|
|
||||||
return LeCore;
|
return LeCore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "letiny-core",
|
"name": "letiny-core",
|
||||||
"version": "1.0.5",
|
"version": "1.1.0",
|
||||||
"description": "A framework for building letsencrypt clients, forked from letiny",
|
"description": "A framework for building letsencrypt clients, forked from letiny",
|
||||||
"main": "node.js",
|
"main": "node.js",
|
||||||
"browser": "browser.js",
|
"browser": "browser.js",
|
||||||
|
@ -30,7 +30,8 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-forge": "^0.6.38",
|
"node-forge": "^0.6.38",
|
||||||
"request": "^2.55.0"
|
"request": "^2.55.0",
|
||||||
|
"rsa-compat": "^1.0.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"ursa": "^0.9.1"
|
"ursa": "^0.9.1"
|
||||||
|
|
Loading…
Reference in New Issue