Skip to content

Commit 052ee5a

Browse files
committed
AUT-2597 Use issuer CNs to get fallback OCSP services
1 parent 7ad1038 commit 052ee5a

File tree

11 files changed

+105
-45
lines changed

11 files changed

+105
-45
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2020-2025 Estonian Information System Authority
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
package eu.webeid.ocsp.protocol;
24+
25+
import org.bouncycastle.asn1.x500.RDN;
26+
import org.bouncycastle.asn1.x500.X500Name;
27+
import org.bouncycastle.asn1.x500.style.BCStyle;
28+
import org.bouncycastle.asn1.x500.style.IETFUtils;
29+
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
30+
31+
import java.security.cert.CertificateEncodingException;
32+
import java.security.cert.X509Certificate;
33+
import java.util.Objects;
34+
import java.util.Optional;
35+
36+
public class IssuerCommonName {
37+
38+
public static Optional<String> getIssuerCommonName(X509Certificate certificate) {
39+
Objects.requireNonNull(certificate, "certificate");
40+
try {
41+
X500Name x500Name = new JcaX509CertificateHolder(certificate).getIssuer();
42+
final RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
43+
return Optional.of(IETFUtils.valueToString(cn.getFirst().getValue()));
44+
} catch (CertificateEncodingException e) {
45+
return Optional.empty();
46+
}
47+
}
48+
49+
private IssuerCommonName() {
50+
throw new IllegalStateException("Utility class");
51+
}
52+
}

src/main/java/eu/webeid/ocsp/service/AiaOcspService.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.Objects;
4242
import java.util.Set;
4343

44+
import static eu.webeid.ocsp.protocol.IssuerCommonName.getIssuerCommonName;
4445
import static eu.webeid.ocsp.protocol.OcspUrl.getOcspUri;
4546

