Skip to content

Commit d7f291c

Browse files
committed
feat(OCSP): make collections immutable in AuthtokeValidationConfiguration.copy() and CertificateValidator.buildTrustAnchorsFromCertificates(), reimplement AIA OCSP access location retriving from certificate with higher level AuthorityInformationAccess API
1 parent bb09f83 commit d7f291c

File tree

4 files changed

+45
-87
lines changed

4 files changed

+45
-87
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ import org.webeid.security.nonce.NonceGeneratorBuilder;
128128

129129
## 4. Add trusted certificate authority certificates
130130

131-
You must explicitly specify which **intermediate** certificate authorities (CAs) are trusted to issue the eID authentication certificates. CA certificates can be loaded from either the truststore file, resources or any stream source. We use the [`CertificateLoader`](https://github.com/web-eid/web-eid-authtoken-validation-java/blob/main/src/test/java/org/webeid/security/testutil/CertificateLoader.java) helper class from [`testutil`](https://github.com/web-eid/web-eid-authtoken-validation-java/tree/main/src/test/java/org/webeid/security/testutil) to load CA certificates from resources here, but consider using [the truststore file](https://github.com/web-eid/web-eid-spring-boot-example/blob/main/src/main/java/org/webeid/example/config/ValidationConfiguration.java#L104-L122) instead.
131+
You must explicitly specify which **intermediate** certificate authorities (CAs) are trusted to issue the eID authentication and OCSP responder certificates. CA certificates can be loaded from either the truststore file, resources or any stream source. We use the [`CertificateLoader`](https://github.com/web-eid/web-eid-authtoken-validation-java/blob/main/src/test/java/org/webeid/security/testutil/CertificateLoader.java) helper class from [`testutil`](https://github.com/web-eid/web-eid-authtoken-validation-java/tree/main/src/test/java/org/webeid/security/testutil) to load CA certificates from resources here, but consider using [the truststore file](https://github.com/web-eid/web-eid-spring-boot-example/blob/main/src/main/java/org/webeid/example/config/ValidationConfiguration.java#L104-L122) instead.
132132

133133
First, copy the trusted certificates, for example `ESTEID-SK_2015.cer` and `ESTEID2018.cer`, to `resources/cacerts/`, then load the certificates as follows:
134134

@@ -260,7 +260,7 @@ As described in section *[5. Configure the authentication token validator](#5-co
260260
261261
The **nonce cache** instance is used to look up nonce expiry time using its unique value as key. The values in the cache are populated by the nonce generator as described in section *[Nonce generation](#nonce-generation)* below. Consider using [Caffeine](https://github.com/ben-manes/caffeine) or [Ehcache](https://www.ehcache.org/) as the caching provider if your application does not run in a cluster, or [Hazelcast](https://hazelcast.com/), [Infinispan](https://infinispan.org/) or non-Java distributed cahces like [Memcached](https://memcached.org/) or [Redis](https://redis.io/) if it does. Cache configuration is described in more detail in section *[2. Add cache support](#2-add-cache-support)*.
262262
263-
The **trusted certificate authority certificates** are used to validate that the user certificate from the authentication token is signed by a trusted certificate authority. Intermediate CA certificates must be used instead of the root CA certificates so that revoked CA certificates can be detected. Trusted certificate authority certificates configuration is described in more detail in section *[4. Add trusted certificate authority certificates](#4-add-trusted-certificate-authority-certificates)*.
263+
The **trusted certificate authority certificates** are used to validate that the user certificate from the authentication token and the OCSP responder certificate is signed by a trusted certificate authority. Intermediate CA certificates must be used instead of the root CA certificates so that revoked CA certificates can be removed. Trusted certificate authority certificates configuration is described in more detail in section *[4. Add trusted certificate authority certificates](#4-add-trusted-certificate-authority-certificates)*.
264264
265265
The authentication token validator configuration and construction is described in more detail in section *[5. Configure the authentication token validator](#5-configure-the-authentication-token-validator)*. Once the validator object has been constructed, it can be used for validating authentication tokens as follows:
266266
@@ -287,13 +287,13 @@ toTitleCase(CertUtil.getSubjectSurname(userCertificate)); // "Jõeorg"
287287
288288
The following additional configuration options are available in `AuthTokenValidatorBuilder`:
289289
290-
- `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.
291290
- `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.
292291
- `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()`.
293292
- `withOcspRequestTimeout(Duration ocspRequestTimeout)` – sets both the connection and response timeout of user certificate revocation check OCSP requests. Default is 5 seconds.
294293
- `withAllowedClientClockSkew(Duration allowedClockSkew)` – sets the tolerated clock skew of the client computer when verifying the token expiration. Default value is 3 minutes.
295294
- `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.
296295
- `withNonceDisabledOcspUrls(URI... urls)` – adds the given URLs to the list of OCSP responder access location 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.
296+
- `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.
297297

298298
Extended configuration example:
299299

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.security.cert.X509CertSelector;
4141
import java.security.cert.X509Certificate;
4242
import java.util.Collection;
43+
import java.util.Collections;
4344
import java.util.Date;
4445
import java.util.Set;
4546
import java.util.stream.Collectors;
@@ -87,9 +88,9 @@ public static X509Certificate validateIsSignedByTrustedCA(X509Certificate certif
8788
}
8889

8990
public static Set<TrustAnchor> buildTrustAnchorsFromCertificates(Collection<X509Certificate> certificates) {
90-
return certificates.stream()
91+
return Collections.unmodifiableSet(certificates.stream()
9192
.map(cert -> new TrustAnchor(cert, null))
92-
.collect(Collectors.toSet());
93+
.collect(Collectors.toSet()));
9394
}
9495

9596
public static CertStore buildCertStoreFromCertificates(Collection<X509Certificate> certificates) throws JceException {

src/main/java/org/webeid/security/validator/AuthTokenValidationConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.time.Duration;
3434
import java.time.ZonedDateTime;
3535
import java.util.Collection;
36+
import java.util.Collections;
3637
import java.util.HashSet;
3738
import java.util.Objects;
3839

@@ -70,15 +71,15 @@ public final class AuthTokenValidationConfiguration {
7071
private AuthTokenValidationConfiguration(AuthTokenValidationConfiguration other) {
7172
this.siteOrigin = other.siteOrigin;
7273
this.nonceCache = other.nonceCache;
73-
this.trustedCACertificates = new HashSet<>(other.trustedCACertificates);
74+
this.trustedCACertificates = Collections.unmodifiableSet(new HashSet<>(other.trustedCACertificates));
7475
this.isUserCertificateRevocationCheckWithOcspEnabled = other.isUserCertificateRevocationCheckWithOcspEnabled;
7576
this.ocspRequestTimeout = other.ocspRequestTimeout;
7677
this.allowedClientClockSkew = other.allowedClientClockSkew;
7778
this.designatedOcspServiceConfiguration = other.designatedOcspServiceConfiguration;
7879
this.isSiteCertificateFingerprintValidationEnabled = other.isSiteCertificateFingerprintValidationEnabled;
7980
this.siteCertificateSha256Fingerprint = other.siteCertificateSha256Fingerprint;
80-
this.disallowedSubjectCertificatePolicies = new HashSet<>(other.disallowedSubjectCertificatePolicies);
81-
this.nonceDisabledOcspUrls = new HashSet<>(other.nonceDisabledOcspUrls);
81+
this.disallowedSubjectCertificatePolicies = Collections.unmodifiableSet(new HashSet<>(other.disallowedSubjectCertificatePolicies));
82+
this.nonceDisabledOcspUrls = Collections.unmodifiableSet(new HashSet<>(other.nonceDisabledOcspUrls));
8283
}
8384

8485
void setSiteOrigin(URI siteOrigin) {

src/main/java/org/webeid/security/validator/ocsp/OcspUrl.java

Lines changed: 35 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,65 @@
11
/*
2-
* Copyright (c) 2017 The Netty Project
32
* Copyright (c) 2020-2021 Estonian Information System Authority
43
*
5-
* The Netty Project and The Web eID Project license this file to you under the
6-
* Apache License, version 2.0 (the "License"); you may not use this file except
7-
* in compliance with the License. You may obtain a copy of the License at:
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:
810
*
9-
* http://www.apache.org/licenses/LICENSE-2.0
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
1013
*
11-
* Unless required by applicable law or agreed to in writing, software
12-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14-
* License for the specific language governing permissions and limitations under
15-
* the License.
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.
1621
*/
1722

1823
package org.webeid.security.validator.ocsp;
1924

20-
import org.bouncycastle.asn1.ASN1Encodable;
21-
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
22-
import org.bouncycastle.asn1.ASN1Primitive;
23-
import org.bouncycastle.asn1.BERTags;
24-
import org.bouncycastle.asn1.DLSequence;
25-
import org.bouncycastle.asn1.DLTaggedObject;
26-
import org.bouncycastle.asn1.x509.Extension;
27-
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
25+
import org.bouncycastle.asn1.ASN1String;
26+
import org.bouncycastle.asn1.x509.AccessDescription;
27+
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
28+
import org.bouncycastle.asn1.x509.GeneralName;
29+
import org.bouncycastle.cert.X509CertificateHolder;
2830

2931
import java.io.IOException;
3032
import java.net.URI;
31-
import java.nio.charset.StandardCharsets;
33+
import java.security.cert.CertificateEncodingException;
3234
import java.security.cert.X509Certificate;
3335
import java.util.Objects;
3436

3537
public final class OcspUrl {
3638

3739
public static final URI AIA_ESTEID_2015 = URI.create("http://aia.sk.ee/esteid2015");
3840

39-
/**
40-
* The OID for OCSP responder URLs.
41-
* <p>
42-
* https://oidref.com/1.3.6.1.5.5.7.48.1
43-
*/
44-
private static final ASN1ObjectIdentifier OCSP_RESPONDER_OID
45-
= new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1").intern();
46-
4741
/**
4842
* Returns the OCSP responder {@link URI} or {@code null} if it doesn't have one.
4943
*/
5044
public static URI getOcspUri(X509Certificate certificate) {
5145
Objects.requireNonNull(certificate, "certificate");
52-
final byte[] value = certificate.getExtensionValue(Extension.authorityInfoAccess.getId());
53-
if (value == null) {
54-
return null;
55-
}
56-
57-
final ASN1Primitive authorityInfoAccess;
46+
final X509CertificateHolder certificateHolder;
5847
try {
59-
authorityInfoAccess = JcaX509ExtensionUtils.parseExtensionValue(value);
60-
} catch (IOException | IllegalArgumentException e) {
61-
return null;
62-
}
63-
if (!(authorityInfoAccess instanceof DLSequence)) {
64-
return null;
65-
}
66-
67-
final DLSequence aiaSequence = (DLSequence) authorityInfoAccess;
68-
final DLTaggedObject taggedObject = findObject(aiaSequence, OCSP_RESPONDER_OID, DLTaggedObject.class);
69-
if (taggedObject == null) {
70-
return null;
71-
}
72-
73-
if (taggedObject.getTagNo() != BERTags.OBJECT_IDENTIFIER) {
74-
return null;
75-
}
76-
77-
final byte[] encoded;
78-
try {
79-
encoded = taggedObject.getEncoded();
80-
} catch (IOException e) {
81-
return null;
82-
}
83-
int length = encoded[1] & 0xFF;
84-
final String uri = new String(encoded, 2, length, StandardCharsets.UTF_8);
85-
return URI.create(uri);
86-
}
87-
88-
private static <T> T findObject(DLSequence sequence, ASN1ObjectIdentifier oid, Class<T> type) {
89-
for (final ASN1Encodable element : sequence) {
90-
if (!(element instanceof DLSequence)) {
91-
continue;
92-
}
93-
94-
final DLSequence subSequence = (DLSequence) element;
95-
if (subSequence.size() != 2) {
96-
continue;
97-
}
98-
99-
final ASN1Encodable key = subSequence.getObjectAt(0);
100-
final ASN1Encodable value = subSequence.getObjectAt(1);
101-
102-
if (key.equals(oid) && type.isInstance(value)) {
103-
return type.cast(value);
48+
certificateHolder = new X509CertificateHolder(certificate.getEncoded());
49+
final AuthorityInformationAccess authorityInformationAccess =
50+
AuthorityInformationAccess.fromExtensions(certificateHolder.getExtensions());
51+
for (AccessDescription accessDescription :
52+
authorityInformationAccess.getAccessDescriptions()) {
53+
if (accessDescription.getAccessMethod().equals(AccessDescription.id_ad_ocsp) &&
54+
accessDescription.getAccessLocation().getTagNo() == GeneralName.uniformResourceIdentifier) {
55+
final String accessLocationUrl = ((ASN1String) accessDescription.getAccessLocation().getName())
56+
.getString();
57+
return URI.create(accessLocationUrl);
58+
}
10459
}
60+
} catch (IOException | CertificateEncodingException | IllegalArgumentException | NullPointerException e) {
61+
return null;
10562
}
106-
10763
return null;
10864
}
10965

0 commit comments

Comments
 (0)