Skip to content

Commit a93cffd

Browse files
committed
refactor(OCSP): simplify AIA OCSP configuration, use single truststore for subject and OCSP responder certificates, add trusted CA certifcates validation
WE2-432 Signed-off-by: Mart Somermaa <[email protected]>
1 parent c360a3c commit a93cffd

40 files changed

+1060
-476
lines changed

README.md

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,6 @@ import java.security.cert.X509Certificate;
143143
...
144144
```
145145

146-
## 5. Add trusted OCSP responder certificates
147-
148-
- AIA
149-
- Designated
150-
151146
## 5. Configure the authentication token validator
152147

153148
Once the prerequisites have been met, the authentication token validator itself can be configured.
@@ -294,12 +289,13 @@ toTitleCase(CertUtil.getSubjectSurname(userCertificate)); // "Jõeorg"
294289
295290
The following additional configuration options are available in `AuthTokenValidatorBuilder`:
296291
297-
- `withSiteCertificateSha256Fingerprint(String siteCertificateFingerprint)` – turns on origin website certificate fingerprint validation. The validator checks that the site certificate fingerprint from the authentication token matches with the provided site certificate SHA-256 fingerprint. This disables powerful man-in-the-middle attacks where attackers are able to issue falsified certificates for the origin, but also disables TLS proxy usage. Due to the technical limitations of web browsers, certificate fingerprint validation currently works only with Firefox. The provided certificate SHA-256 fingerprint should have the prefix `urn:cert:sha-256:` followed by the hexadecimal encoding of the hash value octets as specified in [URN Namespace for Certificates](https://tools.ietf.org/id/draft-seantek-certspec-01.html). Certificate fingerprint validation is disabled by default.
298-
- `withoutUserCertificateRevocationCheckWithOcsp()` – turns off user certificate revocation check with OCSP. The OCSP URL is extracted from the user certificate AIA extension. OCSP check is enabled by default.
292+
- `withSiteCertificateSha256Fingerprint(String siteCertificateFingerprint)` – turns on origin website certificate fingerprint validation. The validator checks that the site certificate fingerprint from the authentication token matches with the provided site certificate SHA-256 fingerprint. This disables powerful man-in-the-middle attacks where attackers are able to issue falsified certificates for the origin, but also disables TLS proxy usage. Due to the technical limitations of web browsers, certificate fingerprint validation is an experimental feature that currently works only with Firefox. The provided certificate SHA-256 fingerprint should have the prefix `urn:cert:sha-256:` followed by the hexadecimal encoding of the hash value octets as specified in [URN Namespace for Certificates](https://tools.ietf.org/id/draft-seantek-certspec-01.html). Certificate fingerprint validation is disabled by default.
293+
- `withoutUserCertificateRevocationCheckWithOcsp()` – turns off user certificate revocation check with OCSP. OCSP check is enabled by default and the OCSP responder access location URL is extracted from the user certificate AIA extension unless a designated OCSP service is activated.
294+
- `withDesignatedOcspServiceConfiguration(DesignatedOcspServiceConfiguration serviceConfiguration)` – activates the provided designated OCSP responder service configuration for user certificate revocation check with OCSP. The designated service is only used for checking the status of the certificates whose issuers are supported by the service, for other certificates the default AIA extension service access location will be used. See configuration examples in `testutil.OcspServiceMaker.getDesignatedOcspServiceConfiguration()`.
299295
- `withOcspRequestTimeout(Duration ocspRequestTimeout)` – sets both the connection and response timeout of user certificate revocation check OCSP requests. Default is 5 seconds.
300296
- `withAllowedClientClockSkew(Duration allowedClockSkew)` – sets the tolerated clock skew of the client computer when verifying the token expiration. Default value is 3 minutes.
301297
- `withDisallowedCertificatePolicies(ASN1ObjectIdentifier... policies)` – adds the given policies to the list of disallowed user certificate policies. In order for the user certificate to be considered valid, it must not contain any policies present in this list. Contains the Estonian Mobile-ID policies by default as it must not be possible to authenticate with a Mobile-ID certificate when an eID smart card is expected.
302-
- `withNonceDisabledOcspUrls(URI... urls)` – adds the given URLs to the list of OCSP URLs for which the nonce protocol extension will be disabled. Some OCSP services don't support the nonce extension. Contains the ESTEID-2015 OCSP URL by default.
298+
- `withNonceDisabledOcspUrls(URI... urls)` – adds the given URLs to the list of OCSP responder URLs for which the nonce protocol extension will be disabled. Some OCSP responders don't support the nonce extension. Contains the ESTEID-2015 OCSP responder URL by default.
303299

304300
Extended configuration example:
305301

@@ -318,9 +314,9 @@ AuthTokenValidator validator = new AuthTokenValidatorBuilder()
318314

319315
### Certificates' *Authority Information Access* (AIA) extension
320316
321-
It is assumed that the AIA extension that contains the certificates’ OCSP service location, is part of both the user and CA certificates. The AIA OCSP URL will be used to check the certificate revocation status with OCSP.
317+
Unless a designated OCSP responder service is in use, it is required that the AIA extension that contains the certificate’s OCSP responder access location is present in the user certificate. The AIA OCSP URL will be used to check the certificate revocation status with OCSP.
322318
323-
**Note that there may be legal limitations to using AIA URLs during signing** as the services behind these URLs provide different security and SLA guarantees than dedicated OCSP services. For digital signing, OCSP responder certificate validation is additionally needed. Using AIA URLs during authentication is sufficient, however.
319+
**Note that there may be legal limitations to using AIA URLs during signing** as the services behind these URLs provide different security and SLA guarantees than dedicated OCSP responder services. Using AIA URLs during authentication is sufficient, however.
324320
325321
## Possible validation errors
326322
@@ -350,6 +346,7 @@ String nonce = nonceGenerator.generateAndStoreNonce();
350346
The `generateAndStoreNonce()` method both generates the nonce and stores it in the cache.
351347
352348
## Extended configuration
349+
353350
The following additional configuration options are available in `NonceGeneratorBuilder`:
354351
355352
- `withNonceTtl(Duration duration)` – overrides the default nonce time-to-live duration. When the time-to-live passes, the nonce is considered to be expired. Default nonce time-to-live is 5 minutes.
@@ -363,14 +360,3 @@ NonceGenerator generator = new NonceGeneratorBuilder()
363360
.withSecureRandom(customSecureRandom)
364361
.build();
365362
```
366-
367-
## Frequently asked questions
368-
369-
### How can I find the AIA OCSP service URLs?
370-
371-
You can find the AIA OCSP service URLs from the electronic ID certificate profile documents, in the section that describes certificate extensions.
372-
The AIA OCSP extension OID is 1.3.6.1.5.5.7.48.1.
373-
374-
For example, the EstEID AIA URLs are specified in the documents
375-
[*Certificate, CRL and OCSP Profile for identification documents of the Republic of Estonia*](https://www.skidsolutions.eu/upload/files/SK-CPR-ESTEID-EN-v8_4-20200630.pdf) and
376-
[*Certificate, CRL and OCSP Profile for ID-1 Format Identity Documents Issued by the Republic of Estonia*](https://www.skidsolutions.eu/upload/files/SK-CPR-ESTEID2018-EN-v1_2_20200630.pdf).

pom.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<artifactId>authtoken-validation</artifactId>
77
<groupId>org.webeid.security</groupId>
8-
<version>2.0.0</version>
8+
<version>1.2.0</version>
99
<packaging>jar</packaging>
1010
<name>authtoken-validation</name>
1111
<description>Web eID authentication token validation library for Java</description>
@@ -16,10 +16,11 @@
1616
<java.version>1.8</java.version>
1717
<jjwt.version>0.11.2</jjwt.version>
1818
<slf4j.version>1.7.30</slf4j.version>
19-
<bouncycastle.version>1.65</bouncycastle.version>
19+
<bouncycastle.version>1.69</bouncycastle.version>
2020
<caffeine.version>2.8.5</caffeine.version>
2121
<junit-jupiter.version>5.6.2</junit-jupiter.version>
2222
<assertj.version>3.17.2</assertj.version>
23+
<mockito.version>3.12.4</mockito.version>
2324
<jacoco.version>0.8.5</jacoco.version>
2425
<sonar.coverage.jacoco.xmlReportPaths>
2526
${project.basedir}/../jacoco-coverage-report/target/site/jacoco-aggregate/jacoco.xml
@@ -96,6 +97,12 @@
9697
<version>${assertj.version}</version>
9798
<scope>test</scope>
9899
</dependency>
100+
<dependency>
101+
<groupId>org.mockito</groupId>
102+
<artifactId>mockito-core</artifactId>
103+
<version>${mockito.version}</version>
104+
<scope>test</scope>
105+
</dependency>
99106
<dependency>
100107
<groupId>org.slf4j</groupId>
101108
<artifactId>slf4j-simple</artifactId>

src/main/java/org/webeid/security/certificate/CertificateData.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,35 @@
3737
public final class CertificateData {
3838

3939
public static String getSubjectCN(X509Certificate certificate) throws CertificateEncodingException {
40-
return getField(certificate, BCStyle.CN);
40+
return getSubjectField(certificate, BCStyle.CN);
4141
}
4242

4343
public static String getSubjectSurname(X509Certificate certificate) throws CertificateEncodingException {
44-
return getField(certificate, BCStyle.SURNAME);
44+
return getSubjectField(certificate, BCStyle.SURNAME);
4545
}
4646

4747
public static String getSubjectGivenName(X509Certificate certificate) throws CertificateEncodingException {
48-
return getField(certificate, BCStyle.GIVENNAME);
48+
return getSubjectField(certificate, BCStyle.GIVENNAME);
4949
}
5050

5151
public static String getSubjectIdCode(X509Certificate certificate) throws CertificateEncodingException {
52-
return getField(certificate, BCStyle.SERIALNUMBER);
52+
return getSubjectField(certificate, BCStyle.SERIALNUMBER);
5353
}
5454

5555
public static String getSubjectCountryCode(X509Certificate certificate) throws CertificateEncodingException {
56-
return getField(certificate, BCStyle.C);
56+
return getSubjectField(certificate, BCStyle.C);
5757
}
5858

59-
private static String getField(X509Certificate certificate, ASN1ObjectIdentifier fieldId) throws CertificateEncodingException {
60-
final X500Name x500Name = new JcaX509CertificateHolder(certificate).getSubject();
59+
private static String getSubjectField(X509Certificate certificate, ASN1ObjectIdentifier fieldId) throws CertificateEncodingException {
60+
return getField(new JcaX509CertificateHolder(certificate).getSubject(), fieldId);
61+
}
62+
63+
private static String getField(X500Name x500Name, ASN1ObjectIdentifier fieldId) throws CertificateEncodingException {
6164
// Example value: [C=EE, CN=JÕEORG\,JAAK-KRISTJAN\,38001085718, 2.5.4.4=#0c074ac395454f5247, 2.5.4.42=#0c0d4a41414b2d4b524953544a414e, 2.5.4.5=#1311504e4f45452d3338303031303835373138]
6265
final RDN[] rdns = x500Name.getRDNs(fieldId);
66+
if (rdns.length == 0 || rdns[0].getFirst() == null) {
67+
throw new CertificateEncodingException("X500 name RDNs empty or first element is null");
68+
}
6369
return Arrays.stream(rdns)
6470
.map(rdn -> IETFUtils.valueToString(rdn.getFirst().getValue()))
6571
.collect(Collectors.joining(", "));

src/main/java/org/webeid/security/certificate/CertificateValidator.java

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,64 @@
1+
/*
2+
* Copyright (c) 2020-2021 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+
123
package org.webeid.security.certificate;
224

325
import org.webeid.security.exceptions.CertificateNotTrustedException;
426
import org.webeid.security.exceptions.JceException;
5-
import org.webeid.security.exceptions.UserCertificateExpiredException;
6-
import org.webeid.security.exceptions.UserCertificateNotYetValidException;
27+
import org.webeid.security.exceptions.CertificateExpiredException;
28+
import org.webeid.security.exceptions.CertificateNotYetValidException;
729

830
import java.security.GeneralSecurityException;
931
import java.security.InvalidAlgorithmParameterException;
1032
import java.security.NoSuchAlgorithmException;
1133
import java.security.cert.CertPathBuilder;
1234
import java.security.cert.CertPathBuilderException;
1335
import java.security.cert.CertStore;
14-
import java.security.cert.CertificateExpiredException;
15-
import java.security.cert.CertificateNotYetValidException;
1636
import java.security.cert.CollectionCertStoreParameters;
1737
import java.security.cert.PKIXBuilderParameters;
1838
import java.security.cert.PKIXCertPathBuilderResult;
1939
import java.security.cert.TrustAnchor;
2040
import java.security.cert.X509CertSelector;
2141
import java.security.cert.X509Certificate;
2242
import java.util.Collection;
23-
import java.util.Collections;
2443
import java.util.Date;
2544
import java.util.Set;
2645
import java.util.stream.Collectors;
2746

2847
public final class CertificateValidator {
2948

30-
/**
31-
* Checks whether the certificate was valid on the given date.
32-
*/
33-
public static void certificateIsValidOnDate(X509Certificate cert, Date date) throws UserCertificateNotYetValidException, UserCertificateExpiredException {
49+
public static void certificateIsValidOnDate(X509Certificate cert, Date date, String subject) throws CertificateNotYetValidException, CertificateExpiredException {
3450
try {
3551
cert.checkValidity(date);
36-
} catch (CertificateNotYetValidException e) {
37-
throw new UserCertificateNotYetValidException(e);
38-
} catch (CertificateExpiredException e) {
39-
throw new UserCertificateExpiredException(e);
52+
} catch (java.security.cert.CertificateNotYetValidException e) {
53+
throw new CertificateNotYetValidException(subject, e);
54+
} catch (java.security.cert.CertificateExpiredException e) {
55+
throw new CertificateExpiredException(subject, e);
56+
}
57+
}
58+
59+
public static void trustedCACertificatesAreValidOnDate(Set<TrustAnchor> trustedCACertificateAnchors, Date date) throws CertificateNotYetValidException, CertificateExpiredException {
60+
for (TrustAnchor cert : trustedCACertificateAnchors) {
61+
certificateIsValidOnDate(cert.getTrustedCert(), date, "Trusted CA");
4062
}
4163
}
4264

@@ -70,10 +92,6 @@ public static Set<TrustAnchor> buildTrustAnchorsFromCertificates(Collection<X509
7092
.collect(Collectors.toSet());
7193
}
7294

73-
public static Set<TrustAnchor> buildTrustAnchorsFromCertificate(X509Certificate certificate) {
74-
return buildTrustAnchorsFromCertificates(Collections.singleton(certificate));
75-
}
76-
7795
public static CertStore buildCertStoreFromCertificates(Collection<X509Certificate> certificates) throws JceException {
7896
// We use the default JCE provider as there is no reason to use Bouncy Castle, moreover BC requires
7997
// the validated certificate to be in the certificate store which breaks the clean immutable usage of
@@ -85,10 +103,6 @@ public static CertStore buildCertStoreFromCertificates(Collection<X509Certificat
85103
}
86104
}
87105

88-
public static CertStore buildCertStoreFromCertificate(X509Certificate certificate) throws JceException {
89-
return buildCertStoreFromCertificates(Collections.singleton(certificate));
90-
}
91-
92106
private CertificateValidator() {
93107
throw new IllegalStateException("Utility class");
94108
}

src/main/java/org/webeid/security/exceptions/AiaOcspResponderConfigurationException.java

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020-2021 Estonian Information System Authority
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -23,10 +23,10 @@
2323
package org.webeid.security.exceptions;
2424

2525
/**
26-
* Thrown when the user certificate valid from date is in the future.
26+
* Thrown when the certificate's valid until date is in the past.
2727
*/
28-
public class UserCertificateNotYetValidException extends TokenValidationException {
29-
public UserCertificateNotYetValidException(Throwable cause) {
30-
super("User certificate is not yet valid", cause);
28+
public class CertificateExpiredException extends TokenValidationException {
29+
public CertificateExpiredException(String subject, Throwable cause) {
30+
super(subject + " certificate has expired", cause);
3131
}
3232
}

src/main/java/org/webeid/security/exceptions/UserCertificateExpiredException.java renamed to src/main/java/org/webeid/security/exceptions/CertificateNotYetValidException.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020-2021 Estonian Information System Authority
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -23,10 +23,10 @@
2323
package org.webeid.security.exceptions;
2424

2525
/**
26-
* Thrown when the user certificate valid until date is in the past.
26+
* Thrown when the certificate's valid from date is in the future.
2727
*/
28-
public class UserCertificateExpiredException extends TokenValidationException {
29-
public UserCertificateExpiredException(Throwable cause) {
30-
super("User certificate has expired", cause);
28+
public class CertificateNotYetValidException extends TokenValidationException {
29+
public CertificateNotYetValidException(String subject, Throwable cause) {
30+
super(subject + " certificate is not yet valid", cause);
3131
}
3232
}

src/main/java/org/webeid/security/exceptions/OCSPCertificateException.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
/*
2+
* Copyright (c) 2020-2021 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, mergCertificateExpiryValidatore, 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+
123
package org.webeid.security.exceptions;
224

325
public class OCSPCertificateException extends TokenValidationException {

src/main/java/org/webeid/security/exceptions/UserCertificateInvalidPolicyException.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)