Merge branch 'rsa-compat'

This commit is contained in:
AJ ONeal 2016-08-03 23:13:56 -04:00
commit c80f7aa624
4 changed files with 83 additions and 41 deletions

View File

@ -297,6 +297,7 @@ the python client, but it's not necessary)
Change History Change History
============== ==============
* v1.5.0 now using letiny-core v2.0.0 and rsa-compat
* v1.4.x I can't remember... but it's better! * v1.4.x I can't remember... but it's better!
* v1.1.0 Added letiny-core, removed node-letsencrypt-python * v1.1.0 Added letiny-core, removed node-letsencrypt-python
* v1.0.2 Works with node-letsencrypt-python * v1.0.2 Works with node-letsencrypt-python

View File

@ -1,8 +1,9 @@
'use strict'; 'use strict';
var PromiseA = require('bluebird'); var PromiseA = require('bluebird');
var crypto = require('crypto');
var LeCore = require('letiny-core'); var LeCore = require('letiny-core');
var leCrypto = LeCore.leCrypto; var RSA = PromiseA.promisifyAll(require('rsa-compat').RSA);
var path = require('path'); var path = require('path');
var mkdirpAsync = PromiseA.promisify(require('mkdirp')); var mkdirpAsync = PromiseA.promisify(require('mkdirp'));
var fs = PromiseA.promisifyAll(require('fs')); var fs = PromiseA.promisifyAll(require('fs'));
@ -11,10 +12,8 @@ function createAccount(args, handlers) {
var os = require("os"); var os = require("os");
var localname = os.hostname(); var localname = os.hostname();
// TODO support ECDSA
// arg.rsaBitLength args.rsaExponent // arg.rsaBitLength args.rsaExponent
return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537).then(function (pems) { return RSA.generateKeypairAsync(args.rsaKeySize || 2048, 65537, { public: true, pem: true }).then(function (keypair) {
/* pems = { privateKeyPem, privateKeyJwk, publicKeyPem, publicKeyMd5, publicKeySha256 } */
return LeCore.registerNewAccountAsync({ return LeCore.registerNewAccountAsync({
email: args.email email: args.email
@ -24,12 +23,15 @@ function createAccount(args, handlers) {
args.tosUrl = tosUrl; args.tosUrl = tosUrl;
handlers.agreeToTerms(args, agree); handlers.agreeToTerms(args, agree);
} }
, accountPrivateKeyPem: pems.privateKeyPem , accountKeypair: keypair
, debug: args.debug || handlers.debug , debug: args.debug || handlers.debug
}).then(function (body) { }).then(function (body) {
// TODO XXX use sha256 // TODO XXX use sha256 (the python client uses md5)
var accountId = pems.publicKeyMd5; // TODO ssh fingerprint (noted on rsa-compat issues page, I believe)
keypair.publicKeyMd5 = crypto.createHash('md5').update(RSA.exportPublicPem(keypair)).digest('hex');
keypair.publicKeySha256 = crypto.createHash('sha256').update(RSA.exportPublicPem(keypair)).digest('hex');
var accountId = keypair.publicKeyMd5;
var accountDir = path.join(args.accountsDir, accountId); var accountDir = path.join(args.accountsDir, accountId);
var regr = { body: body }; var regr = { body: body };
@ -44,11 +46,12 @@ function createAccount(args, handlers) {
, creation_dt: isoDate , creation_dt: isoDate
}; };
// TODO abstract file writing
return PromiseA.all([ return PromiseA.all([
// meta.json {"creation_host": "ns1.redirect-www.org", "creation_dt": "2015-12-11T04:14:38Z"} // meta.json {"creation_host": "ns1.redirect-www.org", "creation_dt": "2015-12-11T04:14:38Z"}
fs.writeFileAsync(path.join(accountDir, 'meta.json'), JSON.stringify(accountMeta), 'utf8') fs.writeFileAsync(path.join(accountDir, 'meta.json'), JSON.stringify(accountMeta), 'utf8')
// private_key.json { "e", "d", "n", "q", "p", "kty", "qi", "dp", "dq" } // private_key.json { "e", "d", "n", "q", "p", "kty", "qi", "dp", "dq" }
, fs.writeFileAsync(path.join(accountDir, 'private_key.json'), JSON.stringify(pems.privateKeyJwk), 'utf8') , fs.writeFileAsync(path.join(accountDir, 'private_key.json'), JSON.stringify(RSA.exportPrivateJwk(keypair)), 'utf8')
// regr.json: // regr.json:
/* /*
{ body: { contact: [ 'mailto:coolaj86@gmail.com' ], { body: { contact: [ 'mailto:coolaj86@gmail.com' ],
@ -60,8 +63,11 @@ function createAccount(args, handlers) {
*/ */
, fs.writeFileAsync(path.join(accountDir, 'regr.json'), JSON.stringify(regr), 'utf8') , fs.writeFileAsync(path.join(accountDir, 'regr.json'), JSON.stringify(regr), 'utf8')
]).then(function () { ]).then(function () {
var pems = {};
// pems.private_key;
pems.meta = accountMeta; pems.meta = accountMeta;
pems.privateKey = pems.privateKeyJwk; pems.keypair = keypair;
pems.regr = regr; pems.regr = regr;
pems.accountId = accountId; pems.accountId = accountId;
pems.id = accountId; pems.id = accountId;
@ -106,18 +112,18 @@ function getAccount(args, handlers) {
return createAccount(args, handlers); return createAccount(args, handlers);
} }
return leCrypto.privateJwkToPemsAsync(files.private_key).then(function (keypair) { var keypair = { privateKeyJwk: files.private_key };
files.accountId = accountId; // preserve current account id keypair.privateKeyPem = RSA.exportPrivatePem(keypair);
files.id = accountId; keypair.publicKeyPem = RSA.exportPublicPem(keypair);
files.publicKeySha256 = keypair.publicKeySha256;
files.publicKeyMd5 = keypair.publicKeyMd5;
files.publicKeyPem = keypair.publicKeyPem; // ascii PEM: ----BEGIN...
files.privateKeyPem = keypair.privateKeyPem; // ascii PEM: ----BEGIN...
files.privateKeyJson = keypair.privateKeyJwk; // json { n: ..., e: ..., iq: ..., etc }
files.privateKeyJwk = keypair.privateKeyJwk; // json { n: ..., e: ..., iq: ..., etc }
return files; //files.private_key;
}); //files.regr;
//files.meta;
files.accountId = accountId; // preserve current account id
files.id = accountId;
files.keypair = keypair;
return files;
}); });
} }

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
var PromiseA = require('bluebird'); var PromiseA = require('bluebird');
var RSA = PromiseA.promisifyAll(require('rsa-compat').RSA);
var mkdirpAsync = PromiseA.promisify(require('mkdirp')); var mkdirpAsync = PromiseA.promisify(require('mkdirp'));
var path = require('path'); var path = require('path');
var fs = PromiseA.promisifyAll(require('fs')); var fs = PromiseA.promisifyAll(require('fs'));
var sfs = require('safe-replace'); var sfs = require('safe-replace');
var LE = require('../'); var LE = require('../');
var LeCore = PromiseA.promisifyAll(require('letiny-core')); var LeCore = PromiseA.promisifyAll(require('letiny-core'));
var leCrypto = PromiseA.promisifyAll(LeCore.leCrypto);
var Accounts = require('./accounts'); var Accounts = require('./accounts');
var merge = require('./common').merge; var merge = require('./common').merge;
@ -166,7 +166,7 @@ function writeCertificateAsync(args, defaults, handlers) {
var obj = args.pyobj; var obj = args.pyobj;
var result = args.pems; var result = args.pems;
result.fullchain = result.cert + '\n' + result.ca; result.fullchain = result.cert + '\n' + (result.chain || result.ca);
obj.checkpoints = parseInt(obj.checkpoints, 10) || 0; obj.checkpoints = parseInt(obj.checkpoints, 10) || 0;
var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]); var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]);
@ -193,18 +193,28 @@ function writeCertificateAsync(args, defaults, handlers) {
return mkdirpAsync(archiveDir).then(function () { return mkdirpAsync(archiveDir).then(function () {
return PromiseA.all([ return PromiseA.all([
sfs.writeFileAsync(certArchive, result.cert, 'ascii') sfs.writeFileAsync(certArchive, result.cert, 'ascii')
, sfs.writeFileAsync(chainArchive, result.ca || result.chain, 'ascii') , sfs.writeFileAsync(chainArchive, (result.chain || result.ca), 'ascii')
, sfs.writeFileAsync(fullchainArchive, result.fullchain, 'ascii') , sfs.writeFileAsync(fullchainArchive, result.fullchain, 'ascii')
, sfs.writeFileAsync(privkeyArchive, result.key || result.privkey || args.domainPrivateKeyPem, 'ascii') , sfs.writeFileAsync(
privkeyArchive
// TODO nix args.key, args.domainPrivateKeyPem ??
, (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair)
, 'ascii'
)
]); ]);
}).then(function () { }).then(function () {
return mkdirpAsync(liveDir); return mkdirpAsync(liveDir);
}).then(function () { }).then(function () {
return PromiseA.all([ return PromiseA.all([
sfs.writeFileAsync(certPath, result.cert, 'ascii') sfs.writeFileAsync(certPath, result.cert, 'ascii')
, sfs.writeFileAsync(chainPath, result.ca || result.chain, 'ascii') , sfs.writeFileAsync(chainPath, (result.chain || result.ca), 'ascii')
, sfs.writeFileAsync(fullchainPath, result.fullchain, 'ascii') , sfs.writeFileAsync(fullchainPath, result.fullchain, 'ascii')
, sfs.writeFileAsync(privkeyPath, result.key || result.privkey || args.domainPrivateKeyPem, 'ascii') , sfs.writeFileAsync(
privkeyPath
// TODO nix args.key, args.domainPrivateKeyPem ??
, (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair)
, 'ascii'
)
]); ]);
}).then(function () { }).then(function () {
obj.checkpoints += 1; obj.checkpoints += 1;
@ -219,10 +229,14 @@ function writeCertificateAsync(args, defaults, handlers) {
, fullchainPath: fullchainPath , fullchainPath: fullchainPath
, privkeyPath: privkeyPath , privkeyPath: privkeyPath
// TODO nix keypair
, keypair: args.domainKeypair
// TODO nix args.key, args.domainPrivateKeyPem ??
// some ambiguity here... // some ambiguity here...
, privkey: result.key || result.privkey || args.domainPrivateKeyPem , privkey: (result.privkey || result.key) || RSA.exportPrivatePem(args.domainKeypair)
, fullchain: result.fullchain || result.cert , fullchain: result.fullchain || (result.cert + '\n' + result.chain)
, chain: result.ca || result.chain , chain: (result.chain || result.ca)
// especially this one... might be cert only, might be fullchain // especially this one... might be cert only, might be fullchain
, cert: result.cert , cert: result.cert
@ -235,21 +249,39 @@ function writeCertificateAsync(args, defaults, handlers) {
function getCertificateAsync(args, defaults, handlers) { function getCertificateAsync(args, defaults, handlers) {
var account = args.account; var account = args.account;
var promise; var promise;
var keypairOpts = { public: true, pem: true };
if (args.domainKeyPath) { if (!args.domainKeyPath) {
// TODO use existing pre-generated key if avaibale // TODO use default path ???
console.warn("[LE /lib/core.js] retrieve from domainKeyPath NOT IMPLEMENTED (please file an issue to remind me about this)"); if (args.debug) {
promise = leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537); console.log('[domainKeyPath]: none');
} else { }
promise = leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537); promise = RSA.generateKeypairAsync(args.rsaKeySize, 65537, keypairOpts);
} }
return promise.then(function (domainKey) { if (args.domainKeyPath) {
if (args.debug) {
console.log('[domainKeyPath]:', args.domainKeyPath);
}
promise = fs.readFileAsync(args.domainKeyPath, 'ascii').then(function (pem) {
return RSA.import({ privateKeyPem: pem });
}, function (/*err*/) {
return RSA.generateKeypairAsync(args.rsaKeySize, 65537, keypairOpts).then(function (keypair) {
return mkdirpAsync(path.dirname(args.domainKeyPath)).then(function () {
return fs.writeFileAsync(args.domainKeyPath, keypair.privateKeyPem, 'ascii').then(function () {
return keypair;
});
});
});
});
}
return promise.then(function (domainKeypair) {
if (args.debug) { if (args.debug) {
console.log("[letsencrypt/lib/core.js] get certificate"); console.log("[letsencrypt/lib/core.js] get certificate");
} }
args.domainPrivateKeyPem = args.domainPrivateKeyPem || domainKey.privateKeyPem; args.domainKeypair = domainKeypair;
//args.registration = domainKey; //args.registration = domainKey;
return LeCore.getCertificateAsync({ return LeCore.getCertificateAsync({
@ -258,8 +290,8 @@ function getCertificateAsync(args, defaults, handlers) {
, newAuthzUrl: args._acmeUrls.newAuthz , newAuthzUrl: args._acmeUrls.newAuthz
, newCertUrl: args._acmeUrls.newCert , newCertUrl: args._acmeUrls.newCert
, accountPrivateKeyPem: account.privateKeyPem , accountKeypair: RSA.import(account.keypair)
, domainPrivateKeyPem: domainKey.privateKeyPem , domainKeypair: domainKeypair
, domains: args.domains , domains: args.domains
// //
@ -302,6 +334,7 @@ function getCertificateAsync(args, defaults, handlers) {
} }
}); });
}).then(function (results) { }).then(function (results) {
// { cert, chain, fullchain, privkey }
args.pems = results; args.pems = results;
return writeCertificateAsync(args, defaults, handlers); return writeCertificateAsync(args, defaults, handlers);
}); });
@ -335,6 +368,7 @@ function getOrCreateDomainCertificate(args, defaults, handlers) {
}); });
} }
// returns 'account' from lib/accounts { meta, regr, keypair, accountId (id) }
function getOrCreateAcmeAccount(args, defaults, handlers) { function getOrCreateAcmeAccount(args, defaults, handlers) {
var pyconf = PromiseA.promisifyAll(require('pyconf')); var pyconf = PromiseA.promisifyAll(require('pyconf'));

View File

@ -1,6 +1,6 @@
{ {
"name": "letsencrypt", "name": "letsencrypt",
"version": "1.4.4", "version": "1.5.0",
"description": "Let's Encrypt for node.js on npm", "description": "Let's Encrypt for node.js on npm",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -37,10 +37,11 @@
"dependencies": { "dependencies": {
"bluebird": "^3.0.6", "bluebird": "^3.0.6",
"homedir": "^0.6.0", "homedir": "^0.6.0",
"letiny-core": "^1.0.5", "letiny-core": "^2.0.1",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"pyconf": "^1.1.2", "pyconf": "^1.1.2",
"request": "^2.67.0", "request": "^2.67.0",
"rsa-compat": "^1.2.1",
"safe-replace": "^1.0.2" "safe-replace": "^1.0.2"
} }
} }