4647
/**
@@ -60,8 +61,10 @@ public AiaOcspService(AiaOcspServiceConfiguration configuration, X509Certificate
6061
this.trustedCACertificateAnchors = configuration.getTrustedCACertificateAnchors();
6162
this.trustedCACertificateCertStore = configuration.getTrustedCACertificateCertStore();
6263
this.url = getOcspAiaUrlFromCertificate(Objects.requireNonNull(certificate));
63-
this.supportsNonce = !configuration.getNonceDisabledOcspUrls().contains(this.url);
6464
this.fallbackOcspService = fallbackOcspService;
65+
String issuerCN = getIssuerCommonName(certificate).orElseThrow(() ->
66+
new UserCertificateOCSPCheckFailedException("Getting the issuer common name failed"));
67+
this.supportsNonce = !configuration.getNonceDisabledIssuerCNs().contains(issuerCN);
6568
}
6669

6770
@Override

src/main/java/eu/webeid/ocsp/service/AiaOcspServiceConfiguration.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
package eu.webeid.ocsp.service;
2424

25-
import java.net.URI;
2625
import java.security.cert.CertStore;
2726
import java.security.cert.TrustAnchor;
2827
import java.util.Collection;
@@ -31,18 +30,18 @@
3130

3231
public class AiaOcspServiceConfiguration {
3332

34-
private final Collection<URI> nonceDisabledOcspUrls;
33+
private final Collection<String> nonceDisabledIssuerCNs;
3534
private final Set<TrustAnchor> trustedCACertificateAnchors;
3635
private final CertStore trustedCACertificateCertStore;
3736

38-
public AiaOcspServiceConfiguration(Collection<URI> nonceDisabledOcspUrls, Set<TrustAnchor> trustedCACertificateAnchors, CertStore trustedCACertificateCertStore) {
39-
this.nonceDisabledOcspUrls = Objects.requireNonNull(nonceDisabledOcspUrls);
37+
public AiaOcspServiceConfiguration(Collection<String> nonceDisabledIssuerCNs, Set<TrustAnchor> trustedCACertificateAnchors, CertStore trustedCACertificateCertStore) {
38+
this.nonceDisabledIssuerCNs = Objects.requireNonNull(nonceDisabledIssuerCNs);
4039
this.trustedCACertificateAnchors = Objects.requireNonNull(trustedCACertificateAnchors);
4140
this.trustedCACertificateCertStore = Objects.requireNonNull(trustedCACertificateCertStore);
4241
}
4342

44-
public Collection<URI> getNonceDisabledOcspUrls() {
45-
return nonceDisabledOcspUrls;
43+
public Collection<String> getNonceDisabledIssuerCNs() {
44+
return nonceDisabledIssuerCNs;
4645
}
4746

4847
public Set<TrustAnchor> getTrustedCACertificateAnchors() {

src/main/java/eu/webeid/ocsp/service/OcspService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package eu.webeid.ocsp.service;
2424

25+
import eu.webeid.resilientocsp.service.FallbackOcspService;
2526
import org.bouncycastle.cert.X509CertificateHolder;
2627
import eu.webeid.security.exceptions.AuthTokenException;
2728

@@ -36,7 +37,7 @@ public interface OcspService {
3637

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

39-
default OcspService getFallbackService() {
40+
default FallbackOcspService getFallbackService() {
4041
return null;
4142
}
4243

src/main/java/eu/webeid/ocsp/service/OcspServiceProvider.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,20 @@
2727
import eu.webeid.resilientocsp.service.FallbackOcspServiceConfiguration;
2828
import eu.webeid.security.exceptions.AuthTokenException;
2929

30-
import java.net.URI;
3130
import java.security.cert.CertificateEncodingException;
3231
import java.security.cert.X509Certificate;
3332
import java.util.Collection;
33+
import java.util.HashMap;
3434
import java.util.Map;
3535
import java.util.Objects;
36-
import java.util.stream.Collectors;
3736

38-
import static eu.webeid.ocsp.protocol.OcspUrl.getOcspUri;
37+
import static eu.webeid.ocsp.protocol.IssuerCommonName.getIssuerCommonName;
3938

4039
public class OcspServiceProvider {
4140

4241
private final DesignatedOcspService designatedOcspService;
4342
private final AiaOcspServiceConfiguration aiaOcspServiceConfiguration;
44-
private final Map<URI, FallbackOcspService> fallbackOcspServiceMap;
43+
private final Map<String, FallbackOcspService> fallbackOcspServiceMap = new HashMap<>();
4544

4645
public OcspServiceProvider(DesignatedOcspServiceConfiguration designatedOcspServiceConfiguration, AiaOcspServiceConfiguration aiaOcspServiceConfiguration) {
4746
this(designatedOcspServiceConfiguration, aiaOcspServiceConfiguration, null);
@@ -52,9 +51,13 @@ public OcspServiceProvider(DesignatedOcspServiceConfiguration designatedOcspServ
5251
new DesignatedOcspService(designatedOcspServiceConfiguration)
5352
: null;
5453
this.aiaOcspServiceConfiguration = Objects.requireNonNull(aiaOcspServiceConfiguration, "aiaOcspServiceConfiguration");
55-
this.fallbackOcspServiceMap = fallbackOcspServiceConfigurations != null ? fallbackOcspServiceConfigurations.stream()
56-
.collect(Collectors.toMap(FallbackOcspServiceConfiguration::getOcspServiceAccessLocation, FallbackOcspService::new))
57-
: Map.of();
54+
if (fallbackOcspServiceConfigurations != null) {
55+
for (FallbackOcspServiceConfiguration configuration : fallbackOcspServiceConfigurations) {
56+
String issuerCN = getIssuerCommonName(configuration.getResponderCertificate()).orElseThrow(() ->
57+
new RuntimeException("Certificate does not contain issuer CN"));
58+
fallbackOcspServiceMap.put(issuerCN, new FallbackOcspService(configuration));
59+
}
60+
}
5861
}
5962

6063
/**
@@ -63,20 +66,16 @@ public OcspServiceProvider(DesignatedOcspServiceConfiguration designatedOcspServ
6366
*
6467
* @param certificate subject certificate that is to be checked with OCSP
6568
* @return either the designated or AIA OCSP service instance
66-
* @throws AuthTokenException when AIA URL is not found in certificate
69+
* @throws UserCertificateOCSPCheckFailedException when issuer common name is not found in certificate
6770
* @throws IllegalArgumentException when certificate is invalid
6871
*/
6972
public OcspService getService(X509Certificate certificate) throws AuthTokenException, CertificateEncodingException {
7073
if (designatedOcspService != null && designatedOcspService.supportsIssuerOf(certificate)) {
7174
return designatedOcspService;
7275
}
73-
URI ocspServiceUri = getOcspUri(certificate).orElseThrow(() ->
74-
new UserCertificateOCSPCheckFailedException("Getting the AIA OCSP responder field from the certificate failed"));
75-
FallbackOcspService fallbackOcspService = fallbackOcspServiceMap.get(ocspServiceUri);
76+
String issuerCommonName = getIssuerCommonName(certificate).orElseThrow(() ->
77+
new UserCertificateOCSPCheckFailedException("Getting the issuer common name failed"));
78+
FallbackOcspService fallbackOcspService = fallbackOcspServiceMap.get(issuerCommonName);
7679
return new AiaOcspService(aiaOcspServiceConfiguration, certificate, fallbackOcspService);
7780
}
78-
79-
public FallbackOcspService getFallbackService(URI ocspServiceUri) {
80-
return fallbackOcspServiceMap.get(ocspServiceUri);
81-
}
8281
}

