Add resilient OCSP certificate revocation checker#99
Add resilient OCSP certificate revocation checker#99madislm wants to merge 31 commits intoweb-eid:WE2-1030-use-platform-ocspfrom
Conversation
0e6161a to
e927de2
Compare
e96286a to
a324e96
Compare
10a405b to
74e7af2
Compare
9cdcc89 to
ef4ae35
Compare
74e7af2 to
f55d636
Compare
Co-authored-by: Madis Jaagup Laurson <madisjaagup.laurson@nortal.com>
…OCSP certificate revocation checker Co-authored-by: Madis Jaagup Laurson <madisjaagup.laurson@nortal.com>
…n into eu.webeid.ocsp.service
…tificateRevocationChecker
…llows old OCSP responses
…tificateRevocationChecker
| */ | ||
| package eu.webeid.security.validator.revocationcheck; | ||
|
|
||
| import eu.webeid.resilientocsp.ResilientOcspCertificateRevocationChecker; |
There was a problem hiding this comment.
Avoid depending on resilientocsp here, this creates a cyclic dependency between two separate libraries.
| // Users must not be able to modify this value. | ||
| .ignoreExceptions(ResilientUserCertificateRevokedException.class) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
Let's move the helpers from RevocationInfo here:
| } | |
| } | |
| private static RevocationInfo withOcspResponse(RevocationInfo revocationInfo, byte[] ocspResponse) { | |
| return revocationInfo.withAdditionalOcspResponseAttribute(RevocationInfo.KEY_OCSP_RESPONSE, ocspResponse); | |
| } | |
| private static RevocationInfo withHttpStatusCode(RevocationInfo revocationInfo, Integer statusCode) { | |
| return revocationInfo.withAdditionalOcspResponseAttribute(RevocationInfo.KEY_HTTP_STATUS_CODE, statusCode); | |
| } | |
| private static RevocationInfo withCircuitBreakerStatistics(RevocationInfo revocationInfo, CircuitBreakerStatistics circuitBreakerStatistics) { | |
| return revocationInfo.withAdditionalOcspResponseAttribute(RevocationInfo.KEY_CIRCUIT_BREAKER_STATISTICS, circuitBreakerStatistics); | |
| } |
| if (result.isSuccess()) { | ||
| RevocationInfo revocationInfo = result.get(); | ||
| if (revocationInfoList.isEmpty()) { | ||
| revocationInfo = revocationInfo.withCircuitBreakerStatistics(circuitBreakerStatistics); |
There was a problem hiding this comment.
Use the local helper instead:
| revocationInfo = revocationInfo.withCircuitBreakerStatistics(circuitBreakerStatistics); | |
| revocationInfo = withCircuitBreakerStatistics(revocationInfo, circuitBreakerStatistics); |
|
|
||
| public RevocationInfo withCircuitBreakerStatistics(ResilientOcspCertificateRevocationChecker.CircuitBreakerStatistics circuitBreakerStatistics) { | ||
| return withAdditionalField(KEY_CIRCUIT_BREAKER_STATISTICS, circuitBreakerStatistics); | ||
| } | ||
|
|
||
| public RevocationInfo withOcspResponse(byte[] ocspResponse) { | ||
| return withAdditionalField(KEY_OCSP_RESPONSE, ocspResponse); | ||
| } | ||
|
|
||
| public RevocationInfo withHttpStatusCode(Integer statusCode) { | ||
| return withAdditionalField(KEY_HTTP_STATUS_CODE, statusCode); | ||
| } |
There was a problem hiding this comment.
As circuit breaker is not conceptually part of the base library, it is best to move these methods to ResilientOcspCertificateRevocationChecker.
| public RevocationInfo withCircuitBreakerStatistics(ResilientOcspCertificateRevocationChecker.CircuitBreakerStatistics circuitBreakerStatistics) { | |
| return withAdditionalField(KEY_CIRCUIT_BREAKER_STATISTICS, circuitBreakerStatistics); | |
| } | |
| public RevocationInfo withOcspResponse(byte[] ocspResponse) { | |
| return withAdditionalField(KEY_OCSP_RESPONSE, ocspResponse); | |
| } | |
| public RevocationInfo withHttpStatusCode(Integer statusCode) { | |
| return withAdditionalField(KEY_HTTP_STATUS_CODE, statusCode); | |
| } |
| } | ||
|
|
||
| } No newline at end of file | ||
| private RevocationInfo withAdditionalField(String key, Object value) { |
| if (value == null) { | ||
| return this; | ||
| } | ||
| Map<String, Object> newOcspResponseAttributes = new HashMap<>(ocspResponseAttributes); |
There was a problem hiding this comment.
ocspResponseAttributes can be null, so null check is needed here:
| Map<String, Object> newOcspResponseAttributes = new HashMap<>(ocspResponseAttributes); | |
| Map<String, Object> newOcspResponseAttributes = ocspResponseAttributes != null | |
| ? new HashMap<>(ocspResponseAttributes) | |
| : new HashMap<>(); |
new HashMap<>(null) throws NullPointerException.
| CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(primaryService.getAccessLocation().toASCIIString()); | ||
|
|
||
| Optional<FallbackOcspService> firstFallbackServiceOpt = primaryService.getFallbackService(); | ||
| if (firstFallbackServiceOpt.isEmpty()) { |
There was a problem hiding this comment.
I think a comment here is needed to clarify that without a configured fallback the primary service is used directly:
| if (firstFallbackServiceOpt.isEmpty()) { | |
| if (firstFallbackServiceOpt.isEmpty()) { | |
| // Without a configured fallback, use the primary service directly without retry or circuit-breaker. |
As this is may be non-obvious, maybe add this to the class-level Javadoc as well:
/**
* OCSP revocation checker that falls back to configured fallback OCSP responders when the primary OCSP service fails.
*
* <p>Retry and circuit-breaker handling are applied only when a fallback OCSP service is configured for the
* certificate issuer. If no fallback is configured, validation is handled by the primary OCSP service directly.
*/
AUT-2514
Signed-off-by: Madis Jaagup Laurson madisjaagup.laurson@nortal.com