diff --git a/History.md b/History.md index 3a4f558..780a038 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +0.0.5-beta / 2015-12 +======================= + + * Added fork option + * Added accountKey and privateKey options + 0.0.4-beta / 2015-12-13 ======================= diff --git a/lib/cli.js b/lib/cli.js index a287dce..a31c0ad 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -14,6 +14,7 @@ app .option('-c, --cert ', 'path to save your certificate (cert.pem)') .option('-k, --key ', 'path to save your private key (privkey.pem)') .option('-i, --ca ', 'path to save issuer certificate (cacert.pem)') + .option('-a, --account ', 'path of the account key (optional)') .option('--pfx ', 'path to save PKCS#12 certificate (optional)') .option('--password ', 'password for PKCS#12 certificate (optional)') .option('--aes', 'use AES instead of 3DES for PKCS#12') @@ -47,6 +48,7 @@ letiny.getCert({ certFile:app.cert || (app.pfx ? false : 'cert.pem'), keyFile:app.key || (app.pfx ? false : 'privkey.pem'), caFile:app.ca || (app.pfx ? false : 'cacert.pem'), + accountKey:app.account, pfxFile:app.pfx, pfxPassword:app.password, aes:app.aes, diff --git a/lib/client.js b/lib/client.js index 5a68ed8..d01a680 100644 --- a/lib/client.js +++ b/lib/client.js @@ -11,7 +11,7 @@ var _DEBUG, NOOP=new Function(), log=NOOP, mkdirp=require('mkdirp').sync, request=require('request'), forge=require('node-forge'), pki=forge.pki, cryptoUtil=require('./crypto-util'), util=require('./acme-util'), - fs=require('fs'), path=require('path'); + fs=require('fs'), path=require('path'), child=require('child_process'); function Acme(privateKey) { this.privateKey=privateKey; @@ -131,23 +131,81 @@ function getCert(options, cb) { log=NOOP; } - makeAccountKeyPair(); + if (options.fork && !~process.argv.indexOf('--letiny-fork')) { + state.child=child.fork(__filename, ['--letiny-fork']); + if (options.challenge) { + return cb(new Error('fork+challenge not supported yet')); + } + state.child.send({request:options}); + state.child.on('message', function(msg) { + var res; + if (msg.result) { + res=msg.result; + cb(res.err ? new Error(res.err) : null, res.cert, res.key, res.ca); + } + }); + return; + } - function makeAccountKeyPair() { + if (options.accountKey) { + if (options.accountKey.length>255) { + state.accountKeyPEM=options.accountKey; + } else { + try { + state.accountKeyPEM=fs.readFileSync(options.accountKey); + } catch(err) { + if (err.code==='ENOENT') { + makeAccountKeyPair(true); + } else { + return handleErr(err, 'Failed to load accountKey'); + } + } + try { + state.accountKeyPair=cryptoUtil.importPemPrivateKey(state.accountKeyPEM); + } catch(err) { + return handleErr(err, 'Failed to parse accountKey'); + } + initAcme(); + } + } else { + makeAccountKeyPair(); + } + + function makeAccountKeyPair(save) { var keypair; log('Generating account keypair...'); keypair=pki.rsa.generateKeyPair(2048); - state.accountKeyPair=cryptoUtil.importPemPrivateKey(pki.privateKeyToPem(keypair.privateKey)); + state.accountKeyPEM=pki.privateKeyToPem(keypair.privateKey); + state.accountKeyPair=cryptoUtil.importPemPrivateKey(state.accountKeyPEM); + if (save) { + try { + fs.writeFileSync(options.accountKey, state.accountKeyPEM); + } catch(err) { + return handleErr(err, 'Failed to save accountKey'); + } + } + initAcme(); + } + + function initAcme() { state.acme=new Acme(state.accountKeyPair); makeKeyPair(); } function makeKeyPair() { var keypair; - log('Generating cert keypair...'); - keypair=pki.rsa.generateKeyPair(2048); - state.certPrivateKeyPEM=pki.privateKeyToPem(keypair.privateKey); - state.certPrivateKey=cryptoUtil.importPemPrivateKey(state.certPrivateKeyPEM); + if (options.privateKey) { + state.certPrivateKeyPEM=options.privateKey; + } else { + log('Generating cert keypair...'); + keypair=pki.rsa.generateKeyPair(2048); + state.certPrivateKeyPEM=pki.privateKeyToPem(keypair.privateKey); + } + try { + state.certPrivateKey=cryptoUtil.importPemPrivateKey(state.certPrivateKeyPEM); + } catch(err) { + return handleErr(err, 'Failed to parse privateKey'); + } register(); } @@ -459,5 +517,22 @@ function parseLink(link) { } } +if (~process.argv.indexOf('--letiny-fork')) { + process.on('message', function(msg) { + if (msg.request) { + getCert(msg.request.options, function(err, cert, key, ca) { + process.send({ + result:{ + err:err ? err.stack : null, + cert:cert, + key:key, + ca:ca + } + }); + }); + } + }); +} + exports.getCert=getCert; diff --git a/package.json b/package.json index 602d990..12143bb 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "letiny", - "version": "0.0.4-beta", + "version": "0.0.5-beta", "description": "Tiny ACME client library and CLI", "author": "Anatol Sommer ", - "license": "MPL", + "license": "MPL-2.0", "repository": { "type": "git", "url": "https://github.com/anatolsommer/letiny.git"