src/main/java/eu/webeid/resilientocsp/ResilientOcspCertificateRevocationChecker.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import eu.webeid.ocsp.service.OcspServiceProvider;
3232
import eu.webeid.resilientocsp.exceptions.ResilientUserCertificateOCSPCheckFailedException;
3333
import eu.webeid.resilientocsp.exceptions.ResilientUserCertificateRevokedException;
34+
import eu.webeid.resilientocsp.service.FallbackOcspService;
3435
import eu.webeid.security.exceptions.AuthTokenException;
3536
import eu.webeid.security.validator.ValidationInfo;
3637
import eu.webeid.security.validator.revocationcheck.RevocationInfo;
@@ -106,8 +107,8 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
106107
} catch (CertificateException e) {
107108
throw new ResilientUserCertificateOCSPCheckFailedException(new ValidationInfo(subjectCertificate, List.of()));
108109
}
109-
final OcspService fallbackOcspService = ocspService.getFallbackService();
110-
if (fallbackOcspService == null) {
110+
final FallbackOcspService firstFallbackService = ocspService.getFallbackService();
111+
if (firstFallbackService == null) {
111112
return List.of(request(ocspService, subjectCertificate, issuerCertificate, false));
112113
}
113114

@@ -123,7 +124,6 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
123124
throw e;
124125
}
125126
};
126-
OcspService firstFallbackService = ocspService.getFallbackService();
127127
CheckedSupplier<RevocationInfo> firstFallbackSupplier = () -> {
128128
try {
129129
return request(firstFallbackService, subjectCertificate, issuerCertificate, true);
@@ -132,7 +132,7 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
132132
throw e;
133133
}
134134
};
135-
OcspService secondFallbackService = getOcspServiceProvider().getFallbackService(firstFallbackService.getAccessLocation());
135+
OcspService secondFallbackService = firstFallbackService.getNextFallback();
136136
CheckedSupplier<RevocationInfo> fallbackSupplier;
137137
if (secondFallbackService == null) {
138138
fallbackSupplier = firstFallbackSupplier;

src/main/java/eu/webeid/resilientocsp/service/FallbackOcspService.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ public class FallbackOcspService implements OcspService {
4242
private final URI url;
4343
private final boolean supportsNonce;
4444
private final X509Certificate trustedResponderCertificate;
45+
private final FallbackOcspService nextFallback;
4546

4647
public FallbackOcspService(FallbackOcspServiceConfiguration configuration) {
47-
this.url = configuration.getFallbackOcspServiceAccessLocation();
48+
this.url = configuration.getAccessLocation();
4849
this.supportsNonce = configuration.doesSupportNonce();
4950
this.trustedResponderCertificate = configuration.getResponderCertificate();
51+
this.nextFallback = configuration.getNextFallback();
5052
}
5153

5254
@Override
@@ -74,4 +76,8 @@ public void validateResponderCertificate(X509CertificateHolder cert, Date now) t
7476
throw new OCSPCertificateException("X509CertificateHolder conversion to X509Certificate failed", e);
7577
}
7678
}
79+
80+
public FallbackOcspService getNextFallback() {
81+
return nextFallback;
82+
}
7783
}

