Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ The token validation process consists of three stages:

## Basic usage

The builder class need a *javax.cache.Cache* instance (use *Hazelcast* or *Infinispan* if you do use a cluster, or *Caffeine* if you don't):
The builder class needs a *javax.cache.Cache* instance (use *Hazelcast* or *Infinispan* if you use a cluster, or *Caffeine* or *Ehcache* if you don't):
```java
Cache<String, Nonce> cache = // TODO: create new cache instance here
```
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>authtoken-validation</artifactId>
<groupId>org.webeid.security</groupId>
<version>1.0.0</version>
<version>1.0.1</version>
<packaging>jar</packaging>
<name>authtoken-validation</name>
<description>Web eID authentication token validation library for Java</description>
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/webeid/security/util/OcspUrls.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.webeid.security.util;

import java.net.URI;

public class OcspUrls {
public static final URI ESTEID_2015 = URI.create("http://aia.sk.ee/esteid2015");

private OcspUrls() {
throw new IllegalStateException("Constants class");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Objects;

import static org.webeid.security.nonce.NonceGeneratorBuilder.requirePositiveDuration;
import static org.webeid.security.util.OcspUrls.ESTEID_2015;
import static org.webeid.security.util.SubjectCertificatePolicies.EST_MOBILE_ID_POLICY;

/**
Expand All @@ -53,6 +54,8 @@ final class AuthTokenValidationConfiguration {
private String siteCertificateSha256Fingerprint;
// Don't allow Estonian Mobile-ID policy by default.
private Collection<ASN1ObjectIdentifier> disallowedSubjectCertificatePolicies = Sets.newHashSet(EST_MOBILE_ID_POLICY);
// Disable OCSP nonce extension for EstEID 2015 cards by default.
private Collection<URI> nonceDisabledOcspUrls = Sets.newHashSet(ESTEID_2015);

AuthTokenValidationConfiguration() {
}
Expand All @@ -67,6 +70,7 @@ private AuthTokenValidationConfiguration(AuthTokenValidationConfiguration other)
this.isSiteCertificateFingerprintValidationEnabled = other.isSiteCertificateFingerprintValidationEnabled;
this.siteCertificateSha256Fingerprint = other.siteCertificateSha256Fingerprint;
this.disallowedSubjectCertificatePolicies = new HashSet<>(other.disallowedSubjectCertificatePolicies);
this.nonceDisabledOcspUrls = new HashSet<>(other.nonceDisabledOcspUrls);
}

void setSiteOrigin(URI siteOrigin) {
Expand Down Expand Up @@ -126,6 +130,14 @@ public String getSiteCertificateSha256Fingerprint() {
return siteCertificateSha256Fingerprint;
}

public Collection<ASN1ObjectIdentifier> getDisallowedSubjectCertificatePolicies() {
return disallowedSubjectCertificatePolicies;
}

public Collection<URI> getNonceDisabledOcspUrls() {
return nonceDisabledOcspUrls;
}

/**
* Checks that the configuration parameters are valid.
*
Expand All @@ -151,8 +163,4 @@ AuthTokenValidationConfiguration copy() {
return new AuthTokenValidationConfiguration(this);
}

public Collection<ASN1ObjectIdentifier> getDisallowedSubjectCertificatePolicies() {
return disallowedSubjectCertificatePolicies;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private ValidatorBatch getCertTrustValidators() {
return ValidatorBatch.createFrom(
certTrustedValidator::validateCertificateTrusted
).addOptional(configuration.isUserCertificateRevocationCheckWithOcspEnabled(),
new SubjectCertificateNotRevokedValidator(certTrustedValidator, httpClientSupplier.get())::validateCertificateNotRevoked
new SubjectCertificateNotRevokedValidator(certTrustedValidator, httpClientSupplier.get(), configuration.getNonceDisabledOcspUrls())::validateCertificateNotRevoked
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public final class OcspRequestBuilder {
private DigestCalculator digestCalculator = Digester.sha1();
private X509Certificate subjectCertificate;
private X509Certificate issuerCertificate;
private boolean ocspNonceEnabled = true;

public OcspRequestBuilder generator(SecureRandom generator) {
this.randomGenerator = generator;
Expand All @@ -91,12 +92,16 @@ public OcspRequestBuilder issuer(X509Certificate issuer) {
return this;
}

public OcspRequestBuilder enableOcspNonce(boolean ocspNonceEnabled) {
this.ocspNonceEnabled = ocspNonceEnabled;
return this;
}

/**
* ATTENTION: The returned {@link OCSPReq} is not re-usable/cacheable! It contains a one-time nonce
* and CA's will (should) reject subsequent requests that have the same nonce value.
*/
public OCSPReq build() throws OCSPException, IOException, CertificateEncodingException {
final SecureRandom generator = Objects.requireNonNull(this.randomGenerator, "randomGenerator");
final DigestCalculator calculator = Objects.requireNonNull(this.digestCalculator, "digestCalculator");
final X509Certificate certificate = Objects.requireNonNull(this.subjectCertificate, "subjectCertificate");
final X509Certificate issuer = Objects.requireNonNull(this.issuerCertificate, "issuerCertificate");
Expand All @@ -109,6 +114,16 @@ public OCSPReq build() throws OCSPException, IOException, CertificateEncodingExc
final OCSPReqBuilder builder = new OCSPReqBuilder();
builder.addRequest(certId);

if (ocspNonceEnabled) {
addNonce(builder);
}

return builder.build();
}

private void addNonce(OCSPReqBuilder builder) {
final SecureRandom generator = Objects.requireNonNull(this.randomGenerator, "randomGenerator");

final byte[] nonce = new byte[8];
generator.nextBytes(nonce);

Expand All @@ -118,7 +133,6 @@ public OCSPReq build() throws OCSPException, IOException, CertificateEncodingExc
};

builder.setRequestExtensions(new Extensions(extensions));

return builder.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.net.URI;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Objects;

public final class SubjectCertificateNotRevokedValidator {
Expand All @@ -46,10 +47,12 @@ public final class SubjectCertificateNotRevokedValidator {

private final SubjectCertificateTrustedValidator trustValidator;
private final OkHttpClient httpClient;
private final Collection<URI> nonceDisabledOcspUrls;

public SubjectCertificateNotRevokedValidator(SubjectCertificateTrustedValidator trustValidator, OkHttpClient httpClient) {
public SubjectCertificateNotRevokedValidator(SubjectCertificateTrustedValidator trustValidator, OkHttpClient httpClient, Collection<URI> nonceDisabledOcspUrls) {
this.trustValidator = trustValidator;
this.httpClient = httpClient;
this.nonceDisabledOcspUrls = nonceDisabledOcspUrls;
}

/**
Expand All @@ -66,9 +69,14 @@ public void validateCertificateNotRevoked(AuthTokenValidatorData actualTokenData
if (uri == null) {
throw new UserCertificateRevocationCheckFailedException("The CA/certificate doesn't have an OCSP responder");
}
final boolean ocspNonceDisabled = nonceDisabledOcspUrls.contains(uri);
if (ocspNonceDisabled) {
LOG.debug("Disabling OCSP nonce extension");
}

final OCSPReq request = new OcspRequestBuilder()
.certificate(certificate)
.enableOcspNonce(!ocspNonceDisabled)
.issuer(Objects.requireNonNull(trustValidator.getSubjectCertificateIssuerCertificate()))
.build();

Expand Down