From c16b7b6c04313a47229e5c72780eaf0b53b89ac0 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:27:39 +0200 Subject: [PATCH 1/9] 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 7a0c18a442155f86a29726eae810a26bc263e462 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:44:18 +0200 Subject: [PATCH 2/9] 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 5cceec5dc88889375b23ad700a8cd4b4cc0edb70 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 10:46:30 +0200 Subject: [PATCH 3/9] 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 51883c658b4fe5e3198a46778a9a44226beb6b7b Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Wed, 11 Mar 2026 11:05:52 +0200 Subject: [PATCH 4/9] 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 1499ca7d714112f1bef6fb6381cf2347e3900a4b Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Tue, 17 Mar 2026 13:06:19 +0200 Subject: [PATCH 5/9] 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 90e918684e6cdcb37516743de65dde12f984ea49 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Tue, 17 Mar 2026 13:11:12 +0200 Subject: [PATCH 6/9] 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)); } } From cdda3be8bcc58fe6b0a1f6edb4e364e040ea6e6b Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Thu, 19 Mar 2026 13:22:23 +0200 Subject: [PATCH 7/9] NFC-115 Review findings Signed-off-by: Sander Kondratjev --- .../security/WebEidAuthentication.java | 11 +++- .../WebEidAuthenticationProvider.java | 11 +--- .../example/service/SigningService.java | 2 +- .../security/WebEidAuthenticationTest.java | 2 +- .../webeid/example/testutil/ObjectMother.java | 2 +- .../UnverifiedSigningCertificate.java | 6 +- .../security/authtoken/WebEidAuthToken.java | 1 - .../AuthTokenVersion11Validator.java | 61 +++++++++++-------- .../AuthTokenVersion1Validator.java | 4 ++ .../AuthTokenVersion11ValidatorTest.java | 2 +- .../AuthTokenVersion1ValidatorTest.java | 14 +++++ 11 files changed, 73 insertions(+), 43 deletions(-) rename src/main/java/eu/webeid/security/{certificate => authtoken}/UnverifiedSigningCertificate.java (93%) diff --git a/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java b/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java index 5ab1a8b5..4c779ded 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java @@ -23,6 +23,7 @@ package eu.webeid.example.security; import eu.webeid.security.authtoken.SupportedSignatureAlgorithm; +import eu.webeid.security.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.certificate.CertificateData; import org.springframework.lang.Nullable; import org.springframework.security.core.Authentication; @@ -48,11 +49,17 @@ private WebEidAuthentication(String principalName, String idCode, String signing this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; } - public static Authentication fromCertificate(X509Certificate userCertificate, @Nullable String signingCertificate, @Nullable List supportedSignatureAlgorithms, List authorities) throws CertificateEncodingException { + public static Authentication fromCertificate(X509Certificate userCertificate, @Nullable UnverifiedSigningCertificate signingCertificate, List authorities) throws CertificateEncodingException { final String principalName = getPrincipalNameFromCertificate(userCertificate); final String idCode = CertificateData.getSubjectIdCode(userCertificate) .orElseThrow(() -> new CertificateEncodingException("Certificate does not contain subject ID code")); - return new WebEidAuthentication(principalName, idCode, signingCertificate, supportedSignatureAlgorithms, authorities); + return new WebEidAuthentication( + principalName, + idCode, + signingCertificate != null ? signingCertificate.getUnverifiedSigningCertificate() : null, + signingCertificate != null ? signingCertificate.getSupportedSignatureAlgorithms() : null, + authorities + ); } private static String getPrincipalNameFromCertificate(X509Certificate userCertificate) throws CertificateEncodingException { 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 d5d2d01e..fbbe43ef 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java @@ -76,15 +76,10 @@ public Authentication authenticate(Authentication auth) throws AuthenticationExc try { final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce(); final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce); - boolean isV11 = authToken.getFormat() != null && authToken.getFormat().startsWith("web-eid:1.1"); - final String signingCertificate = (requireSigningCert && isV11) - ? authToken.getUnverifiedSigningCertificates().get(0).getUnverifiedSigningCertificate() + final var signingCertificate = requireSigningCert + ? authToken.getUnverifiedSigningCertificates().getFirst() // NOTE: Handling multiple signing certificates is out of scope of this example. : null; - final List supportedSignatureAlgorithms = (requireSigningCert && isV11) - ? authToken.getUnverifiedSigningCertificates().get(0).getSupportedSignatureAlgorithms() - : null; - - return WebEidAuthentication.fromCertificate(userCertificate, signingCertificate, supportedSignatureAlgorithms, authorities); + return WebEidAuthentication.fromCertificate(userCertificate, signingCertificate, authorities); } catch (AuthTokenException e) { throw new AuthenticationServiceException("Web eID token validation failed", e); } catch (CertificateEncodingException e) { 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 1c01590e..b1202dea 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.T) // AIA OCSP is supported for signatures with LT or LTA profile. + .withSignatureProfile(SignatureProfile.LT) // AIA OCSP is supported for signatures with LT or LTA profile. .withSigningCertificate(certificate) .withSignatureDigestAlgorithm(signatureDigestAlgorithm) .buildDataToSign(); diff --git a/example/src/test/java/eu/webeid/example/security/WebEidAuthenticationTest.java b/example/src/test/java/eu/webeid/example/security/WebEidAuthenticationTest.java index 4cbaee26..afabf7e7 100644 --- a/example/src/test/java/eu/webeid/example/security/WebEidAuthenticationTest.java +++ b/example/src/test/java/eu/webeid/example/security/WebEidAuthenticationTest.java @@ -38,7 +38,7 @@ class WebEidAuthenticationTest { @Test void whenOrganizationCertificate_thenSucceeds() throws Exception { final X509Certificate certificate = CertificateLoader.decodeCertificateFromBase64(ORGANIZATION_CERT); - final Authentication authentication = WebEidAuthentication.fromCertificate(certificate, null, null, Collections.emptyList()); + final Authentication authentication = WebEidAuthentication.fromCertificate(certificate, null, Collections.emptyList()); assertThat(authentication.getPrincipal()).isEqualTo("Testijad.ee isikutuvastus"); } 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 264e640f..28834b40 100644 --- a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -72,7 +72,7 @@ public class ObjectMother { "\"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\"," + + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"0Ov7ME6pTY1K2GXMj8Wxov/o2fGIMEds8OMY5dKdkB0nrqQX7fG1E5mnsbvyHpMDecMUH6Yg+p1HXdgB/lLqOcFZjt/OVXPjAAApC5d1YgRYATDcxsR1zqQwiNcHdmWn\"," + "\"format\":\"web-eid:1.1\"}", WebEidAuthToken.class); diff --git a/src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java b/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java similarity index 93% rename from src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java rename to src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java index 0ee65957..8dc5a782 100644 --- a/src/main/java/eu/webeid/security/certificate/UnverifiedSigningCertificate.java +++ b/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java @@ -20,14 +20,16 @@ * SOFTWARE. */ -package eu.webeid.security.certificate; +package eu.webeid.security.authtoken; -import eu.webeid.security.authtoken.SupportedSignatureAlgorithm; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.List; +@JsonIgnoreProperties(ignoreUnknown = true) public class UnverifiedSigningCertificate { + private String unverifiedSigningCertificate; private List supportedSignatureAlgorithms; diff --git a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java index 1e013b0b..a476446e 100644 --- a/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java +++ b/src/main/java/eu/webeid/security/authtoken/WebEidAuthToken.java @@ -23,7 +23,6 @@ package eu.webeid.security.authtoken; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import eu.webeid.security.certificate.UnverifiedSigningCertificate; import java.util.List; 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 c6daf3e4..1e4ce909 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -25,7 +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.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenException; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.exceptions.CertificateDecodingException; @@ -46,7 +46,9 @@ import java.security.cert.CertificateNotYetValidException; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -91,50 +93,57 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { final X509Certificate subjectCertificate = validateV1(token, currentChallengeNonce); - final X509Certificate signingCertificate = validateSigningCertificatesExist(token); - validateSupportedSignatureAlgorithms(token.getUnverifiedSigningCertificates()); - validateSameSubject(subjectCertificate, signingCertificate); - validateSameIssuer(subjectCertificate, signingCertificate); - validateSigningCertificateValidity(signingCertificate); - validateKeyUsage(signingCertificate); + final List signingCertificates = validateSigningCertificatesExist(token); + final List unverifiedSigningCertificates = token.getUnverifiedSigningCertificates(); + Iterator unverifiedIterator = unverifiedSigningCertificates.iterator(); + for(X509Certificate signingCertificate : signingCertificates) { + UnverifiedSigningCertificate unverifiedSigningCertificate = unverifiedIterator.next(); + validateSupportedSignatureAlgorithms(unverifiedSigningCertificate); + validateSameSubject(subjectCertificate, signingCertificate); + validateSameIssuer(subjectCertificate, signingCertificate); + validateSigningCertificateValidity(signingCertificate); + validateKeyUsage(signingCertificate); + } return subjectCertificate; } - private static void validateSupportedSignatureAlgorithms(List unverifiedSigningCertificates) throws AuthTokenParseException { - for (UnverifiedSigningCertificate cert : unverifiedSigningCertificates) { - List algorithms = cert.getSupportedSignatureAlgorithms(); + private static void validateSupportedSignatureAlgorithms(UnverifiedSigningCertificate cert) throws AuthTokenParseException { + List algorithms = cert.getSupportedSignatureAlgorithms(); - if (algorithms == null || algorithms.isEmpty()) { - throw new AuthTokenParseException("'supportedSignatureAlgorithms' field is missing"); - } + if (algorithms == null || algorithms.isEmpty()) { + throw new AuthTokenParseException("'supportedSignatureAlgorithms' field is missing"); + } - 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()) - ); + 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"); - } + if (hasInvalid) { + throw new AuthTokenParseException("Unsupported signature algorithm"); } } - private static X509Certificate validateSigningCertificatesExist(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { + private static List validateSigningCertificatesExist(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { 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); + List result = new ArrayList<>(); - if (signingCertificate == null || isNullOrEmpty(signingCertificate.getUnverifiedSigningCertificate())) { - throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + for (UnverifiedSigningCertificate certificate : signingCertificates) { + if (certificate == null || isNullOrEmpty(certificate.getUnverifiedSigningCertificate())) { + throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + } + + result.add(CertificateLoader.decodeCertificateFromBase64(certificate.getUnverifiedSigningCertificate())); } - return CertificateLoader.decodeCertificateFromBase64(signingCertificate.getUnverifiedSigningCertificate()); + return result; } private static void validateSameSubject(X509Certificate subjectCertificate, X509Certificate signingCertificate) diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java index 7ce63762..c5142c30 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java @@ -78,6 +78,10 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { + if (token.getUnverifiedSigningCertificates() != null) { + throw new AuthTokenParseException("'unverifiedSigningCertificates' field is not allowed for format '" + token.getFormat() + "'"); + } + if (token.getUnverifiedCertificate() == null || token.getUnverifiedCertificate().isEmpty()) { throw new AuthTokenParseException("'unverifiedCertificate' field is missing, null or empty"); } 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 cee401d4..bea606a1 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java @@ -24,7 +24,7 @@ import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.certificate.CertificateLoader; -import eu.webeid.security.certificate.UnverifiedSigningCertificate; +import eu.webeid.security.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.validator.AuthTokenSignatureValidator; import eu.webeid.security.validator.AuthTokenValidationConfiguration; diff --git a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1ValidatorTest.java b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1ValidatorTest.java index 44af1342..a8884426 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1ValidatorTest.java @@ -23,6 +23,7 @@ package eu.webeid.security.validator.versionvalidators; import eu.webeid.security.authtoken.WebEidAuthToken; +import eu.webeid.security.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.validator.AuthTokenSignatureValidator; import eu.webeid.security.validator.AuthTokenValidationConfiguration; @@ -35,6 +36,7 @@ import org.junit.jupiter.params.provider.ValueSource; import java.security.cert.CertStore; +import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; @@ -75,10 +77,22 @@ void whenFormatIsNullEmptyOrNotV1_thenSupportsReturnsFalse(String format) { void whenUnverifiedCertificateMissing_thenValidationFails() { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1"); + when(token.getUnverifiedSigningCertificates()).thenReturn(null); when(token.getUnverifiedCertificate()).thenReturn(null); assertThatThrownBy(() -> validator.validate(token, "nonce")) .isInstanceOf(AuthTokenParseException.class) .hasMessageContaining("'unverifiedCertificate' field is missing"); } + + @Test + void whenUnverifiedSigningCertificatesPresentForV1_thenValidationFails() { + WebEidAuthToken token = mock(WebEidAuthToken.class); + when(token.getFormat()).thenReturn("web-eid:1"); + when(token.getUnverifiedSigningCertificates()).thenReturn(List.of(mock(UnverifiedSigningCertificate.class))); + + assertThatThrownBy(() -> validator.validate(token, "nonce")) + .isInstanceOf(AuthTokenParseException.class) + .hasMessageContaining("'unverifiedSigningCertificates' field is not allowed for format 'web-eid:1'"); + } } From bdf1443b1be42fb382f3e9e2d180321d1fe635cc Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Thu, 19 Mar 2026 13:36:30 +0200 Subject: [PATCH 8/9] NFC-115 Improve unverifiedSigningCertificates check Signed-off-by: Sander Kondratjev --- .../versionvalidators/AuthTokenVersion1Validator.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java index c5142c30..851c6ab8 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion1Validator.java @@ -78,8 +78,10 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { - if (token.getUnverifiedSigningCertificates() != null) { - throw new AuthTokenParseException("'unverifiedSigningCertificates' field is not allowed for format '" + token.getFormat() + "'"); + if (isExactV10Format(token.getFormat()) && token.getUnverifiedSigningCertificates() != null) { + throw new AuthTokenParseException( + "'unverifiedSigningCertificates' field is not allowed for format '" + token.getFormat() + "'" + ); } if (token.getUnverifiedCertificate() == null || token.getUnverifiedCertificate().isEmpty()) { @@ -109,4 +111,8 @@ public X509Certificate validate(WebEidAuthToken token, String currentChallengeNo return subjectCertificate; } + + private static boolean isExactV10Format(String format) { + return V1_SUPPORTED_TOKEN_FORMAT_PREFIX.equals(format) || "web-eid:1.0".equals(format); + } } From 38202c66c6353f7a53599a7723540182b5d319f0 Mon Sep 17 00:00:00 2001 From: Sander Kondratjev Date: Fri, 20 Mar 2026 13:39:14 +0200 Subject: [PATCH 9/9] NFC-115 Review findings. Signed-off-by: Sander Kondratjev --- .../security/WebEidAuthentication.java | 2 +- .../WebEidAuthenticationProvider.java | 3 +- .../webeid/example/testutil/ObjectMother.java | 2 +- .../SupportedSignatureAlgorithm.java | 3 ++ .../UnverifiedSigningCertificate.java | 11 +++--- .../AuthTokenVersion11Validator.java | 29 ++++++++-------- .../testutil/AbstractTestWithValidator.java | 2 +- .../validator/AuthTokenSignatureTest.java | 6 ++-- .../AuthTokenSignatureValidatorTest.java | 6 ++-- .../AuthTokenV11CertificateTest.java | 12 +++---- .../AuthTokenVersion11ValidatorTest.java | 34 +++++++++++++++++-- 11 files changed, 72 insertions(+), 38 deletions(-) diff --git a/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java b/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java index 4c779ded..6d00f58f 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthentication.java @@ -56,7 +56,7 @@ public static Authentication fromCertificate(X509Certificate userCertificate, @N return new WebEidAuthentication( principalName, idCode, - signingCertificate != null ? signingCertificate.getUnverifiedSigningCertificate() : null, + signingCertificate != null ? signingCertificate.getCertificate() : null, signingCertificate != null ? signingCertificate.getSupportedSignatureAlgorithms() : null, authorities ); 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 fbbe43ef..96f8a49b 100644 --- a/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java +++ b/example/src/main/java/eu/webeid/example/security/WebEidAuthenticationProvider.java @@ -38,6 +38,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -76,7 +77,7 @@ public Authentication authenticate(Authentication auth) throws AuthenticationExc try { final String nonce = challengeNonceStore.getAndRemove().getBase64EncodedNonce(); final X509Certificate userCertificate = tokenValidator.validate(authToken, nonce); - final var signingCertificate = requireSigningCert + final var signingCertificate = requireSigningCert && !CollectionUtils.isEmpty(authToken.getUnverifiedSigningCertificates()) ? authToken.getUnverifiedSigningCertificates().getFirst() // NOTE: Handling multiple signing certificates is out of scope of this example. : null; return WebEidAuthentication.fromCertificate(userCertificate, signingCertificate, 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 28834b40..1d39fc76 100644 --- a/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java +++ b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java @@ -69,7 +69,7 @@ public class ObjectMother { "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEBDCCA2WgAwIBAgIQY5OGshxoPMFg+Wfc0gFEaTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTIxMDcyMjEyNDMwOFoXDTI2MDcwOTIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQmwEKsJTjaMHSaZj19hb9EJaJlwbKc5VFzmlGMFSJVk4dDy+eUxa5KOA7tWXqzcmhh5SYdv+MxcaQKlKWLMa36pfgv20FpEDb03GCtLqjLTRZ7649PugAQ5EmAqIic29CjggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPlp/ceABC52itoqppEmbf71TJz6MGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgDCAgybz0u3W+tGI+AX+PiI5CrE9ptEHO5eezR1Jo4j7iGaO0i39xTGUB+NSC7P6AQbyE/ywqJjA1a62jTLcS9GHAJCARxN4NO4eVdWU3zVohCXm8WN3DWA7XUcn9TZiLGQ29P4xfQZOXJi/z4PNRRsR4plvSNB3dfyBvZn31HhC7my8woi\"," + "\"unverifiedSigningCertificates\":[{" + - "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"certificate\":\"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\"," + diff --git a/src/main/java/eu/webeid/security/authtoken/SupportedSignatureAlgorithm.java b/src/main/java/eu/webeid/security/authtoken/SupportedSignatureAlgorithm.java index 4bc8263f..131a3a73 100644 --- a/src/main/java/eu/webeid/security/authtoken/SupportedSignatureAlgorithm.java +++ b/src/main/java/eu/webeid/security/authtoken/SupportedSignatureAlgorithm.java @@ -22,6 +22,9 @@ package eu.webeid.security.authtoken; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) public class SupportedSignatureAlgorithm { private String cryptoAlgorithm; private String hashFunction; diff --git a/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java b/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java index 8dc5a782..745600f8 100644 --- a/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java +++ b/src/main/java/eu/webeid/security/authtoken/UnverifiedSigningCertificate.java @@ -29,16 +29,15 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class UnverifiedSigningCertificate { - - private String unverifiedSigningCertificate; + private String certificate; private List supportedSignatureAlgorithms; - public String getUnverifiedSigningCertificate() { - return unverifiedSigningCertificate; + public String getCertificate() { + return certificate; } - public void setUnverifiedSigningCertificate(String unverifiedSigningCertificate) { - this.unverifiedSigningCertificate = unverifiedSigningCertificate; + public void setCertificate(String certificate) { + this.certificate = certificate; } 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 1e4ce909..075e3676 100644 --- a/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java +++ b/src/main/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11Validator.java @@ -23,9 +23,9 @@ package eu.webeid.security.validator.versionvalidators; import eu.webeid.security.authtoken.SupportedSignatureAlgorithm; +import eu.webeid.security.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.authtoken.WebEidAuthToken; import eu.webeid.security.certificate.CertificateLoader; -import eu.webeid.security.authtoken.UnverifiedSigningCertificate; import eu.webeid.security.exceptions.AuthTokenException; import eu.webeid.security.exceptions.AuthTokenParseException; import eu.webeid.security.exceptions.CertificateDecodingException; @@ -48,7 +48,6 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.Set; @@ -93,12 +92,8 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { final X509Certificate subjectCertificate = validateV1(token, currentChallengeNonce); - final List signingCertificates = validateSigningCertificatesExist(token); - final List unverifiedSigningCertificates = token.getUnverifiedSigningCertificates(); - Iterator unverifiedIterator = unverifiedSigningCertificates.iterator(); - for(X509Certificate signingCertificate : signingCertificates) { - UnverifiedSigningCertificate unverifiedSigningCertificate = unverifiedIterator.next(); - validateSupportedSignatureAlgorithms(unverifiedSigningCertificate); + final List signingCertificates = validateSigningCertificates(token); + for (X509Certificate signingCertificate : signingCertificates) { validateSameSubject(subjectCertificate, signingCertificate); validateSameIssuer(subjectCertificate, signingCertificate); validateSigningCertificateValidity(signingCertificate); @@ -116,7 +111,11 @@ private static void validateSupportedSignatureAlgorithms(UnverifiedSigningCertif } boolean hasInvalid = algorithms.stream().anyMatch(algorithm -> - !SUPPORTED_SIGNING_CRYPTO_ALGORITHMS.contains(algorithm.getCryptoAlgorithm()) || + algorithm == null || + algorithm.getCryptoAlgorithm() == null || + algorithm.getHashFunction() == null || + algorithm.getPaddingScheme() == null || + !SUPPORTED_SIGNING_CRYPTO_ALGORITHMS.contains(algorithm.getCryptoAlgorithm()) || !SUPPORTED_SIGNING_HASH_FUNCTIONS.contains(algorithm.getHashFunction()) || !SUPPORTED_SIGNING_PADDING_SCHEMES.contains(algorithm.getPaddingScheme()) ); @@ -126,21 +125,21 @@ private static void validateSupportedSignatureAlgorithms(UnverifiedSigningCertif } } - private static List validateSigningCertificatesExist(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { + private static List validateSigningCertificates(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { 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'"); + throw new AuthTokenParseException("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); } List result = new ArrayList<>(); for (UnverifiedSigningCertificate certificate : signingCertificates) { - if (certificate == null || isNullOrEmpty(certificate.getUnverifiedSigningCertificate())) { - throw new AuthTokenParseException("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + if (certificate == null || isNullOrEmpty(certificate.getCertificate())) { + throw new AuthTokenParseException("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); } - - result.add(CertificateLoader.decodeCertificateFromBase64(certificate.getUnverifiedSigningCertificate())); + validateSupportedSignatureAlgorithms(certificate); + result.add(CertificateLoader.decodeCertificateFromBase64(certificate.getCertificate())); } return result; diff --git a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java index 53a0ff28..e14d0f73 100644 --- a/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java +++ b/src/test/java/eu/webeid/security/testutil/AbstractTestWithValidator.java @@ -48,7 +48,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\"," + "\"unverifiedSigningCertificates\":[{" + - "\"unverifiedSigningCertificate\":\"MIID6zCCA02gAwIBAgIQT7j6zk6pmVRcyspLo5SqejAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUzMVoXDTI5MDUwMjEwNDUzMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASkwENR8GmCpEs6OshDWDfIiKvGuyNMOD2rjIQW321AnZD3oIsqD0svBMNEJJj9Dlvq/47TYDObIa12KAU5IuOBfJs2lrFdSXZjaM+a5TWT3O2JTM36YDH2GcMe/eisepejggGrMIIBpzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGQDBIBgNVHSAEQTA/MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAJBgcEAIvsQAECMB0GA1UdDgQWBBTVX3s48Spy/Es2TcXgkRvwUn2YcjCBigYIKwYBBQUHAQMEfjB8MAgGBgQAjkYBATAIBgYEAI5GAQQwEwYGBACORgEGMAkGBwQAjkYBBgEwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQgGBr+Jbo1GeqgWdIwgMo7SA29AP38JxNm2HWq2Qb+kIHpusAK574Co1K5D4+Mk7/ITTuXQaET5WphHoN7tdAciTaQJBAn0zBigYyVPYSTO68HM6hmlwTwi/KlJDdXW/2NsMjSqofFFJXpGvpxk2CTqSRCjcavxLPnkasTbNROYSJcmM8Xc=\"," + + "\"certificate\":\"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\"," + diff --git a/src/test/java/eu/webeid/security/validator/AuthTokenSignatureTest.java b/src/test/java/eu/webeid/security/validator/AuthTokenSignatureTest.java index 2da650be..44232007 100644 --- a/src/test/java/eu/webeid/security/validator/AuthTokenSignatureTest.java +++ b/src/test/java/eu/webeid/security/validator/AuthTokenSignatureTest.java @@ -48,8 +48,10 @@ class AuthTokenSignatureTest extends AbstractTestWithValidator { static final String V11_AUTH_TOKEN_WRONG_CERT = "{\"algorithm\":\"ES384\"," + "\"unverifiedCertificate\":\"MIIEDDCCA26gAwIBAgIQM8UTDe8zVKtcysotoMgBlzAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE5MDUwMjEwNDUwMVoXDTI5MDUwMjEwNDUwMVowfzELMAkGA1UEBhMCRUUxFjAUBgNVBCoMDUpBQUstS1JJU1RKQU4xEDAOBgNVBAQMB0rDlUVPUkcxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARh/M6SBatkyMHjTmRgIF1MTqZpVIfqHZD6MrQUHdlykVSLNBmloFjoXbQbSe0l+sgKUPSZWb48IGPC7Mrudt5vLvnKy31qZ5a+2Ceg87NrVzdNCWF2oQrwXw63HieIBMmjggHMMIIByDAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwKAYDVR0RBCEwH4EdamFhay1rcmlzdGphbi5qb2VvcmdAZWVzdGkuZWUwHQYDVR0OBBYEFOSW4XJH0oDJAh2nEqFGhrlF9zXQMGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBiwAwgYcCQQjG3AnPzJdtmoaNI59T8vcjsNjVB5XLfUXiBguizma9I6dFqhHiTtfqo2aWpd+dcL8iz/3Dn03C0ruPLnJVt24lAkIB8M6KO+RcVJqXz8KXMUGstjK+1iIE0hd+2JtNmIJcqgNT7sj8f4NZfsix5JuUpY1j4msWG3k0h79U2bWcR8NQZdU=\"," + - "\"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\"}]," + + "\"unverifiedSigningCertificates\":[{" + + "\"certificate\":\"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\":\"arx164xRiwhIQDINe0J+ZxJWZFOQTx0PBtOaWaxAe7gofEIHRIbV1w0sOCYBJnvmvMem9hU4nc2+iJx2x8poYck4Z6eI3GwtiksIec3XQ9ZIk1n/XchXnmPn3GYV+HzJ\"," + "\"format\":\"web-eid:1.1\"}"; diff --git a/src/test/java/eu/webeid/security/validator/AuthTokenSignatureValidatorTest.java b/src/test/java/eu/webeid/security/validator/AuthTokenSignatureValidatorTest.java index 1205dab0..f53bb6b3 100644 --- a/src/test/java/eu/webeid/security/validator/AuthTokenSignatureValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/AuthTokenSignatureValidatorTest.java @@ -47,8 +47,10 @@ class AuthTokenSignatureValidatorTest { private static final String VALID_V11_RS256_AUTH_TOKEN = "{\"algorithm\":\"RS256\"," + "\"unverifiedCertificate\":\"MIIGvjCCBKagAwIBAgIQT7aXeR+zWlBb2Gbar+AFaTANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCTFYxOTA3BgNVBAoMMFZBUyBMYXR2aWphcyBWYWxzdHMgcmFkaW8gdW4gdGVsZXbEq3ppamFzIGNlbnRyczEaMBgGA1UEYQwRTlRSTFYtNDAwMDMwMTEyMDMxHTAbBgNVBAMMFERFTU8gTFYgZUlEIElDQSAyMDE3MB4XDTE4MTAzMDE0MTI0MloXDTIzMTAzMDE0MTI0MlowcDELMAkGA1UEBhMCTFYxHDAaBgNVBAMME0FORFJJUyBQQVJBVURaScWFxaAxFTATBgNVBAQMDFBBUkFVRFpJxYXFoDEPMA0GA1UEKgwGQU5EUklTMRswGQYDVQQFExJQTk9MVi0zMjE5MjItMzMwMzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXkra3rDOOt5K6OnJcg/Xt6JOogPAUBX2kT9zWelze7WSuPx2Ofs//0JoBQ575IVdh3JpLhfh7g60YYi41M6vNACVSNaFOxiEvE9amSFizMiLk5+dp+79rymqOsVQG8CSu8/RjGGlDsALeb3N/4pUSTGXUwSB64QuFhOWjAcmKPhHeYtry0hK3MbwwHzFhYfGpo/w+PL14PEdJlpL1UX/aPyT0Zq76Z4T/Z3PqbTmQp09+2b0thC0JIacSkyJuTu8fVRQvse+8UtYC6Kt3TBLZbPtqfAFSXWbuE47Lc2o840NkVlMHVAesoRAfiQxsK35YWFT0rHPWbLjX6ySiaL25AgMBAAGjggI+MIICOjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjAdBgNVHQ4EFgQUHZWimPze2GXULNaP4EFVdF+MWKQwHwYDVR0jBBgwFoAUj2jOvOLHQCFTCUK75Z4djEvNvTgwgfsGA1UdIASB8zCB8DA7BgYEAI96AQIwMTAvBggrBgEFBQcCARYjaHR0cHM6Ly93d3cuZXBhcmFrc3RzLmx2L3JlcG9zaXRvcnkwgbAGDCsGAQQBgfo9AgECATCBnzAvBggrBgEFBQcCARYjaHR0cHM6Ly93d3cuZXBhcmFrc3RzLmx2L3JlcG9zaXRvcnkwbAYIKwYBBQUHAgIwYAxexaBpcyBzZXJ0aWZpa8SBdHMgaXIgaWVrxLxhdXRzIExhdHZpamFzIFJlcHVibGlrYXMgaXpzbmllZ3TEgSBwZXJzb251IGFwbGllY2lub8WhxIEgZG9rdW1lbnTEgTB9BggrBgEFBQcBAQRxMG8wQgYIKwYBBQUHMAKGNmh0dHA6Ly9kZW1vLmVwYXJha3N0cy5sdi9jZXJ0L2RlbW9fTFZfZUlEX0lDQV8yMDE3LmNydDApBggrBgEFBQcwAYYdaHR0cDovL29jc3AucHJlcC5lcGFyYWtzdHMubHYwSAYDVR0fBEEwPzA9oDugOYY3aHR0cDovL2RlbW8uZXBhcmFrc3RzLmx2L2NybC9kZW1vX0xWX2VJRF9JQ0FfMjAxN18zLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAAOVoRbnMv2UXWYHgnmO9Zg9u8F1YvJiZPMeTYE2CVaiq0nXe4Mq0X5tWcsEiRpGQF9e0dWC6V5m6EmAsHxIRL4chZKRrIrPEiWtP3zyRI1/X2y5GwSUyZmgxkuSOHHw3UjzjrnOoI9izpC0OSNeumqpjT/tLAi35sktGkK0onEUPWGQnZLqd/hzykm+H/dmD27nOnfCJOSqbegLSbhV2w/WAII+IUD3vJ06F6rf9ZN8xbrGkPO8VMCIDIt0eBKFxBdSOgpsTfbERbjQJ+nFEDYhD0bFNYMsFSGnZiWpNaCcZSkk4mtNUa8sNXyaFQGIZk6NjQ/fsBANhUoxFz7rUKrRYqk356i8KFDZ+MJqUyodKKyW9oz+IO5eJxnL78zRbxD+EfAUmrLXOjmGIzU95RR1smS4cirrrPHqGAWojBk8hKbjNTJl9Tfbnsbc9/FUBJLVZAkCi631KfRLQ66bn8N0mbtKlNtdX0G47PXTy7SJtWwDtKQ8+qVpduc8xHLntbdAzie3mWyxA1SBhQuZ9BPf5SPBImWCNpmZNCTmI2e+4yyCnmG/kVNilUAaODH/fgQXFGdsKO/XATFohiies28twkEzqtlVZvZbpBhbJCHYVnQXMhMKcnblkDqXWcSWd3QAKig2yMH95uz/wZhiV+7tZ7cTgwcbCzIDCfpwBC3E=\"," + - "\"unverifiedSigningCertificate\":\"X5C\"," + - "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + + "\"unverifiedSigningCertificates\":[{" + + "\"certificate\":\"X5C\"," + + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]" + + "}]," + "\"issuerApp\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + "\"signature\":\"xsjXsQvVYXWcdV0YPhxLthJxtf0//R8p9WFFlYJGRARrl1ruyoAUwl0xeHgeZOKeJtwiCYCNWJzCG3VM3ydgt92bKhhk1u0JXIPVqvOkmDY72OCN4q73Y8iGSPVTgjk93TgquHlodf7YcqZNhutwNNf3oldHEWJD5zmkdwdpBFXgeOwTAdFwGljDQZbHr3h1Dr+apUDuloS0WuIzUuu8YXN2b8lh8FCTlF0G0DEjhHd/MGx8dbe3UTLHmD7K9DXv4zLJs6EF9i2v/C10SIBQDkPBSVPqMxCDPECjbEPi2+ds94eU7ThOhOQlFFtJ4KjQNTUa2crSixH7cYZF2rNNmA==\"," + "\"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 d4e658fa..0b868db6 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenV11CertificateTest.java @@ -66,7 +66,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\"," + "\"unverifiedSigningCertificates\":[{" + - "\"unverifiedSigningCertificate\":\"X5C\"," + + "\"certificate\":\"X5C\"," + "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]" + "}]," + "\"appVersion\":\"https://web-eid.eu/web-eid-mobile-app/releases/v1.0.0\"," + @@ -126,7 +126,7 @@ void whenV11SigningCertificateFieldIsMissing_thenValidationFails() throws Except assertThatThrownBy(() -> spyValidator.validate(token, VALID_CHALLENGE_NONCE)) .isInstanceOf(AuthTokenParseException.class) - .hasMessage("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + .hasMessage("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); } @Test @@ -190,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).getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().getFirst().getCertificate())) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -213,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).getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().getFirst().getCertificate())) .thenReturn(mockSigningCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -239,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).getUnverifiedSigningCertificate())) + mocked.when(() -> CertificateLoader.decodeCertificateFromBase64(parsedToken.getUnverifiedSigningCertificates().getFirst().getCertificate())) .thenReturn(signingCert); assertThatThrownBy(() -> spyValidator.validate(parsedToken, VALID_CHALLENGE_NONCE)) @@ -267,7 +267,7 @@ private static WebEidAuthToken getWebEidAuthToken(String cert) throws JsonProces ArrayNode certs = (ArrayNode) node.get("unverifiedSigningCertificates"); ObjectNode certNode = (ObjectNode) certs.get(0); - certNode.put("unverifiedSigningCertificate", cert); + certNode.put("certificate", 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 bea606a1..a7a35b2f 100644 --- a/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java +++ b/src/test/java/eu/webeid/security/validator/versionvalidators/AuthTokenVersion11ValidatorTest.java @@ -92,7 +92,21 @@ void whenFormatIsNullEmptyOrNotV11_thenSupportsReturnsFalse(String format) { } @Test - void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Exception { + void whenUnverifiedSigningCertificatesMissing_thenValidationFails() throws Exception { + WebEidAuthToken token = mock(WebEidAuthToken.class); + when(token.getFormat()).thenReturn("web-eid:1.1"); + when(token.getUnverifiedSigningCertificates()).thenReturn(null); + + AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); + doReturn(mock(X509Certificate.class)).when(spyValidator).validateV1(any(), any()); + + assertThatThrownBy(() -> spyValidator.validate(token, "nonce")) + .isInstanceOf(AuthTokenParseException.class) + .hasMessage("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); + } + + @Test + void whenUnverifiedSigningCertificatesContainsNullEntry_thenValidationFails() throws Exception { WebEidAuthToken token = mock(WebEidAuthToken.class); when(token.getFormat()).thenReturn("web-eid:1.1"); when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList(null)); @@ -102,7 +116,21 @@ void whenUnverifiedSigningCertificateMissing_thenValidationFails() throws Except assertThatThrownBy(() -> spyValidator.validate(token, "nonce")) .isInstanceOf(AuthTokenParseException.class) - .hasMessage("'unverifiedSigningCertificates' field is missing, null or empty for format 'web-eid:1.1'"); + .hasMessage("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); + } + + @Test + void whenUnverifiedSigningCertificateValueMissing_thenValidationFails() throws Exception { + WebEidAuthToken token = mock(WebEidAuthToken.class); + when(token.getFormat()).thenReturn("web-eid:1.1"); + when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList(new UnverifiedSigningCertificate())); + + AuthTokenVersion11Validator spyValidator = Mockito.spy(validator); + doReturn(mock(X509Certificate.class)).when(spyValidator).validateV1(any(), any()); + + assertThatThrownBy(() -> spyValidator.validate(token, "nonce")) + .isInstanceOf(AuthTokenParseException.class) + .hasMessage("'unverifiedSigningCertificates' contains a null or empty entry for format 'web-eid:1.1'"); } @Test @@ -111,7 +139,7 @@ void whenSupportedSignatureAlgorithmsMissing_thenValidationFails() throws Except when(token.getFormat()).thenReturn("web-eid:1.1"); UnverifiedSigningCertificate certificate = new UnverifiedSigningCertificate(); - certificate.setUnverifiedSigningCertificate("abc"); + certificate.setCertificate("abc"); certificate.setSupportedSignatureAlgorithms(null); when(token.getUnverifiedSigningCertificates()).thenReturn(Collections.singletonList(certificate));