Skip to content

Commit 1ec25c3

Browse files
committed
Initial commit
Signed-off-by: Mart Somermaa <[email protected]>
1 parent b5a8097 commit 1ec25c3

File tree

66 files changed

+4318
-0
lines changed

Some content is hidden

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

66 files changed

+4318
-0
lines changed

.editorconfig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# EditorConfig helps developers define and maintain consistent
2+
# coding styles between different editors and IDEs
3+
# editorconfig.org
4+
5+
root = true
6+
7+
[*]
8+
9+
# We recommend you to keep these unchanged
10+
end_of_line = lf
11+
charset = utf-8
12+
trim_trailing_whitespace = true
13+
insert_final_newline = true
14+
15+
# Change these settings to your own preference
16+
indent_style = space
17+
indent_size = 4
18+
19+
[*.{ts,tsx,js,jsx,json,css,scss,yml}]
20+
indent_size = 2
21+
22+
[*.md]
23+
trim_trailing_whitespace = false

.gitattributes

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
* text=auto
2+
*.java text eol=lf
3+
*.xml text eol=lf
4+
*.pl text eol=lf
5+
*.py text eol=lf
6+
*.html text eol=lf
7+
*.scss text eol=lf
8+
*.css text eol=lf
9+
*.js text eol=lf
10+
*.bat text eol=crlf
11+
*.cmd text eol=crlf
12+
MANIFEST.MF text eol=lf
13+
commit-msg text eol=lf
14+
.gitattributes text eol=lf
15+
.gitignore text eol=lf
16+
.gitlab-ci.yml text eol=lf

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
target/
2+
3+
### STS ###
4+
.apt_generated
5+
.classpath
6+
.factorypath
7+
.project
8+
.settings
9+
.springBeans
10+
.sts4-cache
11+
12+
### IntelliJ IDEA ###
13+
.idea
14+
*.iws
15+
*.iml
16+
*.ipr
17+
18+
### NetBeans ###
19+
/nbproject/private/
20+
/nbbuild/
21+
/dist/
22+
/nbdist/
23+
/.nb-gradle/
24+
build/
25+
26+
### VS Code ###
27+
.vscode/
28+
29+
### Vim ###
30+
*.swp

.gitlab-ci-mvn-settings.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
3+
<servers>
4+
<server>
5+
<id>gitlab-maven</id>
6+
<configuration>
7+
<httpHeaders>
8+
<property>
9+
<name>Job-Token</name>
10+
<value>${env.CI_JOB_TOKEN}</value>
11+
</property>
12+
</httpHeaders>
13+
</configuration>
14+
</server>
15+
</servers>
16+
</settings>

