Skip to content

Commit fa76e2a

Browse files
committed
feat(token): add Web eID custom JSON token validation
WE2-586 Signed-off-by: Mart Somermaa <mrts@users.noreply.github.com>
1 parent d7f291c commit fa76e2a

File tree

101 files changed

+2167
-2464
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2167
-2464
lines changed

README.md

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Add the following lines to Maven `pom.xml` to include the Web eID authentication
2323
<dependency>
2424
<groupId>org.webeid.security</groupId>
2525
<artifactId>authtoken-validation</artifactId>
26-
<version>1.0.1</version>
26+
<version>2.0.0</version>
2727
</dependency>
2828
</dependencies>
2929

@@ -114,16 +114,15 @@ The validation library needs to generate authentication challenge nonces and sto
114114
Configure the nonce generator as follows:
115115

116116
```java
117-
import org.webeid.security.nonce.NonceGenerator;
118-
import org.webeid.security.nonce.NonceGeneratorBuilder;
117+
119118

120119
...
121-
public NonceGenerator nonceGenerator() {
122-
return new NonceGeneratorBuilder()
123-
.withNonceCache(nonceCache())
124-
.build();
120+
public NonceGenerator challengeNonceGenerator(){
121+
return new NonceGeneratorBuilder()
122+
.withNonceCache(nonceCache())
123+
.build();
125124
}
126-
...
125+
...
127126
```
128127

