mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:51:30 +00:00
feat(M45): ACME certificate profile selection, ARI RFC 9773 renumber, 45-day renewal positioning
Three related ACME ecosystem changes shipped as a single milestone: 1. ACME Certificate Profile Selection: Custom JWS-signed newOrder POST with `profile` field (e.g., `tlsserver`, `shortlived` for 6-day certs) bypassing acme.Client.AuthorizeOrder() since golang.org/x/crypto lacks profile support. ES256 JWS signing with kid mode, nonce management, directory discovery. Empty profile delegates to standard library path (zero behavior change). Configurable via CERTCTL_ACME_PROFILE env var. GUI: profile dropdown on ACME issuer config. 2. ARI RFC 9702 → 9773 Renumber: All 25+ references updated across Go source, docs, README, and examples. Zero remaining occurrences of RFC 9702. 3. 45-Day / Short-Lived Certificate Positioning: 5 domain tests validating renewal thresholds against SC-081v3 validity reduction timeline (200→100→47 days) and Let's Encrypt 45-day/6-day profiles. ARI (RFC 9773) is the expected renewal path for 6-day shortlived certs. New tests: 13 profile + 5 domain threshold + 1 frontend = 19 new tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -691,6 +691,28 @@ describe('API Client', () => {
|
||||
expect(body.config.org_id).toBe('12345');
|
||||
expect(body.config.product_type).toBe('ssl_basic');
|
||||
});
|
||||
|
||||
it('createIssuer sends correct payload for ACME with profile', async () => {
|
||||
mockFetch.mockReturnValueOnce(mockJsonResponse({ id: 'iss-acme-shortlived', name: 'ACME Shortlived' }));
|
||||
const acmePayload = {
|
||||
name: 'ACME Shortlived',
|
||||
type: 'acme',
|
||||
config: {
|
||||
directory_url: 'https://acme-v02.api.letsencrypt.org/directory',
|
||||
email: 'admin@example.com',
|
||||
challenge_type: 'http-01',
|
||||
profile: 'shortlived',
|
||||
},
|
||||
};
|
||||
await createIssuer(acmePayload);
|
||||
const [url, init] = mockFetch.mock.calls[0];
|
||||
expect(url).toBe('/api/v1/issuers');
|
||||
expect(init.method).toBe('POST');
|
||||
const body = JSON.parse(init.body);
|
||||
expect(body.type).toBe('acme');
|
||||
expect(body.config.profile).toBe('shortlived');
|
||||
expect(body.config.directory_url).toBe('https://acme-v02.api.letsencrypt.org/directory');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Audit ──────────────────────────────────────────
|
||||
|
||||
@@ -58,6 +58,7 @@ export const issuerTypes: IssuerTypeConfig[] = [
|
||||
{ key: 'directory_url', label: 'Directory URL', placeholder: 'https://acme-v02.api.letsencrypt.org/directory', required: true },
|
||||
{ key: 'email', label: 'Email', placeholder: 'admin@example.com', required: true },
|
||||
{ key: 'challenge_type', label: 'Challenge Type', type: 'select', options: ['http-01', 'dns-01', 'dns-persist-01'], required: false, defaultValue: 'http-01' },
|
||||
{ key: 'profile', label: 'Certificate Profile', type: 'select', options: ['', 'tlsserver', 'shortlived'], required: false, defaultValue: '' },
|
||||
{ key: 'eab_kid', label: 'EAB Key ID', placeholder: 'External Account Binding Key ID (optional)', required: false },
|
||||
{ key: 'eab_hmac', label: 'EAB HMAC Key', placeholder: 'External Account Binding HMAC key', required: false, type: 'password', sensitive: true },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user