Skip to content

Commit 25dc9d3

Browse files
committed
NFC-82 Review changes
- Refactor Web eID mobile request logic in SigningService - Move Web eID mobile request logic from SigningService into MobleSigningService - Remove addViewControllers() from ApplicationConfiguration and define them in SigningController instead. - Rename /sign/sign paths to /sign/signature - Pass signing certificate instead of auth token into WebEidAuthentication builder.
1 parent cdf3757 commit 25dc9d3

File tree

10 files changed

+194
-151
lines changed

10 files changed

+194
-151
lines changed

example/src/main/java/eu/webeid/example/config/ApplicationConfiguration.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
3232
import org.springframework.context.annotation.Bean;
3333
import org.springframework.context.annotation.Configuration;
34-
import org.springframework.http.HttpMethod;
3534
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
3635
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
3736
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -40,14 +39,12 @@
4039
import org.springframework.security.web.SecurityFilterChain;
4140
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
4241
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
43-
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
44-
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
4542

4643
@Configuration
4744
@ConfigurationPropertiesScan
4845
@EnableWebSecurity
4946
@EnableMethodSecurity(securedEnabled = true)
50-
public class ApplicationConfiguration implements WebMvcConfigurer {
47+
public class ApplicationConfiguration {
5148

5249
@Bean
5350
public SecurityFilterChain filterChain(
@@ -71,10 +68,4 @@ public SecurityFilterChain filterChain(
7168
.headers(h -> h.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
7269
.build();
7370
}
74-
75-
@Override
76-
public void addViewControllers(ViewControllerRegistry registry) {
77-
registry.addViewController("/sign/mobile/certificate").setViewName("welcome");
78-
registry.addViewController("/sign/mobile/signature").setViewName("welcome");
79-
}
8071
}

example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
package eu.webeid.example.security;
2424

2525
import eu.webeid.security.authtoken.SupportedSignatureAlgorithm;
26-
import eu.webeid.security.authtoken.WebEidAuthToken;
2726
import eu.webeid.security.certificate.CertificateData;
2827
import org.springframework.lang.Nullable;
2928
import org.springframework.security.core.Authentication;
@@ -42,39 +41,20 @@ public class WebEidAuthentication extends PreAuthenticatedAuthenticationToken im
4241
private final transient String signingCertificate;
4342
private final transient List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms;
4443

45-
public static Authentication fromCertificate(@Nullable WebEidAuthToken authToken, X509Certificate userCertificate, List<GrantedAuthority> authorities) throws CertificateEncodingException {
46-
final String principalName = getPrincipalNameFromCertificate(userCertificate);
47-
final String idCode = CertificateData.getSubjectIdCode(userCertificate)
48-
.orElseThrow(() -> new CertificateEncodingException("Certificate does not contain subject ID code"));
49-
50-
final String signingCertificate = Optional.ofNullable(authToken)
51-
.map(WebEidAuthToken::getUnverifiedSigningCertificate)
52-
.orElse(null);
53-
54-
final List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms = Optional.ofNullable(authToken)
55-
.map(WebEidAuthToken::getSupportedSignatureAlgorithms)
56-
.orElse(null);
57-
58-
return new WebEidAuthentication(principalName, idCode, signingCertificate, supportedSignatureAlgorithms, authorities);
59-
}
60-
61-
public String getIdCode() {
62-
return idCode;
63-
}
64-
public String getSigningCertificate() {
65-
return signingCertificate;
66-
}
67-
public List<SupportedSignatureAlgorithm> getSupportedSignatureAlgorithms() {
68-
return supportedSignatureAlgorithms;
69-
}
70-
7144
private WebEidAuthentication(String principalName, String idCode, String signingCertificate, List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms, List<GrantedAuthority> authorities) {
7245
super(principalName, idCode, authorities);
7346
this.idCode = idCode;
7447
this.signingCertificate = signingCertificate;
7548
this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
7649
}
7750

51+
public static Authentication fromCertificate(X509Certificate userCertificate, @Nullable String signingCertificate, @Nullable List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms, List<GrantedAuthority> authorities) throws CertificateEncodingException {
52+
final String principalName = getPrincipalNameFromCertificate(userCertificate);
53+
final String idCode = CertificateData.getSubjectIdCode(userCertificate)
54+
.orElseThrow(() -> new CertificateEncodingException("Certificate does not contain subject ID code"));
55+
return new WebEidAuthentication(principalName, idCode, signingCertificate, supportedSignatureAlgorithms, authorities);
56+
}
57+
7858
private static String getPrincipalNameFromCertificate(X509Certificate userCertificate) throws CertificateEncodingException {
7959
final Optional<String> givenName = CertificateData.getSubjectGivenName(userCertificate);
8060
final Optional<String> surname = CertificateData.getSubjectSurname(userCertificate);
@@ -84,10 +64,22 @@ private static String getPrincipalNameFromCertificate(X509Certificate userCertif
8464
} else {
8565
// Organization certificates do not have given name and surname fields.
8666
return CertificateData.getSubjectCN(userCertificate)
87-
.orElseThrow(() -> new CertificateEncodingException("Certificate does not contain subject CN"));
67+
.orElseThrow(() -> new CertificateEncodingException("Certificate does not contain subject CN"));
8868
}
8969
}
9070

71+
public String getIdCode() {
72+
return idCode;
73+
}
74+
75+
public String getSigningCertificate() {
76+
return signingCertificate;
77+
}
78+
79+
public List<SupportedSignatureAlgorithm> getSupportedSignatureAlgorithms() {
80+
return supportedSignatureAlgorithms;
81+
}
82+
9183
@Override
9284
public boolean equals(Object o) {
9385
if (!super.equals(o)) return false;

example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package eu.webeid.example.security;
2424

25+
import eu.webeid.security.authtoken.SupportedSignatureAlgorithm;
2526
import eu.webeid.security.authtoken.WebEidAuthToken;
2627
import eu.webeid.security.challenge.ChallengeNonceStore;
2728
import eu.webeid.security.exceptions.AuthTokenException;
@@ -41,6 +42,7 @@
4142
import java.security.cert.X509Certificate;
4243
import java.util.Collections;
4344
import java.util.List;
45+
import java.util.Optional;
4446

4547
/**
4648
* Parses JWT from token string inside AuthTokenDTO and attempts authentication.
@@ -72,7 +74,14 @@ public Authentication authenticate(Authentication auth) throws AuthenticationExc
7274
try {
7375
final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce();
7476
final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce);
75-
return WebEidAuthentication.fromCertificate(authToken, userCertificate, authorities);
77+
final String signingCertificate = Optional.ofNullable(authToken)
78+
.map(WebEidAuthToken::getUnverifiedSigningCertificate)
79+
.orElse(null);
80+
final List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms = Optional.ofNullable(authToken)
81+
.map(WebEidAuthToken::getSupportedSignatureAlgorithms)
82+
.orElse(null);
83+
84+
return WebEidAuthentication.fromCertificate(userCertificate, signingCertificate, supportedSignatureAlgorithms, authorities);
7685
} catch (AuthTokenException e) {
7786
throw new AuthenticationServiceException("Web eID token validation failed", e);
7887
} catch (CertificateEncodingException e) {
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (c) 2020-2025 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.example.service;
24+
25+
import com.fasterxml.jackson.annotation.JsonInclude;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.ObjectWriter;
28+
import eu.webeid.example.config.WebEidMobileProperties;
29+
import eu.webeid.example.security.WebEidAuthentication;
30+
import eu.webeid.example.service.dto.CertificateDTO;
31+
import eu.webeid.example.service.dto.DigestDTO;
32+
import eu.webeid.example.service.dto.SignatureAlgorithmDTO;
33+
import eu.webeid.security.authtoken.SupportedSignatureAlgorithm;
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
import org.springframework.stereotype.Component;
37+
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
38+
import org.springframework.web.util.UriComponents;
39+
import org.springframework.web.util.UriComponentsBuilder;
40+
41+
import java.io.IOException;
42+
import java.security.NoSuchAlgorithmException;
43+
import java.security.cert.CertificateException;
44+
import java.util.Base64;
45+
import java.util.List;
46+
47+
@Component
48+
public class MobleSigningService {
49+
public static final String CERTIFICATE_RESPONSE_PATH = "/sign/mobile/certificate";
50+
public static final String SIGNATURE_RESPONSE_PATH = "/sign/mobile/signature";
51+
public static final String WEB_EID_MOBILE_SIGN_PATH = "/sign";
52+
public static final String WEB_EID_GET_CERT_PATH = "/cert";
53+
private static final Logger LOG = LoggerFactory.getLogger(MobleSigningService.class);
54+
private static final ObjectWriter OBJECT_WRITER = new ObjectMapper().writerFor(RequestObject.class);
55+
private final SigningService signingService;
56+
private final WebEidMobileProperties webEidMobileProperties;
57+
58+
public MobleSigningService(SigningService signingService, WebEidMobileProperties webEidMobileProperties) {
59+
this.signingService = signingService;
60+
this.webEidMobileProperties = webEidMobileProperties;
61+
}
62+
63+
public MobileInitRequest initSigningRequest(WebEidAuthentication authentication) throws IOException, CertificateException, NoSuchAlgorithmException {
64+
String signingCertificate = authentication.getSigningCertificate();
65+
List<SupportedSignatureAlgorithm> supportedSignatureAlgorithms = authentication.getSupportedSignatureAlgorithms();
66+
if (signingCertificate == null || supportedSignatureAlgorithms == null) {
67+
return initSigningRequest(authentication, null);
68+
}
69+
CertificateDTO certificateDTO = new CertificateDTO();
70+
certificateDTO.setCertificate(signingCertificate);
71+
certificateDTO.setSupportedSignatureAlgorithms(mapSupportedAlgorithms(supportedSignatureAlgorithms));
72+
return initSigningRequest(authentication, certificateDTO);
73+
}
74+
75+
@SuppressWarnings("javax.annotation.Tainted")
76+
public MobileInitRequest initSigningRequest(WebEidAuthentication authentication, CertificateDTO certificateDTO) throws IOException, CertificateException, NoSuchAlgorithmException {
77+
if (certificateDTO != null) {
78+
LOG.info("Initiating signing request");
79+
DigestDTO digest = signingService.prepareContainer(certificateDTO, authentication);
80+
String responseUri = ServletUriComponentsBuilder.fromCurrentContextPath()
81+
.path(SIGNATURE_RESPONSE_PATH)
82+
.build()
83+
.toUriString();
84+
RequestObject initRequest = new RequestObject(responseUri, certificateDTO.getCertificate(), digest.getHash(), digest.getHashFunction());
85+
String payloadJson = OBJECT_WRITER.writeValueAsString(initRequest);
86+
String encoded = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.getBytes());
87+
UriComponents requestUri = UriComponentsBuilder.fromUriString(webEidMobileProperties.baseRequestUri())
88+
.path(WEB_EID_MOBILE_SIGN_PATH)
89+
.fragment(encoded)
90+
.build();
91+
return new MobileInitRequest(requestUri.toUriString());
92+
} else {
93+
LOG.info("Initiating get certificate request");
94+
String responseUri = ServletUriComponentsBuilder.fromCurrentContextPath()
95+
.path(CERTIFICATE_RESPONSE_PATH)
96+
.build()
97+
.toUriString();
98+
RequestObject initRequest = new RequestObject(responseUri, null, null, null);
99+
String payloadJson = OBJECT_WRITER.writeValueAsString(initRequest);
100+
String encoded = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.getBytes());
101+
UriComponents requestUri = UriComponentsBuilder.fromUriString(webEidMobileProperties.baseRequestUri())
102+
.path(WEB_EID_GET_CERT_PATH)
103+
.fragment(encoded)
104+
.build();
105+
return new MobileInitRequest(requestUri.toUriString());
106+
}
107+
}
108+
109+
private List<SignatureAlgorithmDTO> mapSupportedAlgorithms(List<SupportedSignatureAlgorithm> algorithms) {
110+
return algorithms.stream().map(ssa -> {
111+
var dto = new SignatureAlgorithmDTO();
112+
dto.setCryptoAlgorithm(ssa.getCryptoAlgorithm());
113+
dto.setHashFunction(ssa.getHashFunction());
114+
dto.setPaddingScheme(ssa.getPaddingScheme());
115+
return dto;
116+
}).toList();
117+
}
118+
119+
public record MobileInitRequest(
120+
String requestUri) {
121+
}
122+
123+
@JsonInclude(JsonInclude.Include.NON_NULL)
124+
record RequestObject(
125+
String responseUri,
126+
String signingCertificate,
127+
String hash,
128+
String hashFunction) {
129+
}
130+
}

0 commit comments

Comments
 (0)