129128
## 4. Add trusted certificate authority certificates
@@ -150,18 +149,17 @@ The mandatory parameters are the website origin (the URL serving the web applica
150149
The authentication token validator will be used in the login processing component of your web application authentication framework; it is thread-safe and should be scoped as a singleton.
151150

152151
```java
153-
import org.webeid.security.validator.AuthTokenValidator;
154-
import org.webeid.security.validator.AuthTokenValidatorBuilder;
152+
155153

156154
...
157-
public AuthTokenValidator tokenValidator() throws JceException {
158-
return new AuthTokenValidatorBuilder()
159-
.withSiteOrigin("https://example.org")
160-
.withNonceCache(nonceCache())
161-
.withTrustedCertificateAuthorities(trustedCertificateAuthorities())
162-
.build();
155+
public AuthTokenValidator tokenValidator()throws JceException{
156+
return new AuthTokenValidatorBuilder()
157+
.withSiteOrigin("https://example.org")
158+
.withNonceCache(nonceCache())
159+
.withTrustedCertificateAuthorities(trustedCertificateAuthorities())
160+
.build();
163161
}
164-
...
162+
...
165163
```
166164

167165
## 6. Add a REST endpoint for issuing challenge nonces
@@ -181,13 +179,13 @@ import org.springframework.web.bind.annotation.RestController;
181179
public class ChallengeController {
182180

183181
@Autowired // for brevity, prefer constructor dependency injection
184-
private NonceGenerator nonceGenerator;
182+
private NonceGenerator challengeNonceGenerator;
185183

186184
@GetMapping("challenge")
187185
public ChallengeDTO challenge() {
188186
// a simple DTO with a single 'challenge' field
189187
final ChallengeDTO challenge = new ChallengeDTO();
190-
challenge.setNonce(nonceGenerator.generateAndStoreNonce());
188+
challenge.setNonce(challengeNonceGenerator.generateAndStoreNonce());
191189
return challenge;
192190
}
193191
}
@@ -271,7 +269,7 @@ X509Certificate userCertificate = tokenValidator.validate(tokenString);
271269
The `validate()` method returns the validated user certificate object if validation is successful or throws an exception as described in section *[Possible validation errors](#possible-validation-errors)* below if validation fails. The `CertUtil` and `TitleCase` classes can be used for extracting user information from the user certificate object:
272270
273271
```java
274-
import static org.webeid.security.util.TitleCase.toTitleCase;
272+
import static TitleCase.toTitleCase;
275273
276274
...
277275
@@ -318,7 +316,7 @@ Unless a designated OCSP responder service is in use, it is required that the AI
318316
319317
## Possible validation errors
320318
321-
The `validate()` method of `AuthTokenValidator` returns the validated user certificate object if validation is successful or throws an exception if validation fails. All exceptions that can occur during validation derive from `TokenValidationException`, the list of available exceptions is available [here](src/main/java/org/webeid/security/exceptions/). Each exception file contains a documentation comment under which conditions the exception is thrown.
319+
The `validate()` method of `AuthTokenValidator` returns the validated user certificate object if validation is successful or throws an exception if validation fails. All exceptions that can occur during validation derive from `TokenValidationException`, the list of available exceptions is available [here](src/main/java/eu/webeid/security/exceptions/). Each exception file contains a documentation comment under which conditions the exception is thrown.
322320
323321
# Nonce generation
324322
The authentication protocol requires support for generating challenge nonces, large random numbers that can be used only once, and storing them for later use during token validation. The validation library uses the *java.security.SecureRandom* API as the secure random source and the JSR107 *javax.cache.Cache* API for storing issued challenge nonces.
@@ -338,7 +336,7 @@ The nonce cache instance is used to store the nonce expiry time using the nonce
338336
The nonce generator configuration and construction is described in more detail in section *[3. Configure the nonce generator](#3-configure-the-nonce-generator)*. Once the generator object has been constructed, it can be used for generating nonces as follows:
339337
340338
```java
341-
String nonce = nonceGenerator.generateAndStoreNonce();
339+
String nonce = challengeNonceGenerator.generateAndStoreNonce();
342340
```
343341
344342
The `generateAndStoreNonce()` method both generates the nonce and stores it in the cache.

pom.xml

Lines changed: 7 additions & 25 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>1.2.0</version>
8+
<version>2.0.0-SNAPSHOT</version>
99
<packaging>jar</packaging>
1010
<name>authtoken-validation</name>
1111
<description>Web eID authentication token validation library for Java</description>
@@ -15,12 +15,11 @@
1515
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
1616
<java.version>1.8</java.version>
1717
<jjwt.version>0.11.2</jjwt.version>
18-
<slf4j.version>1.7.30</slf4j.version>
18+
<slf4j.version>1.7.32</slf4j.version>
1919
<bouncycastle.version>1.69</bouncycastle.version>
20-
<caffeine.version>2.8.5</caffeine.version>
21-
<junit-jupiter.version>5.6.2</junit-jupiter.version>
22-
<assertj.version>3.17.2</assertj.version>
23-
<mockito.version>3.12.4</mockito.version>
20+
<junit-jupiter.version>5.8.1</junit-jupiter.version>
21+
<assertj.version>3.21.0</assertj.version>
22+
<mockito.version>4.0.0</mockito.version>
2423
<jacoco.version>0.8.5</jacoco.version>
2524
<sonar.coverage.jacoco.xmlReportPaths>
2625
${project.basedir}/../jacoco-coverage-report/target/site/jacoco-aggregate/jacoco.xml
@@ -54,11 +53,6 @@
5453
<version>${jjwt.version}</version>
5554
</dependency>
5655

57-
<dependency>
58-
<groupId>javax.cache</groupId>
59-
<artifactId>cache-api</artifactId>
60-
<version>1.1.1</version>
61-
</dependency>
6256
<dependency>
6357
<groupId>org.slf4j</groupId>
6458
<artifactId>slf4j-api</artifactId>
@@ -67,7 +61,7 @@
6761
<dependency>
6862
<groupId>com.google.guava</groupId>
6963
<artifactId>guava</artifactId>
70-
<version>30.1-jre</version>
64+
<version>31.0.1-jre</version>
7165
</dependency>
7266
<dependency>
7367
<groupId>org.bouncycastle</groupId>
@@ -82,7 +76,7 @@
8276
<dependency>
8377
<groupId>com.squareup.okhttp3</groupId>
8478
<artifactId>okhttp</artifactId>
85-
<version>4.9.0</version>
79+
<version>4.9.2</version>
8680
</dependency>
8781

8882
<dependency>
@@ -109,18 +103,6 @@
109103
<version>${slf4j.version}</version>
110104
<scope>test</scope>
111105
</dependency>
112-
<dependency>
113-
<groupId>com.github.ben-manes.caffeine</groupId>
114-
<artifactId>caffeine</artifactId>
115-
<version>${caffeine.version}</version>
116-
<scope>test</scope>
117-
</dependency>
118-
<dependency>
119-
<groupId>com.github.ben-manes.caffeine</groupId>
120-
<artifactId>jcache</artifactId>
121-
<version>${caffeine.version}</version>
122-
<scope>test</scope>
123-
</dependency>
124106
</dependencies>
125107

126108
<build>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
23+
package eu.webeid.security.authtoken;
24+
25+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
26+
27+
@JsonIgnoreProperties(ignoreUnknown = true)
28+
public class WebEidAuthToken {
29+
30+
private String certificate;
31+
private String signature;
32+
private String algorithm;
33+
private String version;
34+
private boolean useOriginCertHash;
35+
36+
public String getCertificate() {
37+
return certificate;
38+
}
39+
40+
public void setCertificate(String certificate) {
41+
this.certificate = certificate;
42+
}
43+
44+
public String getSignature() {
45+
return signature;
46+
}
47+
48+
public void setSignature(String signature) {
49+
this.signature = signature;
50+
}
51+
52+
public String getAlgorithm() {
53+
return algorithm;
54+
}
55+
56+
public void setAlgorithm(String algorithm) {
57+
this.algorithm = algorithm;
58+
}
59+
60+
public String getVersion() {
61+
return version;
62+
}
63+
64+
public void setVersion(String version) {
65+
this.version = version;
66+
}
67+
68+
public boolean getUseOriginCertHash() {
69+
return useOriginCertHash;
70+
}
71+
72+
public void setUseOriginCertHash(boolean useOriginCertHash) {
73+
this.useOriginCertHash = useOriginCertHash;
74+
}
75+
76+
}

src/main/java/org/webeid/security/certificate/CertificateData.java renamed to src/main/java/eu/webeid/security/certificate/CertificateData.java

Lines changed: 2 additions & 2 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
@@ -20,7 +20,7 @@
2020
* SOFTWARE.
2121
*/
2222

23-
package org.webeid.security.certificate;
23+
package eu.webeid.security.certificate;
2424

2525
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
2626
import org.bouncycastle.asn1.x500.RDN;

src/main/java/org/webeid/security/certificate/CertificateLoader.java renamed to src/main/java/eu/webeid/security/certificate/CertificateLoader.java

Lines changed: 10 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
@@ -20,7 +20,9 @@
2020
* SOFTWARE.
2121
*/
2222

23-
package org.webeid.security.certificate;
23+
package eu.webeid.security.certificate;
24+
25+
import eu.webeid.security.exceptions.CertificateDecodingException;
2426

2527
import java.io.ByteArrayInputStream;
2628
import java.io.IOException;
@@ -29,9 +31,10 @@
2931
import java.security.cert.CertificateFactory;
3032
import java.security.cert.X509Certificate;
3133
import java.util.ArrayList;
32-
import java.util.Base64;
3334
import java.util.List;
3435

36+
import static eu.webeid.security.util.Base64Decoder.decodeBase64;
37+
3538
public final class CertificateLoader {
3639

3740
public static X509Certificate[] loadCertificatesFromResources(String... resourceNames) throws CertificateException, IOException {
@@ -48,11 +51,13 @@ public static X509Certificate[] loadCertificatesFromResources(String... resource
4851
return caCertificates.toArray(new X509Certificate[0]);
4952
}
5053

51-
public static X509Certificate loadCertificateFromBase64String(String certificate) throws CertificateException, IOException {
52-
try (final InputStream targetStream = new ByteArrayInputStream(Base64.getDecoder().decode(certificate))) {
54+
public static X509Certificate decodeCertificateFromBase64(String certificateInBase64) throws CertificateDecodingException {
55+
try (final InputStream targetStream = new ByteArrayInputStream(decodeBase64(certificateInBase64))) {
5356
return (X509Certificate) CertificateFactory
5457
.getInstance("X509")
5558
.generateCertificate(targetStream);
59+
} catch (IOException | CertificateException | IllegalArgumentException e) {
60+
throw new CertificateDecodingException(e);
5661
}
5762
}
5863

src/main/java/org/webeid/security/certificate/CertificateValidator.java renamed to src/main/java/eu/webeid/security/certificate/CertificateValidator.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
* SOFTWARE.
2121
*/
2222

23-
package org.webeid.security.certificate;
23+
package eu.webeid.security.certificate;
2424

25-
import org.webeid.security.exceptions.CertificateNotTrustedException;
26-
import org.webeid.security.exceptions.JceException;
27-
import org.webeid.security.exceptions.CertificateExpiredException;
28-
import org.webeid.security.exceptions.CertificateNotYetValidException;
25+
import eu.webeid.security.exceptions.CertificateExpiredException;
26+
import eu.webeid.security.exceptions.CertificateNotYetValidException;
27+
import eu.webeid.security.exceptions.JceException;
28+
import eu.webeid.security.exceptions.CertificateNotTrustedException;
2929

3030
import java.security.GeneralSecurityException;
3131
import java.security.InvalidAlgorithmParameterException;
@@ -65,13 +65,15 @@ public static void trustedCACertificatesAreValidOnDate(Set<TrustAnchor> trustedC
6565

6666
public static X509Certificate validateIsSignedByTrustedCA(X509Certificate certificate,
6767
Set<TrustAnchor> trustedCACertificateAnchors,
68-
CertStore trustedCACertificateCertStore) throws CertificateNotTrustedException, JceException {
68+
CertStore trustedCACertificateCertStore,
69+
Date date) throws CertificateNotTrustedException, JceException {
6970
final X509CertSelector selector = new X509CertSelector();
7071
selector.setCertificate(certificate);
7172

7273
try {
7374
final PKIXBuilderParameters pkixBuilderParameters = new PKIXBuilderParameters(trustedCACertificateAnchors, selector);
7475
pkixBuilderParameters.setRevocationEnabled(false);
76+
pkixBuilderParameters.setDate(date);
7577
pkixBuilderParameters.addCertStore(trustedCACertificateCertStore);
7678

7779
// See the comment in buildCertStoreFromCertificates() below why we use the default JCE provider.

src/main/java/org/webeid/security/exceptions/OriginMismatchException.java renamed to src/main/java/eu/webeid/security/challenge/ChallengeNonce.java

Lines changed: 17 additions & 12 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) 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
@@ -20,21 +20,26 @@
2020
* SOFTWARE.
2121
*/
2222

23-
package org.webeid.security.exceptions;
23+
package eu.webeid.security.challenge;
2424

25-
/**
26-
* Thrown when the origin URL from the token {@code aud} field does not match the configured origin,
27-
* i.e. the URL that the site is running on.
28-
*/
29-
public class OriginMismatchException extends TokenValidationException {
25+
import java.time.ZonedDateTime;
26+
27+
public class ChallengeNonce {
3028

31-
private static final String MESSAGE = "Origin from the token does not match the configured origin";
29+
private final String base64EncodedNonce;
30+
private final ZonedDateTime expirationTime;
3231

33-
public OriginMismatchException() {
34-
super(MESSAGE);
32+
public ChallengeNonce(String base64EncodedNonce, ZonedDateTime expirationTime) {
33+
this.base64EncodedNonce = base64EncodedNonce;
34+
this.expirationTime = expirationTime;
3535
}
3636

37-
public OriginMismatchException(Throwable cause) {
38-
super(MESSAGE, cause);
37+
public ZonedDateTime getExpirationTime() {
38+
return expirationTime;
3939
}
40+
41+
public String getBase64EncodedNonce() {
42+
return base64EncodedNonce;
43+
}
44+
4045
}

0 commit comments

Comments
 (0)