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..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.getUnverifiedSigningCertificate() + 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/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/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java b/example/src/test/java/eu/webeid/example/testutil/ObjectMother.java index fc795471..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\":[{" + "\"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\"}]," + + "\"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 3ba0b8a8..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 String unverifiedSigningCertificate; - private List supportedSignatureAlgorithms; + private List unverifiedSigningCertificates; public String getUnverifiedCertificate() { return unverifiedCertificate; @@ -69,19 +69,11 @@ 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 List getSupportedSignatureAlgorithms() { - return supportedSignatureAlgorithms; - } - - public void setSupportedSignatureAlgorithms(List supportedSignatureAlgorithms) { - this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; + public void setUnverifiedSigningCertificates(List unverifiedSigningCertificates) { + this.unverifiedSigningCertificates = unverifiedSigningCertificates; } } 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 8770abb1..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; @@ -90,8 +91,8 @@ protected String getSupportedFormatPrefix() { @Override public X509Certificate validate(WebEidAuthToken token, String currentChallengeNonce) throws AuthTokenException { final X509Certificate subjectCertificate = validateV1(token, currentChallengeNonce); - final X509Certificate signingCertificate = validateSigningCertificateExists(token); - validateSupportedSignatureAlgorithms(token.getSupportedSignatureAlgorithms()); + final X509Certificate signingCertificate = validateSigningCertificatesExist(token); + validateSupportedSignatureAlgorithms(token.getUnverifiedSigningCertificates()); validateSameSubject(subjectCertificate, signingCertificate); validateSameIssuer(subjectCertificate, signingCertificate); validateSigningCertificateValidity(signingCertificate); @@ -100,27 +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 validateSigningCertificateExists(WebEidAuthToken token) throws AuthTokenParseException, CertificateDecodingException { - if (isNullOrEmpty(token.getUnverifiedSigningCertificate())) { - throw new AuthTokenParseException("'unverifiedSigningCertificate' field is missing, null or empty for format 'web-eid:1.1'"); + private static X509Certificate 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'"); } - return CertificateLoader.decodeCertificateFromBase64(token.getUnverifiedSigningCertificate()); + + UnverifiedSigningCertificate signingCertificate = 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(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 ad5d400b..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; @@ -46,8 +47,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\"," + - "\"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\":[{" + + "\"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\"}"; @@ -76,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)); } } 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..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\":[{" + "\"unverifiedSigningCertificate\":\"X5C\"," + - "\"supportedSignatureAlgorithms\":[{\"cryptoAlgorithm\":\"RSA\",\"hashFunction\":\"SHA-256\",\"paddingScheme\":\"PKCS1.5\"}]," + + "\"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\"}"; @@ -115,7 +118,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 +126,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 @@ -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.getUnverifiedSigningCertificate())) + 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.getUnverifiedSigningCertificate())) + 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.getUnverifiedSigningCertificate())) + 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.put("unverifiedSigningCertificate", 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 c311363b..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; @@ -94,22 +95,26 @@ 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()).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.getUnverifiedSigningCertificate()).thenReturn("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());