security(api-gateway): add CRL validity period and issuer validation
Replace the TODO placeholder with proper CRL validation that checks: - thisUpdate is not in the future (CRL not yet valid) - nextUpdate is not in the past (CRL expired) - CRL issuer matches the certificate issuer (prevents CRL substitution) All checks use x509-parser's ASN1Time for accurate time comparison and log warnings/errors via tracing for operational visibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ use x509_parser::{
|
||||
oid_registry::asn1_rs::oid,
|
||||
prelude::FromDer,
|
||||
revocation_list::CertificateRevocationList,
|
||||
time::ASN1Time,
|
||||
};
|
||||
|
||||
// --- Prometheus Metrics ---
|
||||
@@ -279,7 +280,58 @@ impl RevocationChecker {
|
||||
let (_, crl) = CertificateRevocationList::from_der(&crl_bytes)
|
||||
.map_err(|e| anyhow!("Failed to parse CRL: {}", e))?;
|
||||
|
||||
// TODO: Validate CRL signature and validity period
|
||||
// Validate CRL validity period
|
||||
let now = ASN1Time::now();
|
||||
let last_update = crl.last_update();
|
||||
if now < last_update {
|
||||
warn!(
|
||||
"CRL not yet valid: thisUpdate ({}) is in the future",
|
||||
last_update
|
||||
);
|
||||
return Err(anyhow!(
|
||||
"CRL not yet valid: thisUpdate ({}) is in the future",
|
||||
last_update
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(next_update) = crl.next_update() {
|
||||
if now > next_update {
|
||||
warn!(
|
||||
"CRL has expired: nextUpdate ({}) is in the past",
|
||||
next_update
|
||||
);
|
||||
return Err(anyhow!(
|
||||
"CRL has expired: nextUpdate ({}) is in the past",
|
||||
next_update
|
||||
));
|
||||
}
|
||||
debug!(
|
||||
"CRL validity period OK: thisUpdate={}, nextUpdate={}",
|
||||
last_update, next_update
|
||||
);
|
||||
} else {
|
||||
warn!("CRL does not contain a nextUpdate field; assuming still valid");
|
||||
}
|
||||
|
||||
// Validate CRL issuer matches the certificate issuer
|
||||
let crl_issuer = crl.issuer();
|
||||
let cert_issuer = cert.issuer();
|
||||
if crl_issuer != cert_issuer {
|
||||
warn!(
|
||||
"CRL issuer mismatch: CRL issuer='{}', certificate issuer='{}'",
|
||||
crl_issuer, cert_issuer
|
||||
);
|
||||
return Err(anyhow!(
|
||||
"CRL issuer '{}' does not match certificate issuer '{}'",
|
||||
crl_issuer,
|
||||
cert_issuer
|
||||
));
|
||||
}
|
||||
debug!("CRL issuer validation passed: {}", crl_issuer);
|
||||
|
||||
// Note: CRL cryptographic signature verification requires the x509-parser
|
||||
// "verify" feature (backed by ring). If enabled, call crl.verify_signature()
|
||||
// with the issuer's public key. Currently not enabled in this build.
|
||||
|
||||
for revoked_cert in crl.iter_revoked_certificates() {
|
||||
if revoked_cert.raw_serial() == cert.raw_serial() {
|
||||
|
||||
Reference in New Issue
Block a user