Skip to content
Open
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 .github/workflows/coverity-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 11
java-version: 17

- name: Cache Maven packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 11
java-version: 17

- name: Cache Maven packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: 11
java-version: 17

- name: Cache Maven packages
uses: actions/cache@v4
Expand Down
103 changes: 50 additions & 53 deletions README.md

Large diffs are not rendered by default.

14 changes: 2 additions & 12 deletions example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</parent>
<groupId>eu.webeid.example</groupId>
<artifactId>web-eid-springboot-example</artifactId>
<version>3.2.3</version>
<version>4.0.0-SNAPSHOT</version>
<name>web-eid-springboot-example</name>
<description>Example Spring Boot application that demonstrates how to use Web eID for authentication and digital
signing
Expand All @@ -21,10 +21,9 @@
<properties>
<java.version>17</java.version>
<maven-surefire-plugin.version>3.6.0</maven-surefire-plugin.version>
<webeid.version>3.2.2</webeid.version>
<webeid.version>4.0.0-SNAPSHOT</webeid.version>
<digidoc4j.version>6.2.0</digidoc4j.version>
<bouncycastle.version>1.86</bouncycastle.version> <!-- The explicit bcutil dependency can be removed once DigiDoc4j supports BC 1.86. -->
<jmockit.version>1.44</jmockit.version> <!-- Keep version 1.44, otherwise mocking will fail. -->
<jib.version>3.5.2</jib.version>
<maven-enforcer-plugin.version>3.6.3</maven-enforcer-plugin.version>
</properties>
Expand Down Expand Up @@ -83,12 +82,6 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>${jmockit.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

Expand All @@ -102,9 +95,6 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>
-javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
</argLine>
<disableXmlReport>true</disableXmlReport>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public AuthTokenValidator validator(YAMLConfig yamlConfig) {
.withSiteOrigin(URI.create(yamlConfig.getLocalOrigin()))
.withTrustedCertificateAuthorities(loadTrustedCACertificatesFromCerFiles())
.withTrustedCertificateAuthorities(loadTrustedCACertificatesFromTrustStore(yamlConfig))
.withOcspRequestTimeout(yamlConfig.getOcspRequestTimeout())
.build();
} catch (JceException e) {
throw new RuntimeException("Error building the Web eID auth token validator.", e);
Expand All @@ -77,7 +76,7 @@ public YAMLConfig yamlConfig() {
return new YAMLConfig();
}

private X509Certificate[] loadTrustedCACertificatesFromCerFiles() {
X509Certificate[] loadTrustedCACertificatesFromCerFiles() {
List<X509Certificate> caCertificates = new ArrayList<>();

try {
Expand All @@ -87,18 +86,19 @@ private X509Certificate[] loadTrustedCACertificatesFromCerFiles() {
Resource[] resources = resolver.getResources(CERTS_RESOURCE_PATH + activeProfile + "/*.cer");

for (Resource resource : resources) {
X509Certificate caCertificate = (X509Certificate) certFactory.generateCertificate(resource.getInputStream());
caCertificates.add(caCertificate);
try (InputStream stream = resource.getInputStream()) {
caCertificates.add((X509Certificate) certFactory.generateCertificate(stream));
}
}

} catch (CertificateException | IOException e) {
throw new RuntimeException("Error initializing trusted CA certificates.", e);
}

return caCertificates.toArray(new X509Certificate[0]);
return caCertificates.toArray(X509Certificate[]::new);
}

private X509Certificate[] loadTrustedCACertificatesFromTrustStore(YAMLConfig yamlConfig) {
X509Certificate[] loadTrustedCACertificatesFromTrustStore(YAMLConfig yamlConfig) {
List<X509Certificate> caCertificates = new ArrayList<>();

try (InputStream is = ValidationConfiguration.class.getResourceAsStream(CERTS_RESOURCE_PATH + activeProfile + "/" + TRUSTED_CERTIFICATES_JKS)) {
Expand All @@ -118,7 +118,7 @@ private X509Certificate[] loadTrustedCACertificatesFromTrustStore(YAMLConfig yam
throw new RuntimeException("Error initializing trusted CA certificates from trust store.", e);
}

return caCertificates.toArray(new X509Certificate[0]);
return caCertificates.toArray(X509Certificate[]::new);
}


Expand Down
11 changes: 0 additions & 11 deletions example/src/main/java/eu/webeid/example/config/YAMLConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

package eu.webeid.example.config;

import java.time.Duration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -23,8 +22,6 @@ public class YAMLConfig {
@Value("truststore-password")
private String trustStorePassword;

private Duration ocspRequestTimeout = Duration.ofSeconds(5L);

@Value("#{new Boolean('${web-eid-auth-token.validation.use-digidoc4j-prod-configuration}'.trim())}")
private Boolean useDigiDoc4jProdConfiguration;

Expand Down Expand Up @@ -59,12 +56,4 @@ public boolean getUseDigiDoc4jProdConfiguration() {
public void setUseDigiDoc4jProdConfiguration(boolean useDigiDoc4jProdConfiguration) {
this.useDigiDoc4jProdConfiguration = useDigiDoc4jProdConfiguration;
}

public Duration getOcspRequestTimeout() {
return ocspRequestTimeout;
}

public void setOcspRequestTimeout(Duration ocspRequestTimeout) {
this.ocspRequestTimeout = ocspRequestTimeout;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import eu.webeid.security.challenge.ChallengeNonceStore;
import eu.webeid.security.exceptions.AuthTokenException;
import eu.webeid.security.validator.AuthTokenValidator;
import eu.webeid.security.validator.ValidationInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
Expand All @@ -20,12 +21,10 @@
import org.springframework.stereotype.Component;

import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

/**
* Parses JWT from token string inside AuthTokenDTO and attempts authentication.
* Validates the Web eID authentication token supplied in AuthTokenDTO.
*/
@Component
public class AuthTokenDTOAuthenticationProvider implements AuthenticationProvider {
Expand All @@ -46,15 +45,17 @@ public AuthTokenDTOAuthenticationProvider(AuthTokenValidator tokenValidator, Cha
public Authentication authenticate(Authentication auth) throws AuthenticationException {
LOG.info("authenticate(): {}", auth);

final PreAuthenticatedAuthenticationToken authentication = (PreAuthenticatedAuthenticationToken) auth;
final WebEidAuthToken authToken = ((AuthTokenDTO) authentication.getCredentials()).getToken();
if (!(auth.getCredentials() instanceof AuthTokenDTO credentials) || credentials.token() == null) {
throw new AuthenticationServiceException("Authentication token is missing");
}
final WebEidAuthToken authToken = credentials.token();

final List<GrantedAuthority> authorities = Collections.singletonList(USER_ROLE);
final List<GrantedAuthority> authorities = List.of(USER_ROLE);

try {
final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce();
final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce);
return WebEidAuthentication.fromCertificate(userCertificate, authorities);
final ValidationInfo validationInfo = tokenValidator.validate(authToken, nonce);
return WebEidAuthentication.fromCertificate(validationInfo.subjectCertificate(), authorities);
} catch (AuthTokenException e) {
throw new AuthenticationServiceException("Web eID token validation failed", e);
} catch (CertificateEncodingException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package eu.webeid.example.security;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import eu.webeid.example.security.ajax.AjaxAuthenticationFailureHandler;
Expand Down Expand Up @@ -30,7 +31,7 @@

public class WebEidAjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger LOG = LoggerFactory.getLogger(WebEidAjaxLoginProcessingFilter.class);
private final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(AuthTokenDTO.class);
private static final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(AuthTokenDTO.class);
private final SecurityContextRepository securityContextRepository;

public WebEidAjaxLoginProcessingFilter(
Expand Down Expand Up @@ -59,7 +60,15 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
}

LOG.info("attemptAuthentication(): Reading request body");
final AuthTokenDTO authTokenDTO = OBJECT_READER.readValue(request.getReader());
final AuthTokenDTO authTokenDTO;
try {
authTokenDTO = OBJECT_READER.readValue(request.getReader());
} catch (JsonProcessingException e) {
throw new AuthenticationServiceException("Invalid authentication request", e);
}
if (authTokenDTO == null || authTokenDTO.token() == null) {
throw new AuthenticationServiceException("Authentication token is missing");
}
LOG.info("attemptAuthentication(): Creating token");
final PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(null, authTokenDTO);
LOG.info("attemptAuthentication(): Calling authentication manager");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ private static String getPrincipalNameFromCertificate(X509Certificate userCertif

@Override
public boolean equals(Object o) {
if (!super.equals(o)) return false;
WebEidAuthentication that = (WebEidAuthentication) o;
return Objects.equals(idCode, that.idCode);
return o instanceof WebEidAuthentication that
&& super.equals(that)
&& Objects.equals(idCode, that.idCode);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFai
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
final String message = AUTHENTICATION_FAILED + exception.getMessage();
LOG.warn("onAuthenticationFailure(): exception {}, returning {} {}",
exception,
HttpServletResponse.SC_UNAUTHORIZED,
message);
LOG.warn("Authentication failed; returning HTTP 401", exception);
final HttpSession session = request.getSession(false);
if (session != null) {
LOG.info("Invalidating session");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

package eu.webeid.example.security.ajax;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
Expand Down Expand Up @@ -42,19 +41,11 @@ public void onAuthenticationSuccess(
response.getWriter().write(AuthSuccessDTO.asJson(authentication));
}

public static class AuthSuccessDTO {
public record AuthSuccessDTO(String sub, String auth) {
private static final ObjectWriter OBJECT_WRITER = new ObjectMapper().writerFor(AuthSuccessDTO.class);

@JsonProperty("sub")
private String sub;

@JsonProperty("auth")
private String auth;

public static String asJson(Authentication authentication) throws JsonProcessingException {
final AuthSuccessDTO dto = new AuthSuccessDTO();
dto.sub = authentication.getName();
dto.auth = authentication.getAuthorities().toString();
final AuthSuccessDTO dto = new AuthSuccessDTO(authentication.getName(), authentication.getAuthorities().toString());
return OBJECT_WRITER.writeValueAsString(dto);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,5 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import eu.webeid.security.authtoken.WebEidAuthToken;

public class AuthTokenDTO {
@JsonProperty("auth-token")
private WebEidAuthToken token;

public WebEidAuthToken getToken() {
return token;
}

public void setToken(WebEidAuthToken token) {
this.token = token;
}
public record AuthTokenDTO(@JsonProperty("auth-token") WebEidAuthToken token) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,7 @@ public DigestDTO prepareContainer(CertificateDTO certificateDTO, WebEidAuthentic
final byte[] digest = signatureDigestAlgorithm.getDssDigestAlgorithm().getMessageDigest()
.digest(dataToSign.getDataToSign());

final DigestDTO digestDTO = new DigestDTO();
digestDTO.setHash(DatatypeConverter.printBase64Binary(digest));
digestDTO.setHashFunction(digestAlgorithmName);

return digestDTO;
return new DigestDTO(DatatypeConverter.printBase64Binary(digest), digestAlgorithmName);
}

/**
Expand All @@ -127,7 +123,7 @@ public FileDTO signContainer(SignatureDTO signatureDTO) {
Container containerToSign = (Container) Objects.requireNonNull(currentSession().getAttribute(SESSION_ATTR_CONTAINER));
DataToSign dataToSign = (DataToSign) Objects.requireNonNull(currentSession().getAttribute(SESSION_ATTR_DATA));

byte[] signatureBytes = DatatypeConverter.parseBase64Binary(signatureDTO.getBase64Signature());
byte[] signatureBytes = DatatypeConverter.parseBase64Binary(signatureDTO.base64Signature());
Signature signature = dataToSign.finalize(signatureBytes);
containerToSign.addSignature(signature);
currentSession().setAttribute(SESSION_ATTR_CONTAINER, containerToSign);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

package eu.webeid.example.service.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

public class CertificateDTO {

Expand Down Expand Up @@ -41,11 +41,12 @@ public X509Certificate toX509Certificate() throws CertificateException {
return (X509Certificate) cf.generateCertificate(inStream);
}

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public List<String> getSupportedHashFunctionNames() {
return supportedSignatureAlgorithms == null ? new ArrayList<>() : supportedSignatureAlgorithms
return supportedSignatureAlgorithms == null ? List.of() : supportedSignatureAlgorithms
.stream()
.map(SignatureAlgorithmDTO::getHashFunction)
.distinct()
.collect(Collectors.toList());
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,5 @@

package eu.webeid.example.service.dto;

public class ChallengeDTO {
private String nonce;

public String getNonce() {
return nonce;
}

public void setNonce(String nonce) {
this.nonce = nonce;
}
public record ChallengeDTO(String nonce) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,5 @@

package eu.webeid.example.service.dto;

public class DigestDTO {
private String hash;
private String hashFunction;

public String getHash() {
return hash;
}

public void setHash(String hash) {
this.hash = hash;
}

public String getHashFunction() {
return hashFunction;
}

public void setHashFunction(String hashFunction) {
this.hashFunction = hashFunction;
}
public record DigestDTO(String hash, String hashFunction) {
}
Loading