From bf9c108f851b9d53e5f39156a0b3850ba5f9ffbf Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:27:39 +0200 Subject: [PATCH 1/6] NFC-115 Implement minimal multiple signing certification solution Signed-off-by: Sander Kondratjev --- .../WebEidAuthenticationProvider.java | 2 +- .../example/service/SigningService.java | 2 +- .../security/authtoken/WebEidAuthToken.java | 10 +++++----- .../AuthTokenVersion11Validator.java | 19 ++++++++++++++----- .../AuthTokenV11CertificateTest.java | 6 +++--- .../AuthTokenVersion11ValidatorTest.java | 4 ++-- 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java index 032564fb..75144e5d 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java @@ -77,7 +77,7 @@ public Authentication authenticate(Authentication auth) throws AuthenticationExc final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce(); final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce); final String signingCertificate = requireSigningCert - ? authToken.getUnverifiedSigningCertificate() + ? authToken.getUnverifiedSigningCertificates().get(0) : null; final List supportedSignatureAlgorithms = requireSigningCert ? authToken.getSupportedSignatureAlgorithms() diff --git a/example/src/main/java/eu/webeid/example/service/SigningService.java b/example/src/main/java/eu/webeid/example/service/SigningService.java index b1202dea..1c01590e 100644 --- a/example/src/main/java/eu/webeid/example/service/SigningService.java +++ b/example/src/main/java/eu/webeid/example/service/SigningService.java @@ -116,7 +116,7 @@ public DigestDTO prepareContainer(CertificateDTO certificateDTO, WebEidAuthentic final DataToSign dataToSign = SignatureBuilder .aSignature(containerToSign) - .withSignatureProfile(SignatureProfile.LT) // AIA OCSP is supported for signatures with LT or LTA profile. + .withSignatureProfile(SignatureProfile.T) // AIA OCSP is supported for signatures with LT or LTA profile. .withSigningCertificate(certificate) .withSignatureDigestAlgorithm(signatureDigestAlgorithm) .buildDataToSign(); diff --git a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java index 3ba0b8a8..6aaf24aa 100644 --- a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java +++ b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java @@ -34,7 +34,7 @@ public class WebEidAuthToken { private String algorithm; private String format; - private String unverifiedSigningCertificate; + private List unverifiedSigningCertificates; private List supportedSignatureAlgorithms; public String getUnverifiedCertificate() { @@ -69,12 +69,12 @@ public void setFormat(String format) { this.format = format; } - public String getUnverifiedSigningCertificate() { - return unverifiedSigningCertificate; + public List getUnverifiedSigningCertificates() { + return unverifiedSigningCertificates; } - public void setUnverifiedSigningCertificate(String unverifiedSigningCertificate) { - this.unverifiedSigningCertificate = unverifiedSigningCertificate; + public void setUnverifiedSigningCertificates(List unverifiedSigningCertificates) { + this.unverifiedSigningCertificates = unverifiedSigningCertificates; } public List getSupportedSignatureAlgorithms() { diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java index 8770abb1..74af04e8 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -90,7 +90,7 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { final X509Certificate subjectCertificate = validateV1(token, currentChallengeNonce); - final X509Certificate signingCertificate = validateSigningCertificateExists(token); + final X509Certificate signingCertificate = validateSigningCertificatesExist(token); validateSupportedSignatureAlgorithms(token.getSupportedSignatureAlgorithms()); validateSameSubject(subjectCertificate, signingCertificate); validateSameIssuer(subjectCertificate, signingCertificate); @@ -116,11 +116,20 @@ private static void validateSupportedSignatureAlgorithms(List signingCertificates = token.getUnverifiedSigningCertificates(); + + if (signingCertificates == null || signingCertificates.isEmpty()) { + throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } - return CertificateLoader.decodeCertificateFromBase64(token.getUnverifiedSigningCertificate()); + + String signingCertificate = signingCertificates.get(0); + + if (isNullOrEmpty(signingCertificate)) { + throw new AuthTokenParseException("'unverifiedSigningCertificates[0]' is null or empty"); + } + + return CertificateLoader.decodeCertificateFromBase64(signingCertificate); } private static void validateSameSubject(X509Certificate subjectCertificate, X509Certificate signingCertificate) diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java index eb97b5d6..bd7c8a15 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java @@ -187,7 +187,7 @@ void whenV11SigningCertificateNotIssuedBySameAuthority_thenValidationFails() thr try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -210,7 +210,7 @@ void whenV11SigningCertificateHasNoAuthorityKeyIdentifier_thenValidationFails() try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -236,7 +236,7 @@ void whenV11SigningCertificateNotSuitableForSigning_thenValidationFails() throws try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) .thenReturn(signingCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java index c311363b..deb17f54 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java @@ -94,7 +94,7 @@ void whenFormatIsNullEmptyOrNotV11_thenSupportsReturnsFalse(String format) { void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); - when(token.getUnverifiedSigningCertificate()).thenReturn(null); + when(token.getUnverifiedSigningCertificates().get(0)).thenReturn(null); AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); doReturn(mock(X509Certificate.class)).when(spyValidator).validateV1(any(), any()); @@ -108,7 +108,7 @@ void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Except void whenSupportedSignatureAlgorithmsMissing_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); - when(token.getUnverifiedSigningCertificate()).thenReturn("abc"); + when(token.getUnverifiedSigningCertificates().get(0)).thenReturn("abc"); when(token.getSupportedSignatureAlgorithms()).thenReturn(null); AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); From b5d78d9f3cc8a4a9c152bd3a68df3c820f9a8347 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:44:18 +0200 Subject: [PATCH 2/6] NFC-115 Fix tests Signed-off-by: Sander Kondratjev --- .../versionvalidators/AuthTokenVersion11Validator.java | 2 +- .../security/testutil/AbstractTestWithValidator.java | 2 +- .../versionvalidators/AuthTokenV11CertificateTest.java | 8 ++++---- .../AuthTokenVersion11ValidatorTest.java | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java index 74af04e8..ad275e4c 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -126,7 +126,7 @@ private static X509Certificate validateSigningCertificatesExist(WebEidAuthToken String signingCertificate = signingCertificates.get(0); if (isNullOrEmpty(signingCertificate)) { - throw new AuthTokenParseException("'unverifiedSigningCertificates[0]' is null or empty"); + throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } return CertificateLoader.decodeCertificateFromBase64(signingCertificate); diff --git a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java index ad5d400b..43af635e 100644 --- a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java +++ b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java @@ -46,7 +46,7 @@ public abstract class AbstractTestWithValidator { public static final String VALID_V11_AUTH_TOKEN = "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"unverifiedSigningCertificates\":[\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"]," + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"0Ov7ME6pTY1K2GXMj8Wxov/o2fGIMEds8OMY5dKdkB0nrqQX7fG1E5mnsbvyHpMDecMUH6Yg+p1HXdgB/lLqOcFZjt/OVXPjAAApC5d1YgRYATDcxsR1zqQwiNcHdmWn\"," + diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java index bd7c8a15..f9f36c0f 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java @@ -64,7 +64,7 @@ class AuthTokenV11CertificateTest extends AbstractTestWithValidator { private static final String V11_AUTH_TOKEN = "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificate\":\"X5C\"," + + "\"unverifiedSigningCertificates\":[\"X5C\"]," + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"xsjXsQvVYXWcdV0YPhxLthJxtf0//R8p9WFFlYJGRARrl1ruyoAUwl0xeHgeZOKeJtwiCYCNWJzCG3VM3ydgt92bKhhk1u0JXIPVqvOkmDY72OCN4q73Y8iGSPVTgjk93TgquHlodf7YcqZNhutwNNf3oldHEWJD5zmkdwdpBFXgeOwTAdFwGljDQZbHr3h1Dr+apUDuloS0WuIzUuu8YXN2b8lh8FCTlF0G0DEjhHd/MGx8dbe3UTLHmD7K9DXv4zLJs6EF9i2v/C10SIBQDkPBSVPqMxCDPECjbEPi2+ds94eU7ThOhOQlFFtJ4KjQNTUa2crSixH7cYZF2rNNmA==\"," + @@ -115,7 +115,7 @@ void whenValidV11Token_thenValidationSucceeds() { void whenV11SigningCertificateFieldIsMissing_thenValidationFails() throws Exception { ObjectMapper mapper = new ObjectMapper(); ObjectNode node = (ObjectNode) mapper.readTree(V11_AUTH_TOKEN); - node.remove("unverifiedSigningCertificate"); + node.remove("unverifiedSigningCertificates"); WebEidAuthToken token = OBJECT_READER.readValue(node.toString()); AuthTokenVersion11Validator spyValidator = spyAuthTokenVersion11Validator(); @@ -123,7 +123,7 @@ void whenV11SigningCertificateFieldIsMissing_thenValidationFails() throws Except assertThatThrownBy(() -> spyValidator.validate(token, VALID_CHALLENGE_NONCE)) .isInstanceOf(AuthTokenParseException.class) - .hasMessage("'unverifiedSigningCertificate' field is missing, null or empty for format 'web-eid:1.1'"); + .hasMessage("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } @Test @@ -260,7 +260,7 @@ private AuthTokenVersion11Validator spyAuthTokenVersion11Validator() { private static WebEidAuthToken getWebEidAuthToken(String cert) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); ObjectNode node = (ObjectNode) mapper.readTree(V11_AUTH_TOKEN); - node.put("unverifiedSigningCertificate", cert); + node.putArray("unverifiedSigningCertificates").add(cert); return OBJECT_READER.readValue(node.toString()); } diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java index deb17f54..ed5a6679 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java @@ -94,21 +94,21 @@ void whenFormatIsNullEmptyOrNotV11_thenSupportsReturnsFalse(String format) { void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); - when(token.getUnverifiedSigningCertificates().get(0)).thenReturn(null); + when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList(null)); AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); doReturn(mock(X509Certificate.class)).when(spyValidator).validateV1(any(), any()); assertThatThrownBy(() -> spyValidator.validate(token, "nonce")) .isInstanceOf(AuthTokenParseException.class) - .hasMessage("'unverifiedSigningCertificate' field is missing, null or empty for format 'web-eid:1.1'"); + .hasMessage("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } @Test void whenSupportedSignatureAlgorithmsMissing_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); - when(token.getUnverifiedSigningCertificates().get(0)).thenReturn("abc"); + when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList("abc")); when(token.getSupportedSignatureAlgorithms()).thenReturn(null); AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); From a6ef18e18a5979a80f6d94efd3821d9a84a11b7d Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:46:30 +0200 Subject: [PATCH 3/6] NFC-115 Improve method Signed-off-by: Sander Kondratjev --- .../versionvalidators/AuthTokenVersion11Validator.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java index ad275e4c..8184b207 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -119,17 +119,11 @@ private static void validateSupportedSignatureAlgorithms(List signingCertificates = token.getUnverifiedSigningCertificates(); - if (signingCertificates == null || signingCertificates.isEmpty()) { + if (signingCertificates == null || signingCertificates.isEmpty() || isNullOrEmpty(signingCertificates.get(0))) { throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } - String signingCertificate = signingCertificates.get(0); - - if (isNullOrEmpty(signingCertificate)) { - throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); - } - - return CertificateLoader.decodeCertificateFromBase64(signingCertificate); + return CertificateLoader.decodeCertificateFromBase64(signingCertificates.get(0)); } private static void validateSameSubject(X509Certificate subjectCertificate, X509Certificate signingCertificate) From 258a34377a5d9ba5ec61202f175d4b01e64bded6 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 11:05:52 +0200 Subject: [PATCH 4/6] NFC-115 Example test fix Signed-off-by: Sander Kondratjev --- .../src/test/java/eu/webeid/example/testutil/ObjectMother.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java index fc795471..029e4592 100644 --- a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -68,7 +68,7 @@ public class ObjectMother { VALID_WEB_EID_1_1AUTH_TOKEN = MAPPER.readValue( "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"unverifiedSigningCertificates\":[\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"]," + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + "\"issuerApp\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"0Ov7ME6pTY1K2GXMj8Wxov/o2fGIMEds8OMY5dKdkB0nrqQX7fG1E5mnsbvyHpMDecMUH6Yg+p1HXdgB/lLqOcFZjt/OVXPjAAApC5d1YgRYATDcxsR1zqQwiNcHdmWn\"," + From de7aa698467de12784e9cead64370b09fb31d9ec Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Tue, 17 Mar 2026 13:06:19 +0200 Subject: [PATCH 5/6] NFC-115 Add support for supportedSignatureAlgorithms Signed-off-by: Sander Kondratjev --- .../WebEidAuthenticationProvider.java | 9 ++-- .../webeid/example/testutil/ObjectMother.java | 6 ++- .../security/authtoken/WebEidAuthToken.java | 16 ++---- .../UnverifiedSigningCertificate.java | 49 +++++++++++++++++++ .../AuthTokenVersion11Validator.java | 41 ++++++++++------ .../testutil/AbstractTestWithValidator.java | 6 ++- .../AuthTokenV11CertificateTest.java | 20 +++++--- .../AuthTokenVersion11ValidatorTest.java | 9 +++- 8 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java diff --git a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java index 75144e5d..d5d2d01e 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java @@ -76,11 +76,12 @@ public Authentication authenticate(Authentication auth) throws AuthenticationExc try { final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce(); final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce); - final String signingCertificate = requireSigningCert - ? authToken.getUnverifiedSigningCertificates().get(0) + boolean isV11 = authToken.getFormat() != null && authToken.getFormat().startsWith("web-eid:1.1"); + final String signingCertificate = (requireSigningCert && isV11) + ? authToken.getUnverifiedSigningCertificates().get(0).getUnverifiedSigningCertificate() : null; - final List supportedSignatureAlgorithms = requireSigningCert - ? authToken.getSupportedSignatureAlgorithms() + final List supportedSignatureAlgorithms = (requireSigningCert && isV11) + ? authToken.getUnverifiedSigningCertificates().get(0).getSupportedSignatureAlgorithms() : null; return WebEidAuthentication.fromCertificate(userCertificate, signingCertificate, supportedSignatureAlgorithms, authorities); diff --git a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java index 029e4592..264e640f 100644 --- a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -68,8 +68,10 @@ public class ObjectMother { VALID_WEB_EID_1_1AUTH_TOKEN = MAPPER.readValue( "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificates\":[\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"]," + - "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + + "\"unverifiedSigningCertificates\":[{" + + "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]" + + "}]," + "\"issuerApp\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"0Ov7ME6pTY1K2GXMj8Wxov/o2fGIMEds8OMY5dKdkB0nrqQX7fG1E5mnsbvyHpMDecMUH6Yg+p1HXdgB/lLqOcFZjt/OVXPjAAApC5d1YgRYATDcxsR1zqQwiNcHdmWn\"," + "\"format\":\"web-eid:1.1\"}", diff --git a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java index 6aaf24aa..1e013b0b 100644 --- a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java +++ b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java @@ -23,6 +23,7 @@ package eu.webeid.security.authtoken; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import eu.webeid.security.certificate.UnverifiedSigningCertificate; import java.util.List; @@ -34,8 +35,7 @@ public class WebEidAuthToken { private String algorithm; private String format; - private List unverifiedSigningCertificates; - private List supportedSignatureAlgorithms; + private List unverifiedSigningCertificates; public String getUnverifiedCertificate() { return unverifiedCertificate; @@ -69,19 +69,11 @@ public void setFormat(String format) { this.format = format; } - public List getUnverifiedSigningCertificates() { + public List getUnverifiedSigningCertificates() { return unverifiedSigningCertificates; } - public void setUnverifiedSigningCertificates(List unverifiedSigningCertificates) { + public void setUnverifiedSigningCertificates(List unverifiedSigningCertificates) { this.unverifiedSigningCertificates = unverifiedSigningCertificates; } - - public List getSupportedSignatureAlgorithms() { - return supportedSignatureAlgorithms; - } - - public void setSupportedSignatureAlgorithms(List supportedSignatureAlgorithms) { - this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; - } } diff --git a/src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java b/src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java new file mode 100644 index 00000000..0ee65957 --- /dev/null +++ b/src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020-2025 Estonian Information System Authority + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package eu.webeid.security.certificate; + +import eu.webeid.security.authtoken.SupportedSignatureAlgorithm; + +import java.util.List; + +public class UnverifiedSigningCertificate { + + private String unverifiedSigningCertificate; + private List supportedSignatureAlgorithms; + + public String getUnverifiedSigningCertificate() { + return unverifiedSigningCertificate; + } + + public void setUnverifiedSigningCertificate(String unverifiedSigningCertificate) { + this.unverifiedSigningCertificate = unverifiedSigningCertificate; + } + + public List getSupportedSignatureAlgorithms() { + return supportedSignatureAlgorithms; + } + + public void setSupportedSignatureAlgorithms(List supportedSignatureAlgorithms) { + this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; + } +} diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java index 8184b207..c6daf3e4 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -25,6 +25,7 @@ import eu.webeid.security.authtoken.SupportedSignatureAlgorithm; import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.certificate.CertificateLoader; +import eu.webeid.security.certificate.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenException; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.exceptions.CertificateDecodingException; @@ -91,7 +92,7 @@ protected String getSupportedFormatPrefix() { public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { final X509Certificate subjectCertificate = validateV1(token, currentChallengeNonce); final X509Certificate signingCertificate = validateSigningCertificatesExist(token); - validateSupportedSignatureAlgorithms(token.getSupportedSignatureAlgorithms()); + validateSupportedSignatureAlgorithms(token.getUnverifiedSigningCertificates()); validateSameSubject(subjectCertificate, signingCertificate); validateSameIssuer(subjectCertificate, signingCertificate); validateSigningCertificateValidity(signingCertificate); @@ -100,30 +101,40 @@ public X509Certificate validate(WebEidAuthToken token, String currentChallengeNo return subjectCertificate; } - private static void validateSupportedSignatureAlgorithms(List algorithms) throws AuthTokenParseException { - if (algorithms == null || algorithms.isEmpty()) { - throw new AuthTokenParseException("'supportedSignatureAlgorithms' field is missing"); - } + private static void validateSupportedSignatureAlgorithms(List unverifiedSigningCertificates) throws AuthTokenParseException { + for (UnverifiedSigningCertificate cert : unverifiedSigningCertificates) { + List algorithms = cert.getSupportedSignatureAlgorithms(); - boolean hasInvalid = algorithms.stream().anyMatch(supportedSignatureAlgorithm -> - !SUPPORTED_SIGNING_CRYPTO_ALGORITHMS.contains(supportedSignatureAlgorithm.getCryptoAlgorithm()) || - !SUPPORTED_SIGNING_HASH_FUNCTIONS.contains(supportedSignatureAlgorithm.getHashFunction()) || - !SUPPORTED_SIGNING_PADDING_SCHEMES.contains(supportedSignatureAlgorithm.getPaddingScheme()) - ); + if (algorithms == null || algorithms.isEmpty()) { + throw new AuthTokenParseException("'supportedSignatureAlgorithms' field is missing"); + } - if (hasInvalid) { - throw new AuthTokenParseException("Unsupported signature algorithm"); + boolean hasInvalid = algorithms.stream().anyMatch(algorithm -> + !SUPPORTED_SIGNING_CRYPTO_ALGORITHMS.contains(algorithm.getCryptoAlgorithm()) || + !SUPPORTED_SIGNING_HASH_FUNCTIONS.contains(algorithm.getHashFunction()) || + !SUPPORTED_SIGNING_PADDING_SCHEMES.contains(algorithm.getPaddingScheme()) + ); + + if (hasInvalid) { + throw new AuthTokenParseException("Unsupported signature algorithm"); + } } } private static X509Certificate validateSigningCertificatesExist(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { - List signingCertificates = token.getUnverifiedSigningCertificates(); + List signingCertificates = token.getUnverifiedSigningCertificates(); + + if (signingCertificates == null || signingCertificates.isEmpty()) { + throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + } + + UnverifiedSigningCertificate signingCertificate = signingCertificates.get(0); - if (signingCertificates == null || signingCertificates.isEmpty() || isNullOrEmpty(signingCertificates.get(0))) { + if (signingCertificate == null || isNullOrEmpty(signingCertificate.getUnverifiedSigningCertificate())) { throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); } - return CertificateLoader.decodeCertificateFromBase64(signingCertificates.get(0)); + return CertificateLoader.decodeCertificateFromBase64(signingCertificate.getUnverifiedSigningCertificate()); } private static void validateSameSubject(X509Certificate subjectCertificate, X509Certificate signingCertificate) diff --git a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java index 43af635e..665ff03f 100644 --- a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java +++ b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java @@ -46,8 +46,10 @@ public abstract class AbstractTestWithValidator { public static final String VALID_V11_AUTH_TOKEN = "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificates\":[\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"]," + - "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + + "\"unverifiedSigningCertificates\":[{" + + "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]" + + "}]," + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"0Ov7ME6pTY1K2GXMj8Wxov/o2fGIMEds8OMY5dKdkB0nrqQX7fG1E5mnsbvyHpMDecMUH6Yg+p1HXdgB/lLqOcFZjt/OVXPjAAApC5d1YgRYATDcxsR1zqQwiNcHdmWn\"," + "\"format\":\"web-eid:1.1\"}"; diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java index f9f36c0f..d4e658fa 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.certificate.CertificateLoader; @@ -64,8 +65,10 @@ class AuthTokenV11CertificateTest extends AbstractTestWithValidator { private static final String V11_AUTH_TOKEN = "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + - "\"unverifiedSigningCertificates\":[\"X5C\"]," + - "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + + "\"unverifiedSigningCertificates\":[{" + + "\"unverifiedSigningCertificate\":\"X5C\"," + + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]" + + "}]," + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"xsjXsQvVYXWcdV0YPhxLthJxtf0//R8p9WFFlYJGRARrl1ruyoAUwl0xeHgeZOKeJtwiCYCNWJzCG3VM3ydgt92bKhhk1u0JXIPVqvOkmDY72OCN4q73Y8iGSPVTgjk93TgquHlodf7YcqZNhutwNNf3oldHEWJD5zmkdwdpBFXgeOwTAdFwGljDQZbHr3h1Dr+apUDuloS0WuIzUuu8YXN2b8lh8FCTlF0G0DEjhHd/MGx8dbe3UTLHmD7K9DXv4zLJs6EF9i2v/C10SIBQDkPBSVPqMxCDPECjbEPi2+ds94eU7ThOhOQlFFtJ4KjQNTUa2crSixH7cYZF2rNNmA==\"," + "\"format\":\"web-eid:1.1\"}"; @@ -187,7 +190,7 @@ void whenV11SigningCertificateNotIssuedBySameAuthority_thenValidationFails() thr try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0).getUnverifiedSigningCertificate())) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -210,7 +213,7 @@ void whenV11SigningCertificateHasNoAuthorityKeyIdentifier_thenValidationFails() try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0).getUnverifiedSigningCertificate())) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -236,7 +239,7 @@ void whenV11SigningCertificateNotSuitableForSigning_thenValidationFails() throws try (MockedStatic mocked = mockStatic(CertificateLoader.class)) { mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedCertificate())) .thenReturn(realSubjectCert); - mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0))) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().get(0).getUnverifiedSigningCertificate())) .thenReturn(signingCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -260,7 +263,12 @@ private AuthTokenVersion11Validator spyAuthTokenVersion11Validator() { private static WebEidAuthToken getWebEidAuthToken(String cert) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); ObjectNode node = (ObjectNode) mapper.readTree(V11_AUTH_TOKEN); - node.putArray("unverifiedSigningCertificates").add(cert); + + ArrayNode certs = (ArrayNode) node.get("unverifiedSigningCertificates"); + ObjectNode certNode = (ObjectNode) certs.get(0); + + certNode.put("unverifiedSigningCertificate", cert); + return OBJECT_READER.readValue(node.toString()); } diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java index ed5a6679..cee401d4 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java @@ -24,6 +24,7 @@ import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.certificate.CertificateLoader; +import eu.webeid.security.certificate.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.validator.AuthTokenSignatureValidator; import eu.webeid.security.validator.AuthTokenValidationConfiguration; @@ -108,8 +109,12 @@ void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Except void whenSupportedSignatureAlgorithmsMissing_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); - when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList("abc")); - when(token.getSupportedSignatureAlgorithms()).thenReturn(null); + + UnverifiedSigningCertificate certificate = new UnverifiedSigningCertificate(); + certificate.setUnverifiedSigningCertificate("abc"); + certificate.setSupportedSignatureAlgorithms(null); + + when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList(certificate)); AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); doReturn(mock(X509Certificate.class)).when(spyValidator).validateV1(any(), any()); From 3cc64da27afa02129d8274dfd5df0510c109837b Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Tue, 17 Mar 2026 13:11:12 +0200 Subject: [PATCH 6/6] NFC-115 Fix tests Signed-off-by: Sander Kondratjev --- .../security/testutil/AbstractTestWithValidator.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java index 665ff03f..53a0ff28 100644 --- a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java +++ b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java @@ -23,6 +23,7 @@ package eu.webeid.security.testutil; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.exceptions.AuthTokenException; @@ -78,7 +79,12 @@ protected WebEidAuthToken replaceTokenField(String token, String field, String v protected WebEidAuthToken removeJsonField() throws Exception { ObjectMapper mapper = new ObjectMapper(); ObjectNode node = (ObjectNode) mapper.readTree(AbstractTestWithValidator.VALID_V11_AUTH_TOKEN); - node.remove("supportedSignatureAlgorithms"); + + ArrayNode certs = (ArrayNode) node.get("unverifiedSigningCertificates"); + ObjectNode certNode = (ObjectNode) certs.get(0); + + certNode.remove("supportedSignatureAlgorithms"); + return validator.parse(mapper.writeValueAsString(node)); } }