README.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
<!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=6 orderedList=false} -->
2+
3+
# web-eid-authtoken-validation-java
4+
5+
![European Regional Development Fund](https://github.com/e-gov/RIHA-Frontend/raw/master/logo/EU/EU.png)
6+
7+
The Web eID authentication token validation library for Java allows validating
8+
Web eID JWT authentication tokens during authentication in web applications.
9+
10+
# Quickstart
11+
12+
Complete the steps below to add strong authentication support to your web application back end.
13+
14+
To run this quickstart you need a Java web application that uses Maven or Gradle to manage packages.
15+
16+
See full example [here]().
17+
18+
## 1. Add the library to your Maven or Gradle project
19+
20+
Add the following lines to Maven `pom.xml`:
21+
22+
```xml
23+
<dependency>
24+
<groupId>org.webeid.security</groupId>
25+
<artifactId>authtoken-validation</artifactId>
26+
<version>1.0.0-SNAPSHOT</version>
27+
</dependency>
28+
29+
<repositories>
30+
<repository>
31+
<id>gitlab-maven</id>
32+
<url>https://gitlab.com/api/v4/projects/19948337/packages/maven</url>
33+
</repository>
34+
</repositories>
35+
```
36+
37+
## 2. Add cache support
38+
39+
## 3. Add trusted certificate authorities
40+
41+
## 4. Add REST endpoints for the authentication requests
42+
43+
## 5. Add authentication token validation
44+
45+
46+
# Introduction
47+
48+
This library has everything that it takes to ensure that the authentication token sent by the Web-eID browser extension contains valid data. And that this data is consistent and was not modified in-between by the third party. It is easy to configure and to integrate into your authentication service.
49+
50+
The library is designed to take advantage of the so-called "builder" pattern to separate the configuration and execution parts from each other.
51+
52+
# Token validation
53+
54+
The token validation process consists of three stages:
55+
56+
- Firstly, the **token header** gets parsed and the user certificate is extracted from the *x5c* field. Then the certificate is checked for validity, expiration and purpose. Also, an optional OCSP check is executed.
57+
- Secondly, if the user certificate is valid and has a suitable purpose, the **token signature** is checked for validity.
58+
- Lastly, the **token body** gets parsed. *Nonce* and *Origin* fields get validated. Also, an optional service certificate fingerprint check is executed.
59+
60+
## Basic usage
61+
62+
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):
63+
```java
64+
Cache<String, Nonce> cache = // TODO: create new cache instance here
65+
```
66+
You will also need to provide issuer certificates:
67+
```java
68+
X509Certificate[] trustedCertificateAuthorities = // TODO: load trusted issuer certs
69+
```
70+
The **cache** instance is used to look up the nonce object using its unique value as a search key. The values in the cache are populated by the nonce generator (which is described in detail in the *Nonce generation* chapter), while the **trustedCertificateAuthorities** certificates are used to validate the user certificate's trust chain.
71+
72+
The simplest way to create a validator instance is to use the builder class with a minimal set of mandatory parameters:
73+
```java
74+
AuthTokenValidator validator = new AuthTokenValidatorBuilder("https://my.origin.address")
75+
.withNonceCache(cache)
76+
.withTrustedCertificateAuthorities(trustedCertificateAuthorities)
77+
.build();
78+
X509Certificate userCertificate = tokenValidator.validate(myTokenString);
79+
```
80+
81+
## Configuration
82+
Additional configuration is possible for the builder class:
83+
84+
- `withCertificateFingerprint(String)` - certificate fingerprint validation is disabled by default, but can be enabled.
85+
- `withoutCertificateRevocationValidation()` - disables certificate OCSP validation, which is enabled by default.
86+
- `withAllowedClockSkew(Long)` - allows clock skew in seconds during token parsing process. Default value is **180L**, which corresponds to 3 minutes.
87+
88+
Example:
89+
```java
90+
AuthTokenValidator validator = new AuthTokenValidatorBuilder("https://my.origin.address")
91+
.withNonceCache(cache)
92+
.withTrustedCertificateAuthorities(trustedCertificateAuthorities)
93+
.withCertificateFingerprint("urn:cert:sha-256:fingerprint-hash-goes-here")
94+
.withoutCertificateRevocationValidation()
95+
.withAllowedClockSkew(3600L)
96+
.build();
97+
98+
X509Certificate userCertificate = tokenValidator.validate(myTokenString);
99+
```
100+
101+
### Certificate fingerprint
102+
Due to the technical limitation of Web Browsers, certificate fingerprint validation currently works only when Firefox browser is used.
103+
104+
## What gets validated
105+
The token validation process covers different aspects. It ensures, that:
106+
107+
- **token header** is valid, contains a valid and trusted certificate, which has not expired and has a proper purpose.
108+
- **token signature** is not empty, is valid and was created using the certificate, that was specified in the header.
109+
- **token body** is not empty and has meaningful data.
110+
- **token** has not expired.
111+
- **nonce value**, received from the client-side, has the corresponding nonce object in the cache, which has not expired.
112+
- **Origin URL** is valid and matches the *expected Origin URL* set in builder class.
113+
114+
**NB!** `Nonce object` is a `Nonce value` plus `metadata` . To know more about it please refer to the *Nonce generation* chapter.
115+
116+
## Possible validation errors
117+
There is a set of possible errors that can occur during the validation process:
118+
119+
#### NonceNotFoundException
120+
Is thrown if the nonce object is not found from the nonce cache using provided nonce value.
121+
#### NonceExpiredException
122+
Is thrown if the nonce object is found but has expired.
123+
#### OriginMismatchException
124+
Is thrown if origin URL does not match the *expected origin URL* which was set in builder class.
125+
#### ServiceCertificateFingerprintValidationException
126+
Is thrown if the service certificate fingerprint validation is enabled, however, the actual fingerprint does not match the *expected certificate fingerprint* which was set in builder class.
127+
#### TokenExpiredException
128+
Is thrown if an expired token is detected and the `withAllowedClockSkew` configuration option does not cover the time difference.
129+
#### TokenParseException
130+
Is thrown if the token has an invalid format and cannot be parsed.
131+
#### TokenSignatureValidationException
132+
Is thrown if the token signature is missing or has an invalid format.
133+
#### UserCertificateExpiredException
134+
Is thrown if the user certificate's validity period end date is in the past.
135+
#### UserCertificateMissingPurposeException
136+
Is thrown if the purpose of the user certificate is not defined.
137+
#### UserCertificateNotTrustedException
138+
Is thrown if the user certificate is not trusted.
139+
#### UserCertificateNotYetValidException
140+
Is thrown if the user certificate's validity period start date is in the future.
141+
#### UserCertificateParseException
142+
Is thrown if the user certificate cannot be parsed from the token's x5c field.
143+
#### UserCertificateRevocationCheckFailException
144+
Is thrown if the user certificate OCSP check has failed.
145+
#### UserCertificateRevokedException
146+
Is thrown if the user certificate OCSP check's result is not GOOD.
147+
#### UserCertificateWrongPurposeException
148+
Is thrown if according to the user certificate's purpose is not meant to be used for authentication.
149+
150+
## Create your own validator implementation
151+
It is possible to create a custom implementation of the token validator. To achieve this, you have to:
152+
153+
- Create a new validator class, which extends the `AuthTokenValidator` interface.
154+
- Create a new builder class, which extends the `AuthTokenValidatorBuilder` class and overrides the `build()` method to create an instance of your new validator class.
155+
156+
**MyCustomTokenValidator.java**
157+
```java
158+
class MyCustomTokenValidator implements AuthTokenValidator {
159+
...
160+
}
161+
```
162+
**MyCustomBuilder.java**
163+
```java
164+
class MyCustomBuilder extends AuthTokenValidatorBuilder {
165+
@Override
166+
public AuthTokenValidator build() {
167+
...
168+
validateParameters();
169+
return new MyCustomTokenValidator(...);
170+
}
171+
...
172+
}
173+
```
174+
175+
Additionally, you can override the `validateParameters()` method in case you need to add new fields and validate them:
176+
177+
178+
**MyCustomBuilder.java**
179+
```java
180+
class MyCustomBuilder extends AuthTokenValidatorBuilder {
181+
182+
private String myNewField = "";
183+
184+
private MyCustomBuilder withMyNewField(String myNewField) {
185+
this.myNewField = myNewField;
186+
}
187+
188+
@Override
189+
public AuthTokenValidator build() {
190+
validateParameters();
191+
return new MyCustomTokenValidator(..., myNewField);
192+
}
193+
194+
@Override
195+
protected void validateParameters() {
196+
super.validateParameters();
197+
Objects.requireNonNull(myNewField, "My new field must not be null");
198+
}
199+
...
200+
}
201+
```
202+
Then use it in your application:
203+
```java
204+
MyCustomTokenValidator validator = new MyCustomBuilder("https://my.origin.address")
205+
.withNonceCache(cache)
206+
.withTrustedCertificateAuthorities(trustedCertificateAuthorities)
207+
.withMyNewField("My new field value")
208+
.build();
209+
210+
X509Certificate certificate = validator.validate(myTokenString);
211+
```
212+
213+
# Nonce generation
214+
Nonce value generation is implemented similarly to the token validation - it also uses the builder pattern and also requires the cache instance.
215+
216+
## Basic usage
217+
218+
The builder class will need a *javax.cache.Cache* instance (use *Hazelcast* or *Infinispan* if you do use a cluster, or *Caffeine* if you don't):
219+
```java
220+
Cache<String, Nonce> cache = // TODO: create new cache instance here
221+
```
222+
223+
The **cache** is used store nonce objects.
224+
225+
The simplest way to create a generator instance is to use the builder class with a minimal set of mandatory parameters:
226+
227+
```java
228+
NonceGenerator generator = new NonceGeneratorBuilder()
229+
.withNonceCache(cache)
230+
.build();
231+
232+
byte[] nonceKey = nonceGenerator.generate();
233+
```
234+
The`generate()` method also puts the generated nonce object into the provided cache.
235+
236+
## Configuration
237+
Additional configuration is possible for the builder class:
238+
239+
- `withNonceTtl(int)` - specifies the time-to-live in minutes. Default value is 5.
240+
- `withSecureRandom(SecureRandom)` - allows to specify a custom [SecureRandom](https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html) class instance.
241+
242+
Example:
243+
```java
244+
NonceGenerator generator = new NonceGeneratorBuilder()
245+
.withNonceCache(cache)
246+
.withNonceTtl(10)
247+
.withSecureRandom(myCustomSecureRandomInstance)
248+
.build();
249+
250+
byte[] nonceKey = nonceGenerator.generate();
251+
```
252+
## How it works
253+
Here are some useful facts:
254+
255+
- Nonce objects are stored into the cache on the server-side and later are looked up from the same cache using the nonce values as keys.
256+
- Nonce values are sent to the client-side, nonce objects are not.
257+
- Every nonce value is meant to be unique and as less likely reproducible as possible.
258+
- Every nonce object is meant to be used only once.
259+
- Every nonce object is meant to be used before it expires.

0 commit comments

Comments
 (0)