acme.js/tests/index.js

226 lines
5.1 KiB
JavaScript
Raw Normal View History

2019-10-04 23:35:59 +00:00
'use strict';
2019-10-05 11:21:07 +00:00
require('dotenv').config();
2019-10-08 21:50:17 +00:00
var CSR = require('@root/csr');
var Enc = require('@root/encoding/base64');
2019-10-15 11:01:52 +00:00
var PEM = require('@root/pem');
2019-10-08 10:33:14 +00:00
var punycode = require('punycode');
2019-10-08 19:40:01 +00:00
var ACME = require('../acme.js');
2019-10-15 11:01:52 +00:00
var Keypairs = require('@root/keypairs');
2019-10-06 07:22:18 +00:00
var acme = ACME.create({
// debug: true
});
2019-10-05 11:21:07 +00:00
// TODO exec npm install --save-dev CHALLENGE_MODULE
2019-10-04 23:35:59 +00:00
var config = {
env: process.env.ENV,
email: process.env.SUBSCRIBER_EMAIL,
2019-10-05 11:21:07 +00:00
domain: process.env.BASE_DOMAIN,
challengeType: process.env.CHALLENGE_TYPE,
2019-10-06 07:22:18 +00:00
challengeModule: process.env.CHALLENGE_PLUGIN,
2019-10-05 11:21:07 +00:00
challengeOptions: JSON.parse(process.env.CHALLENGE_OPTIONS)
2019-10-04 23:35:59 +00:00
};
config.debug = !/^PROD/i.test(config.env);
2019-10-06 07:22:18 +00:00
var pluginPrefix = 'acme-' + config.challengeType + '-';
var pluginName = config.challengeModule;
var plugin;
function badPlugin(err) {
if ('MODULE_NOT_FOUND' !== err.code) {
console.error(err);
return;
}
console.error("Couldn't find '" + pluginName + "'. Is it installed?");
console.error("\tnpm install --save-dev '" + pluginName + "'");
}
try {
plugin = require(pluginName);
} catch (err) {
if (
'MODULE_NOT_FOUND' !== err.code ||
0 === pluginName.indexOf(pluginPrefix)
) {
badPlugin(err);
process.exit(1);
}
try {
pluginName = pluginPrefix + pluginName;
plugin = require(pluginName);
} catch (e) {
badPlugin(e);
process.exit(1);
}
}
config.challenger = plugin.create(config.challengeOptions);
2019-10-05 11:21:07 +00:00
if (!config.challengeType || !config.domain) {
console.error(
new Error('Missing config variables. Check you .env and the docs')
.message
);
console.error(config);
process.exit(1);
}
var challenges = {};
challenges[config.challengeType] = config.challenger;
2019-10-04 23:35:59 +00:00
2019-10-06 07:22:18 +00:00
async function happyPath(accKty, srvKty, rnd) {
2019-10-04 23:35:59 +00:00
var agreed = false;
var metadata = await acme.init(
'https://acme-staging-v02.api.letsencrypt.org/directory'
);
// Ready to use, show page
if (config.debug) {
console.info('ACME.js initialized');
console.info(metadata);
2019-10-08 10:33:14 +00:00
console.info();
2019-10-04 23:35:59 +00:00
console.info();
}
2019-10-06 07:22:18 +00:00
var accountKeypair = await Keypairs.generate({ kty: accKty });
2019-10-04 23:35:59 +00:00
if (config.debug) {
console.info('Account Key Created');
console.info(JSON.stringify(accountKeypair, null, 2));
2019-10-08 10:33:14 +00:00
console.info();
2019-10-04 23:35:59 +00:00
console.info();
}
var account = await acme.accounts.create({
agreeToTerms: agree,
// TODO detect jwk/pem/der?
accountKeypair: { privateKeyJwk: accountKeypair.private },
2019-10-08 10:48:31 +00:00
subscriberEmail: config.email
2019-10-04 23:35:59 +00:00
});
2019-10-21 19:45:47 +00:00
2019-10-04 23:35:59 +00:00
// TODO top-level agree
function agree(tos) {
if (config.debug) {
console.info('Agreeing to Terms of Service:');
console.info(tos);
2019-10-08 10:33:14 +00:00
console.info();
2019-10-04 23:35:59 +00:00
console.info();
}
agreed = true;
return Promise.resolve(tos);
}
if (config.debug) {
console.info('New Subscriber Account');
console.info(JSON.stringify(account, null, 2));
console.info();
console.info();
}
if (!agreed) {
throw new Error('Failed to ask the user to agree to terms');
}
2019-10-08 21:50:17 +00:00
var certKeypair = await Keypairs.generate({ kty: srvKty });
var pem = await Keypairs.export({
jwk: certKeypair.private,
encoding: 'pem'
});
2019-10-04 23:35:59 +00:00
if (config.debug) {
console.info('Server Key Created');
2019-10-08 21:50:17 +00:00
console.info('privkey.jwk.json');
console.info(JSON.stringify(certKeypair, null, 2));
// This should be saved as `privkey.pem`
2019-10-04 23:35:59 +00:00
console.info();
2019-10-08 21:50:17 +00:00
console.info('privkey.' + srvKty.toLowerCase() + '.pem:');
console.info(pem);
2019-10-05 11:21:07 +00:00
console.info();
}
2019-10-08 21:50:17 +00:00
// 'subject' should be first in list
2019-10-06 07:22:18 +00:00
var domains = randomDomains(rnd);
2019-10-05 11:21:07 +00:00
if (config.debug) {
console.info('Get certificates for random domains:');
2019-10-08 10:33:14 +00:00
console.info(
domains
.map(function(puny) {
var uni = punycode.toUnicode(puny);
if (puny !== uni) {
return puny + ' (' + uni + ')';
}
return puny;
})
.join('\n')
);
console.info();
2019-10-05 11:21:07 +00:00
}
2019-10-08 21:50:17 +00:00
// Create CSR
var csrDer = await CSR.csr({
jwk: certKeypair.private,
domains: domains,
encoding: 'der'
});
var csr = Enc.bufToUrlBase64(csrDer);
var csrPem = PEM.packBlock({
type: 'CERTIFICATE REQUEST',
bytes: csrDer /* { jwk: jwk, domains: opts.domains } */
});
if (config.debug) {
console.info('Certificate Signing Request');
console.info(csrPem);
console.info();
}
2019-10-05 11:21:07 +00:00
var results = await acme.certificates.create({
account: account,
accountKeypair: { privateKeyJwk: accountKeypair.private },
2019-10-08 21:50:17 +00:00
csr: csr,
2019-10-05 11:21:07 +00:00
domains: domains,
challenges: challenges, // must be implemented
2019-10-08 21:50:17 +00:00
customerEmail: null
2019-10-05 11:21:07 +00:00
});
if (config.debug) {
console.info('Got SSL Certificate:');
2019-10-06 07:22:18 +00:00
console.info(Object.keys(results));
2019-10-05 11:21:07 +00:00
console.info(results.expires);
console.info(results.cert);
console.info(results.chain);
2019-10-08 10:33:14 +00:00
console.info();
console.info();
2019-10-04 23:35:59 +00:00
}
}
2019-10-06 07:22:18 +00:00
// Try EC + RSA
var rnd = random();
happyPath('EC', 'RSA', rnd)
2019-10-04 23:35:59 +00:00
.then(function() {
2019-10-06 07:22:18 +00:00
// Now try RSA + EC
rnd = random();
return happyPath('RSA', 'EC', rnd).then(function() {
console.info('success');
});
2019-10-04 23:35:59 +00:00
})
.catch(function(err) {
console.error('Error:');
console.error(err.stack);
});
2019-10-06 07:22:18 +00:00
function randomDomains(rnd) {
2019-10-04 23:35:59 +00:00
return ['foo-acmejs', 'bar-acmejs', '*.baz-acmejs', 'baz-acmejs'].map(
function(pre) {
2019-10-08 10:33:14 +00:00
return punycode.toASCII(pre + '-' + rnd + '.' + config.domain);
2019-10-04 23:35:59 +00:00
}
);
}
function random() {
2019-10-08 10:33:14 +00:00
return (
parseInt(
Math.random()
.toString()
.slice(2, 99),
10
)
.toString(16)
.slice(0, 4) + '例'
);
2019-10-04 23:35:59 +00:00
}