Skip to content

Add resilient OCSP certificate revocation checker#99

Open
madislm wants to merge 24 commits intoweb-eid:WE2-1030-use-platform-ocspfrom
madislm:AUT-2514
Open

Add resilient OCSP certificate revocation checker#99
madislm wants to merge 24 commits intoweb-eid:WE2-1030-use-platform-ocspfrom
madislm:AUT-2514

Conversation

@madislm
Copy link
Copy Markdown

@madislm madislm commented Feb 2, 2026

AUT-2514

Signed-off-by: Madis Jaagup Laurson madisjaagup.laurson@nortal.com

@madislm madislm force-pushed the AUT-2514 branch 2 times, most recently from 0e6161a to e927de2 Compare February 2, 2026 09:16
@madislm madislm force-pushed the AUT-2514 branch 5 times, most recently from e96286a to a324e96 Compare February 10, 2026 08:29
@mrts mrts force-pushed the WE2-1030-use-platform-ocsp branch from 10a405b to 74e7af2 Compare February 20, 2026 16:55
@madislm madislm force-pushed the AUT-2514 branch 2 times, most recently from 9cdcc89 to ef4ae35 Compare February 27, 2026 09:35
@mrts mrts force-pushed the WE2-1030-use-platform-ocsp branch from 74e7af2 to f55d636 Compare March 6, 2026 18:37
throw new OCSPCertificateException("Responder certificate from the OCSP response is not equal to " +
"the configured fallback OCSP responder certificate");
}
return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The early return is not wrong, but it would perhaps be clearer to structure pinned and trusted CA validation as explicit branches:

if (trustedResponderCertificate != null) {
    validatePinnedResponderCertificate(responderCertificate);
} else {
    validateResponderCertificateAgainstTrustedCa(responderCertificate, now);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created separate branches and methods.

void validateResponderCertificate(X509CertificateHolder cert, Date now) throws AuthTokenException;

default FallbackOcspService getFallbackService() {
return null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning null from the default getFallbackService() is not ideal as absence of a fallback is part of the contract. Optional would express that perhaps more clearly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now using Optional for that purpose.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you catch OCSPClientException here as well, is the IOException catch still needed?

Copy link
Copy Markdown
Author

@madislm madislm Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added OCSPClientException; IOException is thrown by getCertificateId.

"latest allowed: '" + latestAcceptableTimeSkew + "'", ocspResponderUri);
}
if (thisUpdate.isBefore(minimumValidThisUpdateTime)) {
if (!allowThisUpdateInPast && thisUpdate.isBefore(minimumValidThisUpdateTime)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowThisUpdateInPast = true is used in fallback mode, this means accepting indefinitely stale OCSP responses, which is a security risk.

My preference would be that primary OCSP uses the current short age limit and fallback OCSP uses a configurable separate, much larger maxThisUpdateAge (90 days or whatnot) and there is no boolean bypass.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a separate fallbackMaxOcspResponseThisUpdateAge parameter and removed the flag.

Objects.requireNonNull(certificate, "certificate");
try {
X500Name x500Name = new JcaX509CertificateHolder(certificate).getIssuer();
final RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will throw ArrayIndexOutOfBoundsException on certificates without CN.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not apply after moving to distinguished names.


public class IssuerCommonName {

public static Optional<String> getIssuerCommonName(X509Certificate certificate) {
Copy link
Copy Markdown
Member

@mrts mrts Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using only Common Name seems error-prone, using the full normalized Distinguished Name seems much safer.

For example, in the following (hypothetical) case there would be CN collision, but DN would work:

CN=ESTEID2025, O=AS Sertifitseerimiskeskus, C=EE
CN=ESTEID2025, O=Zetes Estonia OÜ, C=EE

This applies to all locations where CN is used for lookup like OcspServiceProvider, AiaOcspService, FallbackOcspServiceConfiguration and AiaOcspServiceConfiguration.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now using DNs for all lookups that used CNs.


public class OCSPClientException extends RuntimeException {

private byte[] responseBody;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields should be final/immutable.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields are now final.


package eu.webeid.ocsp.exceptions;

public class OCSPClientException extends RuntimeException {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCSPClientException looks more like a checked exception than a RuntimeException. Failures are part of the OcspClient contract and callers are supposed to handle them rather than let them escape accidentally.

Suggested change
public class OCSPClientException extends RuntimeException {
public class OCSPClientException extends Exception {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made it an Exception instead of a RuntimeException.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants