acme: ARI support

ARI is a hint from the cert issuer about when to renew a cert. We will
use it when the API is available.

It provides a mechanim for CAs to revoke certs and signal to clients
that cert should be renewed.

https://www.rfc-editor.org/rfc/rfc9773.txt
https://letsencrypt.org/2024/04/25/guide-to-integrating-ari-into-existing-acme-clients
This commit is contained in:
Girish Ramakrishnan
2026-01-17 22:31:36 +01:00
parent f65b33f3fc
commit 6877dfb772
5 changed files with 122 additions and 13 deletions

View File

@@ -13,7 +13,9 @@ exports = module.exports = {
checkHost,
generateDkimKey,
generateDhparam,
validateCertificate
validateCertificate,
getSerial,
getAuthorityKeyId
};
const assert = require('node:assert'),
@@ -26,14 +28,16 @@ const assert = require('node:assert'),
safe = require('safetydance'),
shell = require('./shell.js')('openssl');
async function generateKey(certName, type) {
debug(`generateKey: generating new key for ${certName} ${type}`);
async function generateKey(type) {
debug(`generateKey: generating new key for${type}`);
if (type === 'rsa4096') {
return await shell.spawn('openssl', ['genrsa', '4096'], { encoding: 'utf8' });
} else if (type === 'secp256r1') {
return await shell.spawn('openssl', ['ecparam', '-genkey', '-name', type], { encoding: 'utf8' });
}
throw new BoxError(BoxError.INTERNAL_ERROR, `Unhandled key type ${type}`);
}
async function getModulus(pem) {
@@ -219,3 +223,20 @@ async function validateCertificate(subdomain, domain, certificate) {
return null;
}
async function getSerial(pem) {
assert.strictEqual(typeof pem, 'string');
const serialOut = await shell.spawn('openssl', ['x509', '-noout', '-serial'], { encoding: 'utf8', input: pem });
return Buffer.from(serialOut.trim().split('=')[1], 'hex'); // serial=xx
}
async function getAuthorityKeyId(pem) {
assert.strictEqual(typeof pem, 'string');
const stdout = await shell.spawn('openssl', ['x509', '-noout', '-text'], { encoding: 'utf8', input: pem });
// if there is multiple AKI, it can have "keyid:" . length is also not fixed to 59
const akiMatch = stdout.match(/Authority Key Identifier[\s\S]*?\n\s*([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2})+)/m);
if (!akiMatch) throw new BoxError(BoxError.OPENSSL_ERROR, 'AKI not found');
return Buffer.from(akiMatch[1].replace(/:/g, ''), 'hex');
}