src/main/java/eu/webeid/resilientocsp/service/FallbackOcspServiceConfiguration.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,23 @@
3131

3232
public class FallbackOcspServiceConfiguration {
3333

34-
private final URI ocspServiceAccessLocation;
35-
private final URI fallbackOcspServiceAccessLocation;
34+
private final URI accessLocation;
3635
private final X509Certificate responderCertificate;
3736
private final boolean doesSupportNonce;
37+
private final FallbackOcspService nextFallback;
3838

39-
public FallbackOcspServiceConfiguration(URI ocspServiceAccessLocation, URI fallbackOcspServiceAccessLocation,
40-
X509Certificate responderCertificate, boolean doesSupportNonce) throws OCSPCertificateException {
41-
this.ocspServiceAccessLocation = Objects.requireNonNull(ocspServiceAccessLocation, "Primary OCSP service access location");
42-
this.fallbackOcspServiceAccessLocation = Objects.requireNonNull(fallbackOcspServiceAccessLocation, "Fallback OCSP service access location");
39+
public FallbackOcspServiceConfiguration(URI accessLocation, X509Certificate responderCertificate,
40+
boolean doesSupportNonce,
41+
FallbackOcspService nextFallback) throws OCSPCertificateException {
42+
this.accessLocation = Objects.requireNonNull(accessLocation, "Fallback OCSP service access location");
4343
this.responderCertificate = Objects.requireNonNull(responderCertificate, "Fallback OCSP responder certificate");
4444
OcspResponseValidator.validateHasSigningExtension(responderCertificate);
4545
this.doesSupportNonce = doesSupportNonce;
46+
this.nextFallback = nextFallback;
4647
}
4748

48-
public URI getOcspServiceAccessLocation() {
49-
return ocspServiceAccessLocation;
50-
}
51-
52-
public URI getFallbackOcspServiceAccessLocation() {
53-
return fallbackOcspServiceAccessLocation;
49+
public URI getAccessLocation() {
50+
return accessLocation;
5451
}
5552

5653
public X509Certificate getResponderCertificate() {
@@ -61,4 +58,7 @@ public boolean doesSupportNonce() {
6158
return doesSupportNonce;
6259
}
6360

61+
public FallbackOcspService getNextFallback() {
62+
return nextFallback;
63+
}
6464
}

src/test/java/eu/webeid/ocsp/service/OcspServiceMaker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class OcspServiceMaker {
4141

4242
private static final String TEST_OCSP_ACCESS_LOCATION = "http://demo.sk.ee/ocsp";
4343
private static final List<X509Certificate> TRUSTED_CA_CERTIFICATES;
44-
private static final URI TEST_ESTEID_2015 = URI.create("http://aia.demo.sk.ee/esteid2015");
44+
private static final String ISSUER_CN = "TEST of ESTEID-SK 2015";
4545

4646
static {
4747
try {
@@ -69,7 +69,7 @@ public static OcspServiceProvider getDesignatedOcspServiceProvider(String ocspSe
6969

7070
private static AiaOcspServiceConfiguration getAiaOcspServiceConfiguration() throws JceException {
7171
return new AiaOcspServiceConfiguration(
72-
Set.of(TEST_ESTEID_2015),
72+
Set.of(ISSUER_CN),
7373
CertificateValidator.buildTrustAnchorsFromCertificates(TRUSTED_CA_CERTIFICATES),
7474
CertificateValidator.buildCertStoreFromCertificates(TRUSTED_CA_CERTIFICATES));
7575
}

src/test/java/eu/webeid/ocsp/service/OcspServiceProviderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,4 @@ void whenAiaOcspServiceConfigurationDoesNotHaveResponderCertTrustedCA_thenThrows
100100
// assertThatThrownBy(() -> validatorWithOcspCheck
101101
// .validate(token, VALID_CHALLENGE_NONCE))
102102
// .isInstanceOf(UserCertificateRevokedException.class);
103-
// }
103+
// }

0 commit comments

Comments
 (0)