mirror of
https://github.com/certd/certd.git
synced 2026-04-24 12:27:25 +08:00
🔱: [acme] sync upgrade with 7 commits [trident-sync]
CHANGELOG Fix tls-alpn-01 pebble test on Node v18+ Return correct tls-alpn-01 key authorization, tests Support tls-alpn-01 internal challenge verification Add tls-alpn-01 challenge test server support Add ALPN crypto utility methods
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v5.3.0
|
||||||
|
|
||||||
|
* `added` Support and tests for satisfying `tls-alpn-01` challenges
|
||||||
|
* `changed` Method `getChallengeKeyAuthorization()` now returns `$token.$thumbprint` when called with a `tls-alpn-01` challenge
|
||||||
|
* Previously returned base64url encoded SHA256 digest of `$token.$thumbprint` erroneously
|
||||||
|
* This change is not considered breaking since the previous behavior was incorrect
|
||||||
|
|
||||||
## v5.2.0 (2024-01-22)
|
## v5.2.0 (2024-01-22)
|
||||||
|
|
||||||
* `fixed` Allow self-signed or invalid certs when validating `http-01` challenges that redirect to HTTPS - [#65](https://github.com/publishlab/node-acme-client/issues/65)
|
* `fixed` Allow self-signed or invalid certs when validating `http-01` challenges that redirect to HTTPS - [#65](https://github.com/publishlab/node-acme-client/issues/65)
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version
|
|||||||
/* Default ACME settings */
|
/* Default ACME settings */
|
||||||
instance.defaults.acmeSettings = {
|
instance.defaults.acmeSettings = {
|
||||||
httpChallengePort: 80,
|
httpChallengePort: 80,
|
||||||
httpsChallengePort: 443
|
httpsChallengePort: 443,
|
||||||
|
tlsAlpnChallengePort: 443
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -462,22 +462,19 @@ class AcmeClient {
|
|||||||
const thumbprint = keysum.digest('base64url');
|
const thumbprint = keysum.digest('base64url');
|
||||||
const result = `${challenge.token}.${thumbprint}`;
|
const result = `${challenge.token}.${thumbprint}`;
|
||||||
|
|
||||||
/**
|
/* https://tools.ietf.org/html/rfc8555#section-8.3 */
|
||||||
* https://tools.ietf.org/html/rfc8555#section-8.3
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (challenge.type === 'http-01') {
|
if (challenge.type === 'http-01') {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* https://tools.ietf.org/html/rfc8555#section-8.4 */
|
||||||
* https://tools.ietf.org/html/rfc8555#section-8.4
|
if (challenge.type === 'dns-01') {
|
||||||
* https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01
|
return createHash('sha256').update(result).digest('base64url');
|
||||||
*/
|
}
|
||||||
|
|
||||||
if ((challenge.type === 'dns-01') || (challenge.type === 'tls-alpn-01')) {
|
/* https://tools.ietf.org/html/rfc8737 */
|
||||||
const shasum = createHash('sha256').update(result);
|
if (challenge.type === 'tls-alpn-01') {
|
||||||
return shasum.digest('base64url');
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unable to produce key authorization, unknown challenge type: ${challenge.type}`);
|
throw new Error(`Unable to produce key authorization, unknown challenge type: ${challenge.type}`);
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ const { promisify } = require('util');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const jsrsasign = require('jsrsasign');
|
const jsrsasign = require('jsrsasign');
|
||||||
|
|
||||||
|
const randomInt = promisify(crypto.randomInt);
|
||||||
const generateKeyPair = promisify(crypto.generateKeyPair);
|
const generateKeyPair = promisify(crypto.generateKeyPair);
|
||||||
|
|
||||||
|
/* https://datatracker.ietf.org/doc/html/rfc8737#section-6.1 */
|
||||||
|
const alpnAcmeIdentifierOID = '1.3.6.1.5.5.7.1.31';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine key type and info by attempting to derive public key
|
* Determine key type and info by attempting to derive public key
|
||||||
@@ -231,7 +235,7 @@ exports.getPemBodyAsB64u = (pem) => {
|
|||||||
throw new Error('Unable to parse PEM body from string');
|
throw new Error('Unable to parse PEM body from string');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First object, hex and back to b64 without new lines */
|
/* Select first object, decode to hex and b64u */
|
||||||
return jsrsasign.hextob64u(jsrsasign.pemtohex(chain[0]));
|
return jsrsasign.hextob64u(jsrsasign.pemtohex(chain[0]));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -303,6 +307,28 @@ exports.readCsrDomains = (csrPem) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse params from a single or chain of PEM encoded certificates
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {buffer|string} certPem PEM encoded certificate or chain
|
||||||
|
* @returns {object} Certificate params
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getCertificateParams(certPem) {
|
||||||
|
const chain = splitPemChain(certPem);
|
||||||
|
|
||||||
|
if (!chain.length) {
|
||||||
|
throw new Error('Unable to parse PEM body from string');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse certificate */
|
||||||
|
const obj = new jsrsasign.X509();
|
||||||
|
obj.readCertPEM(chain[0]);
|
||||||
|
return obj.getParam();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read information from a certificate
|
* Read information from a certificate
|
||||||
* If multiple certificates are chained, the first will be read
|
* If multiple certificates are chained, the first will be read
|
||||||
@@ -324,16 +350,7 @@ exports.readCsrDomains = (csrPem) => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.readCertificateInfo = (certPem) => {
|
exports.readCertificateInfo = (certPem) => {
|
||||||
const chain = splitPemChain(certPem);
|
const params = getCertificateParams(certPem);
|
||||||
|
|
||||||
if (!chain.length) {
|
|
||||||
throw new Error('Unable to parse PEM body from string');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse certificate */
|
|
||||||
const obj = new jsrsasign.X509();
|
|
||||||
obj.readCertPEM(chain[0]);
|
|
||||||
const params = obj.getParam();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
issuer: {
|
issuer: {
|
||||||
@@ -462,7 +479,7 @@ function formatCsrAltNames(altNames) {
|
|||||||
* }, certificateKey);
|
* }, certificateKey);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.createCsr = async (data, keyPem = null) => {
|
async function createCsr(data, keyPem = null) {
|
||||||
if (!keyPem) {
|
if (!keyPem) {
|
||||||
keyPem = await createPrivateRsaKey(data.keySize);
|
keyPem = await createPrivateRsaKey(data.keySize);
|
||||||
}
|
}
|
||||||
@@ -517,10 +534,95 @@ exports.createCsr = async (data, keyPem = null) => {
|
|||||||
extreq: extensionRequests
|
extreq: extensionRequests
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Sign CSR, get PEM */
|
/* Done */
|
||||||
csr.sign();
|
|
||||||
const pem = csr.getPEM();
|
const pem = csr.getPEM();
|
||||||
|
return [keyPem, Buffer.from(pem)];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createCsr = createCsr;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a self-signed ALPN certificate for TLS-ALPN-01 challenges
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/html/rfc8737
|
||||||
|
*
|
||||||
|
* @param {object} authz Identifier authorization
|
||||||
|
* @param {string} keyAuthorization Challenge key authorization
|
||||||
|
* @param {string} [keyPem] PEM encoded CSR private key
|
||||||
|
* @returns {Promise<buffer[]>} [privateKey, certificate]
|
||||||
|
*
|
||||||
|
* @example Create a ALPN certificate
|
||||||
|
* ```js
|
||||||
|
* const [alpnKey, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Create a ALPN certificate with ECDSA private key
|
||||||
|
* ```js
|
||||||
|
* const alpnKey = await acme.crypto.createPrivateEcdsaKey();
|
||||||
|
* const [, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization, alpnKey);
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) => {
|
||||||
|
/* Create CSR first */
|
||||||
|
const now = new Date();
|
||||||
|
const commonName = authz.identifier.value;
|
||||||
|
const [key, csr] = await createCsr({ commonName }, keyPem);
|
||||||
|
|
||||||
|
/* Parse params and grab stuff we need */
|
||||||
|
const params = jsrsasign.KJUR.asn1.csr.CSRUtil.getParam(csr.toString());
|
||||||
|
const { subject, sbjpubkey, extreq, sigalg } = params;
|
||||||
|
|
||||||
|
/* ALPN extension */
|
||||||
|
const alpnExt = {
|
||||||
|
critical: true,
|
||||||
|
extname: alpnAcmeIdentifierOID,
|
||||||
|
extn: new jsrsasign.KJUR.asn1.DEROctetString({
|
||||||
|
hex: crypto.createHash('sha256').update(keyAuthorization).digest('hex')
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Pseudo-random serial - max 20 bytes, 11 for epoch (year 5138), 9 random */
|
||||||
|
const random = await randomInt(1, 999999999);
|
||||||
|
const serial = `${Math.floor(now.getTime() / 1000)}${random}`;
|
||||||
|
|
||||||
|
/* Self-signed ALPN certificate */
|
||||||
|
const certificate = new jsrsasign.KJUR.asn1.x509.Certificate({
|
||||||
|
subject,
|
||||||
|
sbjpubkey,
|
||||||
|
sigalg,
|
||||||
|
version: 3,
|
||||||
|
serial: { hex: Buffer.from(serial).toString('hex') },
|
||||||
|
issuer: subject,
|
||||||
|
notbefore: jsrsasign.datetozulu(now),
|
||||||
|
notafter: jsrsasign.datetozulu(now),
|
||||||
|
cakey: key.toString(),
|
||||||
|
ext: extreq.concat([alpnExt])
|
||||||
|
});
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
return [keyPem, Buffer.from(pem)];
|
const pem = certificate.getPEM();
|
||||||
|
return [key, Buffer.from(pem)];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that a ALPN certificate contains the expected key authorization
|
||||||
|
*
|
||||||
|
* @param {buffer|string} certPem PEM encoded certificate
|
||||||
|
* @param {string} keyAuthorization Expected challenge key authorization
|
||||||
|
* @returns {boolean} True when valid
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.isAlpnCertificateAuthorizationValid = (certPem, keyAuthorization) => {
|
||||||
|
const params = getCertificateParams(certPem);
|
||||||
|
const expectedHex = crypto.createHash('sha256').update(keyAuthorization).digest('hex');
|
||||||
|
const acmeExt = (params.ext || []).find((e) => (e && e.extname && (e.extname === alpnAcmeIdentifierOID)));
|
||||||
|
|
||||||
|
if (!acmeExt || !acmeExt.extn || !acmeExt.extn.octstr || !acmeExt.extn.octstr.hex) {
|
||||||
|
throw new Error('Unable to locate ALPN extension within parsed certificate');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if match */
|
||||||
|
return (acmeExt.extn.octstr.hex === expectedHex);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Utility methods
|
* Utility methods
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const tls = require('tls');
|
||||||
const dns = require('dns').promises;
|
const dns = require('dns').promises;
|
||||||
const { readCertificateInfo, splitPemChain } = require('./crypto');
|
const { readCertificateInfo, splitPemChain } = require('./crypto');
|
||||||
const { log } = require('./logger');
|
const { log } = require('./logger');
|
||||||
@@ -245,6 +246,60 @@ async function getAuthoritativeDnsResolver(recordName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to retrieve TLS ALPN certificate from peer
|
||||||
|
*
|
||||||
|
* https://nodejs.org/api/tls.html#tlsconnectoptions-callback
|
||||||
|
*
|
||||||
|
* @param {string} host Host the TLS client should connect to
|
||||||
|
* @param {number} port Port the client should connect to
|
||||||
|
* @param {string} servername Server name for the SNI (Server Name Indication)
|
||||||
|
* @returns {Promise<string>} PEM encoded certificate
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function retrieveTlsAlpnCertificate(host, port, timeout = 30000) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
/* TLS connection */
|
||||||
|
const socket = tls.connect({
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
servername: host,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
ALPNProtocols: ['acme-tls/1']
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.setTimeout(timeout);
|
||||||
|
socket.setEncoding('utf-8');
|
||||||
|
|
||||||
|
/* Grab certificate once connected and close */
|
||||||
|
socket.on('secureConnect', () => {
|
||||||
|
result = socket.getPeerX509Certificate();
|
||||||
|
socket.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Errors */
|
||||||
|
socket.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('timeout', () => {
|
||||||
|
socket.destroy(new Error('TLS ALPN certificate lookup request timed out'));
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Done, return cert as PEM if found */
|
||||||
|
socket.on('end', () => {
|
||||||
|
if (result) {
|
||||||
|
return resolve(result.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return reject(new Error('TLS ALPN lookup failed to retrieve certificate'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export utils
|
* Export utils
|
||||||
*/
|
*/
|
||||||
@@ -254,5 +309,6 @@ module.exports = {
|
|||||||
parseLinkHeader,
|
parseLinkHeader,
|
||||||
findCertificateChainForIssuer,
|
findCertificateChainForIssuer,
|
||||||
formatResponseError,
|
formatResponseError,
|
||||||
getAuthoritativeDnsResolver
|
getAuthoritativeDnsResolver,
|
||||||
|
retrieveTlsAlpnCertificate
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const https = require('https');
|
|||||||
const { log } = require('./logger');
|
const { log } = require('./logger');
|
||||||
const axios = require('./axios');
|
const axios = require('./axios');
|
||||||
const util = require('./util');
|
const util = require('./util');
|
||||||
|
const { isAlpnCertificateAuthorizationValid } = require('./crypto');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,11 +122,40 @@ async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify ACME TLS ALPN challenge
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/html/rfc8737
|
||||||
|
*
|
||||||
|
* @param {object} authz Identifier authorization
|
||||||
|
* @param {object} challenge Authorization challenge
|
||||||
|
* @param {string} keyAuthorization Challenge key authorization
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
|
||||||
|
const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443;
|
||||||
|
const host = authz.identifier.value;
|
||||||
|
log(`Establishing TLS connection with host: ${host}:${tlsAlpnPort}`);
|
||||||
|
|
||||||
|
const certificate = await util.retrieveTlsAlpnCertificate(host, tlsAlpnPort);
|
||||||
|
log('Certificate received from server successfully, matching key authorization in ALPN');
|
||||||
|
|
||||||
|
if (!isAlpnCertificateAuthorizationValid(certificate, keyAuthorization)) {
|
||||||
|
throw new Error(`Authorization not found in certificate from ${authz.identifier.value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export API
|
* Export API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'http-01': verifyHttpChallenge,
|
'http-01': verifyHttpChallenge,
|
||||||
'dns-01': verifyDnsChallenge
|
'dns-01': verifyDnsChallenge,
|
||||||
|
'tls-alpn-01': verifyTlsAlpnChallenge
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ const https = require('https');
|
|||||||
const { assert } = require('chai');
|
const { assert } = require('chai');
|
||||||
const cts = require('./challtestsrv');
|
const cts = require('./challtestsrv');
|
||||||
const axios = require('./../src/axios');
|
const axios = require('./../src/axios');
|
||||||
|
const { retrieveTlsAlpnCertificate } = require('./../src/util');
|
||||||
|
const { isAlpnCertificateAuthorizationValid } = require('./../src/crypto');
|
||||||
|
|
||||||
const domainName = process.env.ACME_DOMAIN_NAME || 'example.com';
|
const domainName = process.env.ACME_DOMAIN_NAME || 'example.com';
|
||||||
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
||||||
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
||||||
|
const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443;
|
||||||
|
|
||||||
|
|
||||||
describe('pebble', () => {
|
describe('pebble', () => {
|
||||||
@@ -33,6 +36,9 @@ describe('pebble', () => {
|
|||||||
const testDns01ChallengeHost = `_acme-challenge.${uuid()}.${domainName}.`;
|
const testDns01ChallengeHost = `_acme-challenge.${uuid()}.${domainName}.`;
|
||||||
const testDns01ChallengeValue = uuid();
|
const testDns01ChallengeValue = uuid();
|
||||||
|
|
||||||
|
const testTlsAlpn01ChallengeHost = `${uuid()}.${domainName}`;
|
||||||
|
const testTlsAlpn01ChallengeValue = uuid();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pebble CTS required
|
* Pebble CTS required
|
||||||
@@ -181,4 +187,29 @@ describe('pebble', () => {
|
|||||||
assert.deepStrictEqual(resp, [[testDns01ChallengeValue]]);
|
assert.deepStrictEqual(resp, [[testDns01ChallengeValue]]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TLS-ALPN-01 challenge response
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('tls-alpn-01', () => {
|
||||||
|
it('should not locate challenge response', async () => {
|
||||||
|
await assert.isRejected(retrieveTlsAlpnCertificate(testTlsAlpn01ChallengeHost, tlsAlpnPort), /(failed to retrieve)|(ssl3_read_bytes:tlsv1 alert internal error)/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should timeout challenge response', async () => {
|
||||||
|
await assert.isRejected(retrieveTlsAlpnCertificate('example.org', tlsAlpnPort, 500), /timed out/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add challenge response', async () => {
|
||||||
|
const resp = await cts.addTlsAlpn01ChallengeResponse(testTlsAlpn01ChallengeHost, testTlsAlpn01ChallengeValue);
|
||||||
|
assert.isTrue(resp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should locate challenge response', async () => {
|
||||||
|
const resp = await retrieveTlsAlpnCertificate(testTlsAlpn01ChallengeHost, tlsAlpnPort);
|
||||||
|
assert.isTrue(isAlpnCertificateAuthorizationValid(resp, testTlsAlpn01ChallengeValue));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ describe('verify', () => {
|
|||||||
const testDns01Key = uuid();
|
const testDns01Key = uuid();
|
||||||
const testDns01Cname = `${uuid()}.${domainName}`;
|
const testDns01Cname = `${uuid()}.${domainName}`;
|
||||||
|
|
||||||
|
const testTlsAlpn01Authz = { identifier: { type: 'dns', value: `${uuid()}.${domainName}` } };
|
||||||
|
const testTlsAlpn01Challenge = { type: 'dns-01', status: 'pending', token: uuid() };
|
||||||
|
const testTlsAlpn01Key = uuid();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pebble CTS required
|
* Pebble CTS required
|
||||||
@@ -128,4 +132,25 @@ describe('verify', () => {
|
|||||||
assert.isTrue(resp);
|
assert.isTrue(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tls-alpn-01
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('tls-alpn-01', () => {
|
||||||
|
it('should reject challenge', async () => {
|
||||||
|
await assert.isRejected(verify['tls-alpn-01'](testTlsAlpn01Authz, testTlsAlpn01Challenge, testTlsAlpn01Key));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mock challenge response', async () => {
|
||||||
|
const resp = await cts.addTlsAlpn01ChallengeResponse(testTlsAlpn01Authz.identifier.value, testTlsAlpn01Key);
|
||||||
|
assert.isTrue(resp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should verify challenge', async () => {
|
||||||
|
const resp = await verify['tls-alpn-01'](testTlsAlpn01Authz, testTlsAlpn01Challenge, testTlsAlpn01Key);
|
||||||
|
assert.isTrue(resp);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ describe('crypto', () => {
|
|||||||
let testSanCsr;
|
let testSanCsr;
|
||||||
let testNonCnCsr;
|
let testNonCnCsr;
|
||||||
let testNonAsciiCsr;
|
let testNonAsciiCsr;
|
||||||
|
let testAlpnCertificate;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,6 +216,31 @@ describe('crypto', () => {
|
|||||||
assert.strictEqual(result.commonName, testCsrDomain);
|
assert.strictEqual(result.commonName, testCsrDomain);
|
||||||
assert.deepStrictEqual(result.altNames, [testCsrDomain]);
|
assert.deepStrictEqual(result.altNames, [testCsrDomain]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALPN
|
||||||
|
*/
|
||||||
|
|
||||||
|
it(`${n}/should generate alpn certificate`, async () => {
|
||||||
|
const authz = { identifier: { value: 'test.example.com' } };
|
||||||
|
const [key, cert] = await crypto.createAlpnCertificate(authz, 'super-secret.12345', await createFn());
|
||||||
|
|
||||||
|
assert.isTrue(Buffer.isBuffer(key));
|
||||||
|
assert.isTrue(Buffer.isBuffer(cert));
|
||||||
|
|
||||||
|
testAlpnCertificate = cert;
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${n}/should not validate invalid alpn certificate key authorization`, () => {
|
||||||
|
assert.isFalse(crypto.isAlpnCertificateAuthorizationValid(testAlpnCertificate, 'aaaaaaa'));
|
||||||
|
assert.isFalse(crypto.isAlpnCertificateAuthorizationValid(testAlpnCertificate, 'bbbbbbb'));
|
||||||
|
assert.isFalse(crypto.isAlpnCertificateAuthorizationValid(testAlpnCertificate, 'ccccccc'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${n}/should validate valid alpn certificate key authorization`, () => {
|
||||||
|
assert.isTrue(crypto.isAlpnCertificateAuthorizationValid(testAlpnCertificate, 'super-secret.12345'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -250,7 +276,7 @@ describe('crypto', () => {
|
|||||||
* CSR with auto-generated key
|
* CSR with auto-generated key
|
||||||
*/
|
*/
|
||||||
|
|
||||||
it('should generate a csr with auto-generated key', async () => {
|
it('should generate a csr with default key', async () => {
|
||||||
const [key, csr] = await crypto.createCsr({
|
const [key, csr] = await crypto.createCsr({
|
||||||
commonName: testCsrDomain
|
commonName: testCsrDomain
|
||||||
});
|
});
|
||||||
@@ -281,6 +307,19 @@ describe('crypto', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALPN
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('should generate alpn certificate with default key', async () => {
|
||||||
|
const authz = { identifier: { value: 'test.example.com' } };
|
||||||
|
const [key, cert] = await crypto.createAlpnCertificate(authz, 'abc123');
|
||||||
|
|
||||||
|
assert.isTrue(Buffer.isBuffer(key));
|
||||||
|
assert.isTrue(Buffer.isBuffer(cert));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PEM utils
|
* PEM utils
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ if (capEabEnabled && process.env.ACME_EAB_KID && process.env.ACME_EAB_HMAC_KEY)
|
|||||||
|
|
||||||
describe('client', () => {
|
describe('client', () => {
|
||||||
const testDomain = `${uuid()}.${domainName}`;
|
const testDomain = `${uuid()}.${domainName}`;
|
||||||
|
const testDomainAlpn = `${uuid()}.${domainName}`;
|
||||||
const testDomainWildcard = `*.${testDomain}`;
|
const testDomainWildcard = `*.${testDomain}`;
|
||||||
const testContact = `mailto:test-${uuid()}@nope.com`;
|
const testContact = `mailto:test-${uuid()}@nope.com`;
|
||||||
|
|
||||||
@@ -78,16 +79,22 @@ describe('client', () => {
|
|||||||
let testAccount;
|
let testAccount;
|
||||||
let testAccountUrl;
|
let testAccountUrl;
|
||||||
let testOrder;
|
let testOrder;
|
||||||
|
let testOrderAlpn;
|
||||||
let testOrderWildcard;
|
let testOrderWildcard;
|
||||||
let testAuthz;
|
let testAuthz;
|
||||||
|
let testAuthzAlpn;
|
||||||
let testAuthzWildcard;
|
let testAuthzWildcard;
|
||||||
let testChallenge;
|
let testChallenge;
|
||||||
|
let testChallengeAlpn;
|
||||||
let testChallengeWildcard;
|
let testChallengeWildcard;
|
||||||
let testKeyAuthorization;
|
let testKeyAuthorization;
|
||||||
|
let testKeyAuthorizationAlpn;
|
||||||
let testKeyAuthorizationWildcard;
|
let testKeyAuthorizationWildcard;
|
||||||
let testCsr;
|
let testCsr;
|
||||||
|
let testCsrAlpn;
|
||||||
let testCsrWildcard;
|
let testCsrWildcard;
|
||||||
let testCertificate;
|
let testCertificate;
|
||||||
|
let testCertificateAlpn;
|
||||||
let testCertificateWildcard;
|
let testCertificateWildcard;
|
||||||
|
|
||||||
|
|
||||||
@@ -107,6 +114,7 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should generate certificate signing request', async () => {
|
it('should generate certificate signing request', async () => {
|
||||||
[, testCsr] = await acme.crypto.createCsr({ commonName: testDomain }, await createKeyFn());
|
[, testCsr] = await acme.crypto.createCsr({ commonName: testDomain }, await createKeyFn());
|
||||||
|
[, testCsrAlpn] = await acme.crypto.createCsr({ commonName: testDomainAlpn }, await createKeyFn());
|
||||||
[, testCsrWildcard] = await acme.crypto.createCsr({ commonName: testDomainWildcard }, await createKeyFn());
|
[, testCsrWildcard] = await acme.crypto.createCsr({ commonName: testDomainWildcard }, await createKeyFn());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -336,12 +344,14 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should create new order', async () => {
|
it('should create new order', async () => {
|
||||||
const data1 = { identifiers: [{ type: 'dns', value: testDomain }] };
|
const data1 = { identifiers: [{ type: 'dns', value: testDomain }] };
|
||||||
const data2 = { identifiers: [{ type: 'dns', value: testDomainWildcard }] };
|
const data2 = { identifiers: [{ type: 'dns', value: testDomainAlpn }] };
|
||||||
|
const data3 = { identifiers: [{ type: 'dns', value: testDomainWildcard }] };
|
||||||
|
|
||||||
testOrder = await testClient.createOrder(data1);
|
testOrder = await testClient.createOrder(data1);
|
||||||
testOrderWildcard = await testClient.createOrder(data2);
|
testOrderAlpn = await testClient.createOrder(data2);
|
||||||
|
testOrderWildcard = await testClient.createOrder(data3);
|
||||||
|
|
||||||
[testOrder, testOrderWildcard].forEach((item) => {
|
[testOrder, testOrderAlpn, testOrderWildcard].forEach((item) => {
|
||||||
spec.rfc8555.order(item);
|
spec.rfc8555.order(item);
|
||||||
assert.strictEqual(item.status, 'pending');
|
assert.strictEqual(item.status, 'pending');
|
||||||
});
|
});
|
||||||
@@ -353,7 +363,7 @@ describe('client', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
it('should get existing order', async () => {
|
it('should get existing order', async () => {
|
||||||
await Promise.all([testOrder, testOrderWildcard].map(async (existing) => {
|
await Promise.all([testOrder, testOrderAlpn, testOrderWildcard].map(async (existing) => {
|
||||||
const result = await testClient.getOrder(existing);
|
const result = await testClient.getOrder(existing);
|
||||||
|
|
||||||
spec.rfc8555.order(result);
|
spec.rfc8555.order(result);
|
||||||
@@ -368,9 +378,10 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should get identifier authorization', async () => {
|
it('should get identifier authorization', async () => {
|
||||||
const orderAuthzCollection = await testClient.getAuthorizations(testOrder);
|
const orderAuthzCollection = await testClient.getAuthorizations(testOrder);
|
||||||
|
const alpnAuthzCollection = await testClient.getAuthorizations(testOrderAlpn);
|
||||||
const wildcardAuthzCollection = await testClient.getAuthorizations(testOrderWildcard);
|
const wildcardAuthzCollection = await testClient.getAuthorizations(testOrderWildcard);
|
||||||
|
|
||||||
[orderAuthzCollection, wildcardAuthzCollection].forEach((collection) => {
|
[orderAuthzCollection, alpnAuthzCollection, wildcardAuthzCollection].forEach((collection) => {
|
||||||
assert.isArray(collection);
|
assert.isArray(collection);
|
||||||
assert.isNotEmpty(collection);
|
assert.isNotEmpty(collection);
|
||||||
|
|
||||||
@@ -381,9 +392,10 @@ describe('client', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testAuthz = orderAuthzCollection.pop();
|
testAuthz = orderAuthzCollection.pop();
|
||||||
|
testAuthzAlpn = alpnAuthzCollection.pop();
|
||||||
testAuthzWildcard = wildcardAuthzCollection.pop();
|
testAuthzWildcard = wildcardAuthzCollection.pop();
|
||||||
|
|
||||||
testAuthz.challenges.concat(testAuthzWildcard.challenges).forEach((item) => {
|
testAuthz.challenges.concat(testAuthzAlpn.challenges).concat(testAuthzWildcard.challenges).forEach((item) => {
|
||||||
spec.rfc8555.challenge(item);
|
spec.rfc8555.challenge(item);
|
||||||
assert.strictEqual(item.status, 'pending');
|
assert.strictEqual(item.status, 'pending');
|
||||||
});
|
});
|
||||||
@@ -396,12 +408,14 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should get challenge key authorization', async () => {
|
it('should get challenge key authorization', async () => {
|
||||||
testChallenge = testAuthz.challenges.find((c) => (c.type === 'http-01'));
|
testChallenge = testAuthz.challenges.find((c) => (c.type === 'http-01'));
|
||||||
|
testChallengeAlpn = testAuthzAlpn.challenges.find((c) => (c.type === 'tls-alpn-01'));
|
||||||
testChallengeWildcard = testAuthzWildcard.challenges.find((c) => (c.type === 'dns-01'));
|
testChallengeWildcard = testAuthzWildcard.challenges.find((c) => (c.type === 'dns-01'));
|
||||||
|
|
||||||
testKeyAuthorization = await testClient.getChallengeKeyAuthorization(testChallenge);
|
testKeyAuthorization = await testClient.getChallengeKeyAuthorization(testChallenge);
|
||||||
|
testKeyAuthorizationAlpn = await testClient.getChallengeKeyAuthorization(testChallengeAlpn);
|
||||||
testKeyAuthorizationWildcard = await testClient.getChallengeKeyAuthorization(testChallengeWildcard);
|
testKeyAuthorizationWildcard = await testClient.getChallengeKeyAuthorization(testChallengeWildcard);
|
||||||
|
|
||||||
[testKeyAuthorization, testKeyAuthorizationWildcard].forEach((k) => assert.isString(k));
|
[testKeyAuthorization, testKeyAuthorizationAlpn, testKeyAuthorizationWildcard].forEach((k) => assert.isString(k));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -438,9 +452,11 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should verify challenge', async () => {
|
it('should verify challenge', async () => {
|
||||||
await cts.assertHttpChallengeCreateFn(testAuthz, testChallenge, testKeyAuthorization);
|
await cts.assertHttpChallengeCreateFn(testAuthz, testChallenge, testKeyAuthorization);
|
||||||
|
await cts.assertTlsAlpnChallengeCreateFn(testAuthzAlpn, testChallengeAlpn, testKeyAuthorizationAlpn);
|
||||||
await cts.assertDnsChallengeCreateFn(testAuthzWildcard, testChallengeWildcard, testKeyAuthorizationWildcard);
|
await cts.assertDnsChallengeCreateFn(testAuthzWildcard, testChallengeWildcard, testKeyAuthorizationWildcard);
|
||||||
|
|
||||||
await testClient.verifyChallenge(testAuthz, testChallenge);
|
await testClient.verifyChallenge(testAuthz, testChallenge);
|
||||||
|
await testClient.verifyChallenge(testAuthzAlpn, testChallengeAlpn);
|
||||||
await testClient.verifyChallenge(testAuthzWildcard, testChallengeWildcard);
|
await testClient.verifyChallenge(testAuthzWildcard, testChallengeWildcard);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -450,7 +466,7 @@ describe('client', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
it('should complete challenge', async () => {
|
it('should complete challenge', async () => {
|
||||||
await Promise.all([testChallenge, testChallengeWildcard].map(async (challenge) => {
|
await Promise.all([testChallenge, testChallengeAlpn, testChallengeWildcard].map(async (challenge) => {
|
||||||
const result = await testClient.completeChallenge(challenge);
|
const result = await testClient.completeChallenge(challenge);
|
||||||
|
|
||||||
spec.rfc8555.challenge(result);
|
spec.rfc8555.challenge(result);
|
||||||
@@ -464,7 +480,7 @@ describe('client', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
it('should wait for valid challenge status', async () => {
|
it('should wait for valid challenge status', async () => {
|
||||||
await Promise.all([testChallenge, testChallengeWildcard].map(async (c) => testClient.waitForValidStatus(c)));
|
await Promise.all([testChallenge, testChallengeAlpn, testChallengeWildcard].map(async (c) => testClient.waitForValidStatus(c)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -474,11 +490,13 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should finalize order', async () => {
|
it('should finalize order', async () => {
|
||||||
const finalize = await testClient.finalizeOrder(testOrder, testCsr);
|
const finalize = await testClient.finalizeOrder(testOrder, testCsr);
|
||||||
|
const finalizeAlpn = await testClient.finalizeOrder(testOrderAlpn, testCsrAlpn);
|
||||||
const finalizeWildcard = await testClient.finalizeOrder(testOrderWildcard, testCsrWildcard);
|
const finalizeWildcard = await testClient.finalizeOrder(testOrderWildcard, testCsrWildcard);
|
||||||
|
|
||||||
[finalize, finalizeWildcard].forEach((f) => spec.rfc8555.order(f));
|
[finalize, finalizeAlpn, finalizeWildcard].forEach((f) => spec.rfc8555.order(f));
|
||||||
|
|
||||||
assert.strictEqual(testOrder.url, finalize.url);
|
assert.strictEqual(testOrder.url, finalize.url);
|
||||||
|
assert.strictEqual(testOrderAlpn.url, finalizeAlpn.url);
|
||||||
assert.strictEqual(testOrderWildcard.url, finalizeWildcard.url);
|
assert.strictEqual(testOrderWildcard.url, finalizeWildcard.url);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -488,7 +506,7 @@ describe('client', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
it('should wait for valid order status', async () => {
|
it('should wait for valid order status', async () => {
|
||||||
await Promise.all([testOrder, testOrderWildcard].map(async (o) => testClient.waitForValidStatus(o)));
|
await Promise.all([testOrder, testOrderAlpn, testOrderWildcard].map(async (o) => testClient.waitForValidStatus(o)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -498,9 +516,10 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should get certificate', async () => {
|
it('should get certificate', async () => {
|
||||||
testCertificate = await testClient.getCertificate(testOrder);
|
testCertificate = await testClient.getCertificate(testOrder);
|
||||||
|
testCertificateAlpn = await testClient.getCertificate(testOrderAlpn);
|
||||||
testCertificateWildcard = await testClient.getCertificate(testOrderWildcard);
|
testCertificateWildcard = await testClient.getCertificate(testOrderWildcard);
|
||||||
|
|
||||||
[testCertificate, testCertificateWildcard].forEach((cert) => {
|
[testCertificate, testCertificateAlpn, testCertificateWildcard].forEach((cert) => {
|
||||||
assert.isString(cert);
|
assert.isString(cert);
|
||||||
acme.crypto.readCertificateInfo(cert);
|
acme.crypto.readCertificateInfo(cert);
|
||||||
});
|
});
|
||||||
@@ -539,11 +558,13 @@ describe('client', () => {
|
|||||||
|
|
||||||
it('should revoke certificate', async () => {
|
it('should revoke certificate', async () => {
|
||||||
await testClient.revokeCertificate(testCertificate);
|
await testClient.revokeCertificate(testCertificate);
|
||||||
|
await testClient.revokeCertificate(testCertificateAlpn, { reason: 0 });
|
||||||
await testClient.revokeCertificate(testCertificateWildcard, { reason: 4 });
|
await testClient.revokeCertificate(testCertificateWildcard, { reason: 4 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow getting revoked certificate', async () => {
|
it('should not allow getting revoked certificate', async () => {
|
||||||
await assert.isRejected(testClient.getCertificate(testOrder));
|
await assert.isRejected(testClient.getCertificate(testOrder));
|
||||||
|
await assert.isRejected(testClient.getCertificate(testOrderAlpn));
|
||||||
await assert.isRejected(testClient.getCertificate(testOrderWildcard));
|
await assert.isRejected(testClient.getCertificate(testOrderWildcard));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ describe('client.auto', () => {
|
|||||||
const testHttpDomain = `${uuid()}.${domainName}`;
|
const testHttpDomain = `${uuid()}.${domainName}`;
|
||||||
const testHttpsDomain = `${uuid()}.${domainName}`;
|
const testHttpsDomain = `${uuid()}.${domainName}`;
|
||||||
const testDnsDomain = `${uuid()}.${domainName}`;
|
const testDnsDomain = `${uuid()}.${domainName}`;
|
||||||
|
const testAlpnDomain = `${uuid()}.${domainName}`;
|
||||||
const testWildcardDomain = `${uuid()}.${domainName}`;
|
const testWildcardDomain = `${uuid()}.${domainName}`;
|
||||||
|
|
||||||
const testSanDomains = [
|
const testSanDomains = [
|
||||||
@@ -280,6 +281,22 @@ describe('client.auto', () => {
|
|||||||
assert.isString(cert);
|
assert.isString(cert);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should order certificate using tls-alpn-01', async () => {
|
||||||
|
const [, csr] = await acme.crypto.createCsr({
|
||||||
|
commonName: testAlpnDomain
|
||||||
|
}, await createKeyFn());
|
||||||
|
|
||||||
|
const cert = await testClient.auto({
|
||||||
|
csr,
|
||||||
|
termsOfServiceAgreed: true,
|
||||||
|
challengeCreateFn: cts.assertTlsAlpnChallengeCreateFn,
|
||||||
|
challengeRemoveFn: cts.challengeRemoveFn,
|
||||||
|
challengePriority: ['tls-alpn-01']
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.isString(cert);
|
||||||
|
});
|
||||||
|
|
||||||
it('should order san certificate', async () => {
|
it('should order san certificate', async () => {
|
||||||
const [, csr] = await acme.crypto.createCsr({
|
const [, csr] = await acme.crypto.createCsr({
|
||||||
commonName: testSanDomains[0],
|
commonName: testSanDomains[0],
|
||||||
|
|||||||
@@ -63,9 +63,14 @@ async function addDns01ChallengeResponse(host, value) {
|
|||||||
return request('set-txt', { host, value });
|
return request('set-txt', { host, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addTlsAlpn01ChallengeResponse(host, content) {
|
||||||
|
return request('add-tlsalpn01', { host, content });
|
||||||
|
}
|
||||||
|
|
||||||
exports.addHttp01ChallengeResponse = addHttp01ChallengeResponse;
|
exports.addHttp01ChallengeResponse = addHttp01ChallengeResponse;
|
||||||
exports.addHttps01ChallengeResponse = addHttps01ChallengeResponse;
|
exports.addHttps01ChallengeResponse = addHttps01ChallengeResponse;
|
||||||
exports.addDns01ChallengeResponse = addDns01ChallengeResponse;
|
exports.addDns01ChallengeResponse = addDns01ChallengeResponse;
|
||||||
|
exports.addTlsAlpn01ChallengeResponse = addTlsAlpn01ChallengeResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,6 +92,11 @@ async function assertDnsChallengeCreateFn(authz, challenge, keyAuthorization) {
|
|||||||
return addDns01ChallengeResponse(`_acme-challenge.${authz.identifier.value}.`, keyAuthorization);
|
return addDns01ChallengeResponse(`_acme-challenge.${authz.identifier.value}.`, keyAuthorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function assertTlsAlpnChallengeCreateFn(authz, challenge, keyAuthorization) {
|
||||||
|
assert.strictEqual(challenge.type, 'tls-alpn-01');
|
||||||
|
return addTlsAlpn01ChallengeResponse(authz.identifier.value, keyAuthorization);
|
||||||
|
}
|
||||||
|
|
||||||
async function challengeCreateFn(authz, challenge, keyAuthorization) {
|
async function challengeCreateFn(authz, challenge, keyAuthorization) {
|
||||||
if (challenge.type === 'http-01') {
|
if (challenge.type === 'http-01') {
|
||||||
return assertHttpChallengeCreateFn(authz, challenge, keyAuthorization);
|
return assertHttpChallengeCreateFn(authz, challenge, keyAuthorization);
|
||||||
@@ -96,6 +106,10 @@ async function challengeCreateFn(authz, challenge, keyAuthorization) {
|
|||||||
return assertDnsChallengeCreateFn(authz, challenge, keyAuthorization);
|
return assertDnsChallengeCreateFn(authz, challenge, keyAuthorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (challenge.type === 'tls-alpn-01') {
|
||||||
|
return assertTlsAlpnChallengeCreateFn(authz, challenge, keyAuthorization);
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`Unsupported challenge type ${challenge.type}`);
|
throw new Error(`Unsupported challenge type ${challenge.type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,4 +120,5 @@ exports.challengeThrowFn = async () => { throw new Error('oops'); };
|
|||||||
exports.assertHttpChallengeCreateFn = assertHttpChallengeCreateFn;
|
exports.assertHttpChallengeCreateFn = assertHttpChallengeCreateFn;
|
||||||
exports.assertHttpsChallengeCreateFn = assertHttpsChallengeCreateFn;
|
exports.assertHttpsChallengeCreateFn = assertHttpsChallengeCreateFn;
|
||||||
exports.assertDnsChallengeCreateFn = assertDnsChallengeCreateFn;
|
exports.assertDnsChallengeCreateFn = assertDnsChallengeCreateFn;
|
||||||
|
exports.assertTlsAlpnChallengeCreateFn = assertTlsAlpnChallengeCreateFn;
|
||||||
exports.challengeCreateFn = challengeCreateFn;
|
exports.challengeCreateFn = challengeCreateFn;
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ if (process.env.ACME_HTTPS_PORT) {
|
|||||||
axios.defaults.acmeSettings.httpsChallengePort = process.env.ACME_HTTPS_PORT;
|
axios.defaults.acmeSettings.httpsChallengePort = process.env.ACME_HTTPS_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.ACME_TLSALPN_PORT) {
|
||||||
|
axios.defaults.acmeSettings.tlsAlpnChallengePort = process.env.ACME_TLSALPN_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* External account binding
|
* External account binding
|
||||||
|
|||||||
+2
@@ -156,6 +156,8 @@ export interface CryptoInterface {
|
|||||||
readCsrDomains(csrPem: CsrBuffer | CsrString): CertificateDomains;
|
readCsrDomains(csrPem: CsrBuffer | CsrString): CertificateDomains;
|
||||||
readCertificateInfo(certPem: CertificateBuffer | CertificateString): CertificateInfo;
|
readCertificateInfo(certPem: CertificateBuffer | CertificateString): CertificateInfo;
|
||||||
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CsrBuffer]>;
|
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CsrBuffer]>;
|
||||||
|
createAlpnCertificate(authz: Authorization, keyAuthorization: string, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CertificateBuffer]>;
|
||||||
|
isAlpnCertificateAuthorizationValid(certPem: CertificateBuffer | CertificateString, keyAuthorization: string): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const crypto: CryptoInterface;
|
export const crypto: CryptoInterface;
|
||||||
|
|||||||
Reference in New Issue
Block a user