Files
smartacme/ts/acme/acme.classes.challenge.ts

46 lines
1.5 KiB
TypeScript
Raw Normal View History

import * as crypto from 'node:crypto';
import { AcmeCrypto } from './acme.classes.crypto.js';
import type { AcmeHttpClient } from './acme.classes.http-client.js';
import type { IAcmeChallenge } from './acme.interfaces.js';
/**
* ACME challenge operations - key authorization computation and challenge completion
*/
export class AcmeChallengeManager {
private httpClient: AcmeHttpClient;
private accountKeyPem: string;
constructor(httpClient: AcmeHttpClient, accountKeyPem: string) {
this.httpClient = httpClient;
this.accountKeyPem = accountKeyPem;
}
/**
* Compute the key authorization for a challenge.
* For http-01: returns `token.thumbprint`
* For dns-01: returns `base64url(sha256(token.thumbprint))`
*
* This is a synchronous, pure-crypto computation.
*/
getKeyAuthorization(challenge: IAcmeChallenge): string {
const jwk = AcmeCrypto.getJwk(this.accountKeyPem);
const thumbprint = AcmeCrypto.getJwkThumbprint(jwk);
const keyAuth = `${challenge.token}.${thumbprint}`;
if (challenge.type === 'dns-01') {
// DNS-01 uses base64url(SHA-256(keyAuthorization))
return crypto.createHash('sha256').update(keyAuth).digest().toString('base64url');
}
// HTTP-01 and others use the raw key authorization
return keyAuth;
}
/**
* Notify the ACME server to validate a challenge (POST {} to challenge URL)
*/
async complete(challenge: IAcmeChallenge): Promise<void> {
await this.httpClient.signedRequest(challenge.url, {});
}
}