diff --git a/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificatePurposeValidator.java b/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificatePurposeValidator.java index 279772f..4306a9c 100644 --- a/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificatePurposeValidator.java +++ b/src/main/java/eu/webeid/security/validator/certvalidators/SubjectCertificatePurposeValidator.java @@ -36,8 +36,8 @@ public final class SubjectCertificatePurposeValidator { private static final Logger LOG = LoggerFactory.getLogger(SubjectCertificatePurposeValidator.class); + private static final int KEY_USAGE_DIGITAL_SIGNATURE = 0; private static final String EXTENDED_KEY_USAGE_CLIENT_AUTHENTICATION = "1.3.6.1.5.5.7.3.2"; - /** * Validates that the purpose of the user certificate from the authentication token contains client authentication. * @@ -46,9 +46,18 @@ public final class SubjectCertificatePurposeValidator { */ public static void validateCertificatePurpose(X509Certificate subjectCertificate) throws AuthTokenException { try { + final boolean[] keyUsage = subjectCertificate.getKeyUsage(); + if (keyUsage == null) { + throw new UserCertificateMissingPurposeException(); + } + if (!keyUsage[KEY_USAGE_DIGITAL_SIGNATURE]) { + throw new UserCertificateWrongPurposeException(); + } final List usages = subjectCertificate.getExtendedKeyUsage(); if (usages == null || usages.isEmpty()) { - throw new UserCertificateMissingPurposeException(); + // Digital Signature extension present, but Extended Key Usage extension not present, + // assume it is an authentication certificate (e.g. Luxembourg eID). + return; } if (!usages.contains(EXTENDED_KEY_USAGE_CLIENT_AUTHENTICATION)) { throw new UserCertificateWrongPurposeException(); diff --git a/src/test/java/eu/webeid/security/testutil/AuthTokenValidators.java b/src/test/java/eu/webeid/security/testutil/AuthTokenValidators.java index 2590ada..ec977e7 100644 --- a/src/test/java/eu/webeid/security/testutil/AuthTokenValidators.java +++ b/src/test/java/eu/webeid/security/testutil/AuthTokenValidators.java @@ -94,6 +94,20 @@ public static AuthTokenValidator getAuthTokenValidatorWithDisallowedESTEIDPolicy .build(); } + public static AuthTokenValidator getAuthTokenValidatorForBelgianIdCard() throws CertificateException, IOException, JceException { + return getAuthTokenValidator( + "https://47f0-46-131-86-189.ngrok-free.app", + CertificateLoader.loadCertificatesFromResources("eID TEST EC Citizen CA.cer") + ); + } + + public static AuthTokenValidator getAuthTokenValidatorForFinnishIdCard() throws CertificateException, IOException, JceException { + return getAuthTokenValidator( + "https://47f0-46-131-86-189.ngrok-free.app", + CertificateLoader.loadCertificatesFromResources("DVV TEST Certificates - G5E.crt", "VRK TEST CA for Test Purposes - G4.crt") + ); + } + public static AuthTokenValidatorBuilder getDefaultAuthTokenValidatorBuilder() throws CertificateException, IOException { return getAuthTokenValidatorBuilder(TOKEN_ORIGIN_URL, getCACertificates()); } diff --git a/src/test/java/eu/webeid/security/validator/AuthTokenCertificateBelgianIdCardTest.java b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateBelgianIdCardTest.java new file mode 100644 index 0000000..c354b47 --- /dev/null +++ b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateBelgianIdCardTest.java @@ -0,0 +1,97 @@ +/* + * 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.validator; + +import static eu.webeid.security.testutil.DateMocker.mockDate; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.mockStatic; + +import eu.webeid.security.authtoken.WebEidAuthToken; +import eu.webeid.security.exceptions.AuthTokenException; +import eu.webeid.security.testutil.AbstractTestWithValidator; +import eu.webeid.security.testutil.AuthTokenValidators; +import eu.webeid.security.util.DateAndTime; +import java.io.IOException; +import java.security.cert.CertificateException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class AuthTokenCertificateBelgianIdCardTest extends AbstractTestWithValidator { + + private static final String BELGIAN_TEST_ID_CARD_AUTH_TOKEN_ECC = + "{" + + " \"action\": \"web-eid:authenticate-success\"," + + " \"algorithm\": \"ES384\"," + + " \"appVersion\": \"https://web-eid.eu/web-eid-app/releases/2.7.0+965\"," + + " \"format\": \"web-eid:1.0\"," + + " \"signature\": \"VWCxJ+NrWpNsLJwLbJ1IXuJkkrRsxhfZ1uVmaoY3gBMPrvULaLAp+A1VYGJ2QWobL9FvhMyEQpVlO99ytovux3pX75gHkf3Z0sBjtNqr/QS0ac+qI2hEccFnU0H7deO7\"," + + " \"unverifiedCertificate\": \"MIIDQDCCAsegAwIBAgIQEAAAAAAA8evx/gAAAAGKYTAKBggqhkjOPQQDAzAuMQswCQYDVQQGEwJCRTEfMB0GA1UEAwwWZUlEIFRFU1QgRUMgQ2l0aXplbiBDQTAeFw0yMDEwMjIyMjAwMDBaFw0zMDEwMjIyMjAwMDBaMHYxCzAJBgNVBAYTAkJFMScwJQYDVQQDDB5Ob3JhIFNwZWNpbWVuIChBdXRoZW50aWNhdGlvbikxETAPBgNVBAQMCFNwZWNpbWVuMRUwEwYDVQQqDAxOb3JhIEFuZ8OobGUxFDASBgNVBAUTCzAxMDUwMzk5ODY0MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPybypdwlvczyuKQtJ87s/RmB+hRFaP4BdtR/Sc8jfQTmKUYVn0KDYZPBllh928yMPxU7F+Za3FtFrAPCnDH75IquYsn0oc5olVO7Uas5gn61Y2EA5askyCljNVLA0Gquo4IBYDCCAVwwHwYDVR0jBBgwFoAU3bN/45oZjlnJEVYCLfX6nW/ehEswDgYDVR0PAQH/BAQDAgeAMEkGA1UdIARCMEAwPgYHYDgMAQECAjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vZWlkZGV2Y2FyZHMuemV0ZXNjYXJkcy5iZS9jZXJ0MBMGA1UdJQQMMAoGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9laWRkZXZjYXJkcy56ZXRlc2NhcmRzLmJlL2NybC9jaXRpemVuY2FFQy5jcmwwgYEGCCsGAQUFBwEBBHUwczA+BggrBgEFBQcwAoYyaHR0cDovL2VpZGRldmNhcmRzLnpldGVzY2FyZHMuYmUvY2VydC9yb290Y2FFQy5jcnQwMQYIKwYBBQUHMAGGJWh0dHA6Ly9laWRkZXZjYXJkcy56ZXRlc2NhcmRzLmJlOjg4ODgwCgYIKoZIzj0EAwMDZwAwZAIwE7uLOjrhXbid+tRKe/5wgE/R3rFVsE6HkpHJg+9+mqlBToLrLWvckmiPRmUot85BAjBNyxy48pVF+azJEnt0Z/hipToVhgJLlMkPFwZiL2+4B3w2WtNeSphEl3gjClos+Wg=\"" + + "}"; + + private static final String BELGIAN_TEST_ID_CARD_AUTH_TOKEN_RSA = + "{" + + " \"action\": \"web-eid:authenticate-success\"," + + " \"algorithm\": \"RS256\"," + + " \"appVersion\": \"https://web-eid.eu/web-eid-app/releases/2.7.0+965\"," + + " \"format\": \"web-eid:1.0\"," + + " \"signature\": \"KQsMoSj3lWz1H3NZ2LYtV27oIi2LdiBonYVjxZrRUt7qFBmepRRHY+vtM0qOZ0J8i9DwR25hmVi60S2yNAkYMIdYp3g2o8FamSpdz5MZBAGCpxF0yqK74sHN+87qjqj4qMv2rUIKMluhvjuwLSzZHaJzJyels/jdOHTQNgZ8S3ufEoCvLYcVU19TFryoo7ZWKfSB8qTWIv3UdOBTWG7fcU/fOwQmw9YAGrfKTJevTDIwcdLccqKXc1JzDWx6eargAx9Pa3Ehwa1SwB0aTXYVsfO+9awlFzjTXAnCudzKLoYBmNJedmv0MXlxNHSFQ9sNZDVgV4Sb5nQSlXE0st9uoQ==\"," + + " \"unverifiedCertificate\": \"MIID7zCCA3WgAwIBAgIQEAAAAAAA8evx/gAAAAHu2TAKBggqhkjOPQQDAzAuMQswCQYDVQQGEwJCRTEfMB0GA1UEAwwWZUlEIFRFU1QgRUMgQ2l0aXplbiBDQTAeFw0yMzA5MjYyMjAwMDBaFw0zMzA5MjYyMjAwMDBaMHYxCzAJBgNVBAYTAkJFMScwJQYDVQQDDB5Ob3JhIFNwZWNpbWVuIChBdXRoZW50aWNhdGlvbikxETAPBgNVBAQMCFNwZWNpbWVuMRUwEwYDVQQqDAxOb3JhIEFuZ8OobGUxFDASBgNVBAUTCzAxMDUwMzk5ODY0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/JkeHwt4rQhx/QrpPSCLUd98YxiZRvtKyo1glLiByNvDu7D6Jgg70tj2wlrwn3X0c8Rz/Yy3SgLIPzc92ptvaJG0H52cecRKABzDKwn9Qf/ZwsasDkKnprx0KUbiTaBU9Se1AEim5hyG4naahcfQJo+SwvIMhjIVPk1l1B+fbwRBlYG+LoJXdLSwoYRWArnf+4vx2PXR1nrmfLDw0NrtuNQy637O58DSL2XGd0+jKpQfYCBrqN0B26zA2RU0Uzb0ztEf4VXYH26dmv+bqwpXdPQYPQ7elNFXzFCRnmgJfxt4aKSkGZ2sQFWrKeIRxzVizLFDnfW8TSV5S4tuwYSMwIDAQABo4IBYDCCAVwwHwYDVR0jBBgwFoAU3bN/45oZjlnJEVYCLfX6nW/ehEswDgYDVR0PAQH/BAQDAgeAMEkGA1UdIARCMEAwPgYHYDgMAQECAjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vZWlkZGV2Y2FyZHMuemV0ZXNjYXJkcy5iZS9jZXJ0MBMGA1UdJQQMMAoGCCsGAQUFBwMCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9laWRkZXZjYXJkcy56ZXRlc2NhcmRzLmJlL2NybC9jaXRpemVuY2FFQy5jcmwwgYEGCCsGAQUFBwEBBHUwczA+BggrBgEFBQcwAoYyaHR0cDovL2VpZGRldmNhcmRzLnpldGVzY2FyZHMuYmUvY2VydC9yb290Y2FFQy5jcnQwMQYIKwYBBQUHMAGGJWh0dHA6Ly9laWRkZXZjYXJkcy56ZXRlc2NhcmRzLmJlOjg4ODgwCgYIKoZIzj0EAwMDaAAwZQIwL3Mp3pSlxfjUQ6zlsAwLHBbs1//7vPiqx//IcyUZYuVXZ00KG8MelmaMNbmzDVxaAjEAnqHxkwV5sSDwQCUbb6Tofaypm+DO6C2LriHS5r+s84x2Eq+s+lAJ1L4WkcgsKxns\"" + + "}"; + + private MockedStatic mockedClock; + + @Override + @BeforeEach + protected void setup() { + super.setup(); + mockedClock = mockStatic(DateAndTime.DefaultClock.class); + // Ensure that the certificates do not expire. + mockDate("2024-12-24", mockedClock); + } + + @AfterEach + void tearDown() { + mockedClock.close(); + } + + @Test + void whenIdCardWithECCSignatureCertificateIsValidated_thenValidationSucceeds() throws AuthTokenException, CertificateException, IOException { + AuthTokenValidator validator = AuthTokenValidators.getAuthTokenValidatorForBelgianIdCard(); + final WebEidAuthToken token = validator.parse(BELGIAN_TEST_ID_CARD_AUTH_TOKEN_ECC); + assertThatCode(() -> validator + .validate(token, "iMeEwP2cgUINY2XoO/lqEpOUn7z/ysHRqGXkGKC4VXE=")) + .doesNotThrowAnyException(); + } + + @Test + void whenIdCardWithRSASignatureCertificateIsValidated_thenValidationSucceeds() throws AuthTokenException, CertificateException, IOException { + AuthTokenValidator validator = AuthTokenValidators.getAuthTokenValidatorForBelgianIdCard(); + final WebEidAuthToken token = validator.parse(BELGIAN_TEST_ID_CARD_AUTH_TOKEN_RSA); + assertThatCode(() -> validator + .validate(token, "YPVgYc7Qds0qmK/RilPLffnsIg7IIovM4BAWqGZWwiY=")) + .doesNotThrowAnyException(); + } + +} diff --git a/src/test/java/eu/webeid/security/validator/AuthTokenCertificateFinnishIdCardTest.java b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateFinnishIdCardTest.java new file mode 100644 index 0000000..c4fe11d --- /dev/null +++ b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateFinnishIdCardTest.java @@ -0,0 +1,98 @@ +/* + * 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.validator; + +import static eu.webeid.security.testutil.DateMocker.mockDate; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.Mockito.mockStatic; + +import eu.webeid.security.authtoken.WebEidAuthToken; +import eu.webeid.security.exceptions.AuthTokenException; +import eu.webeid.security.testutil.AbstractTestWithValidator; +import eu.webeid.security.testutil.AuthTokenValidators; +import eu.webeid.security.util.DateAndTime; +import java.io.IOException; +import java.security.cert.CertificateException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class AuthTokenCertificateFinnishIdCardTest extends AbstractTestWithValidator { + + private static final String FINNISH_TEST_ID_CARD_BACKMAN_JUHANI_AUTH_TOKEN = + "{" + + " \"action\": \"web-eid:authenticate-success\"," + + " \"algorithm\": \"ES384\"," + + " \"appVersion\": \"https://web-eid.eu/web-eid-app/releases/2.7.0+965\"," + + " \"format\": \"web-eid:1.0\"," + + " \"signature\": \"dUzVVAvN4dLFSKo0De4WQsDMiXpoQVjT8km6RLePeRyhlsA7swaq7XLfGOO1Qw4o5DrWAKBOlElwpJO9GgO6nPhDsco4SVKHSdSKbJMvg0E8qrCo3dUbdT/Y5UhKFPNl\"," + + " \"unverifiedCertificate\": \"MIIEOjCCA7+gAwIBAgIEBhwJHTAMBggqhkjOPQQDAwUAMHgxCzAJBgNVBAYTAkZJMSkwJwYDVQQKDCBEaWdpLSBqYSB2YWVzdG90aWV0b3ZpcmFzdG8gVEVTVDEYMBYGA1UECwwPVGVzdGl2YXJtZW50ZWV0MSQwIgYDVQQDDBtEVlYgVEVTVCBDZXJ0aWZpY2F0ZXMgLSBHNUUwHhcNMjMwMTI1MjIwMDAwWhcNMjgwMTIzMjE1OTU5WjB5MQswCQYDVQQGEwJGSTESMBAGA1UEBRMJOTk5MDIwMDE2MQ8wDQYDVQQqDAZKVUhBTkkxGTAXBgNVBAQMEFNQRUNJTUVOLUJBQ0tNQU4xKjAoBgNVBAMMIVNQRUNJTUVOLUJBQ0tNQU4gSlVIQU5JIDk5OTAyMDAxNjB2MBAGByqGSM49AgEGBSuBBAAiA2IABKq3yVI9NYmZwV2Matvk6yXFLLYn087ldhvl1AfCRoV8mTGhmL+y/R4DzaTeTrS9epEUcR9x2697h6DLBUkiOlAcI3nN92RJgNlBOCdvBdNcYgx57njSJHde4Rsm5gmLLqOCAhUwggIRMB8GA1UdIwQYMBaAFBKet+Iox/OUaou9Tcb0wjaXUkIIMB0GA1UdDgQWBBS8olmlfP/C700H4k/wLPrKX513QzAOBgNVHQ8BAf8EBAMCA4gwgc0GA1UdIASBxTCBwjCBvwYKKoF2hAVjCoJgATCBsDAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5maW5laWQuZmkvY3BzOTkvMIGEBggrBgEFBQcCAjB4GnZWYXJtZW5uZXBvbGl0aWlra2Egb24gc2FhdGF2aWxsYSAtIENlcnRpZmlrYXRwb2xpY3kgZmlubnMgLSBDZXJ0aWZpY2F0ZSBwb2xpY3kgaXMgYXZhaWxhYmxlIGh0dHA6Ly93d3cuZmluZWlkLmZpL2Nwczk5MDAGA1UdEQQpMCeBJVMxSnVoYW5pMDQ5LlNQRUNJTUVOLUJhY2ttYW5AdGVzdGkuZmkwDwYDVR0TAQH/BAUwAwEBADA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vcHJveHkuZmluZWlkLmZpL2NybC9kdnZ0cDVlYy5jcmwwcgYIKwYBBQUHAQEEZjBkMDIGCCsGAQUFBzAChiZodHRwOi8vcHJveHkuZmluZWlkLmZpL2NhL2R2dnRwNWVjLmNydDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3B0ZXN0LmZpbmVpZC5maS9kdnZ0cDVlYzAMBggqhkjOPQQDAwUAA2cAMGQCMClSh2MQZVYZyKfgmntQxuVUtQvIIqs8aOdsKpla4wt/IU6hMbGEAfIv4AzLXLsS5QIwUcjlY8BCj4+x84ihAqqHNIle6kyKek/Tj994SjQBmUadtyUSDvg8O5MppKvgJCNV\"" + + "}"; + + private static final String FINNISH_TEST_ID_CARD_BABAFO_VELI_AUTH_TOKEN = + "{" + + " \"action\": \"web-eid:authenticate-success\"," + + " \"algorithm\": \"PS256\"," + + " \"appVersion\": \"https://web-eid.eu/web-eid-app/releases/2.7.0+965\"," + + " \"format\": \"web-eid:1.0\"," + + " \"signature\": \"TFJ+l/NyDIMzoRyJxXprA88kBZXTvQ1gu2vUWhf4sz468acq46WWllIVs9/nIwBRMt3cPnDwKT21EkgIBc/bhBO+7SlWcRAov0N9Nja0pebJAfYKyY0VONN9T4/LRnCg3NVFZequuk+6roV1vVPhySmOz29w/HM5F5tENbxkgn5uw3q7H44qUVE/s01vhmiCHpz98HGm01jX4p6Pm1IxQ5lcx+2wSYvm0t1G973pz+SXmJBE0rGOS8v+bmP15mIiIyGYeUFIvgw9cWsLhgyhYZwymm+Isfa/wAKbtmxT1bI2a7xIR+XDrG4xrwqOETaYUzshOfgvD5JViY+GLianbA==\"," + + " \"unverifiedCertificate\": \"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\"" + + "}"; + + private MockedStatic mockedClock; + + @Override + @BeforeEach + protected void setup() { + super.setup(); + + mockedClock = mockStatic(DateAndTime.DefaultClock.class); + // Ensure that the certificates do not expire. + mockDate("2024-12-24", mockedClock); + } + + @AfterEach + void tearDown() { + mockedClock.close(); + } + + @Test + void whenIdCardSignatureCertificateWithG5ERootCertificateIsValidated_thenValidationSucceeds() throws AuthTokenException, CertificateException, IOException { + AuthTokenValidator validator = AuthTokenValidators.getAuthTokenValidatorForFinnishIdCard(); + final WebEidAuthToken token = validator.parse(FINNISH_TEST_ID_CARD_BACKMAN_JUHANI_AUTH_TOKEN); + assertThatCode(() -> validator + .validate(token, "x9qZDRO/ao2zprt3Z0bkW4CvvE/gALFtUIf3tcC0XxY=")) + .doesNotThrowAnyException(); + } + + @Test + void whenIdCardSignatureCertificateWithG4RootCertificateIsValidated_thenValidationSucceeds() throws AuthTokenException, CertificateException, IOException { + AuthTokenValidator validator = AuthTokenValidators.getAuthTokenValidatorForFinnishIdCard(); + final WebEidAuthToken token = validator.parse(FINNISH_TEST_ID_CARD_BABAFO_VELI_AUTH_TOKEN); + assertThatCode(() -> validator + .validate(token, "ZqlDATkQRqh7LkqEbspBc2qDjot29oiNLlITdLgiVIo=")) + .doesNotThrowAnyException(); + } + +} diff --git a/src/test/java/eu/webeid/security/validator/AuthTokenCertificateTest.java b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateTest.java index 15191c2..14ed066 100644 --- a/src/test/java/eu/webeid/security/validator/AuthTokenCertificateTest.java +++ b/src/test/java/eu/webeid/security/validator/AuthTokenCertificateTest.java @@ -58,7 +58,7 @@ class AuthTokenCertificateTest extends AbstractTestWithValidator { "\"signature\":\"arx164xRiwhIQDINe0J+ZxJWZFOQTx0PBtOaWaxAe7gofEIHRIbV1w0sOCYBJnvmvMem9hU4nc2+iJx2x8poYck4Z6eI3GwtiksIec3XQ9ZIk1n/XchXnmPn3GYV+HzJ\"," + "\"format\":\"web-eid:1\"}"; - private static final String MISSING_PURPOSE_CERT = "MIICxjCCAa6gAwIBAgIJANTbd26vS6fmMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNVBAMTCndlYi1laWQuZXUwHhcNMjAwOTI0MTIyNDMzWhcNMzAwOTIyMTIyNDMzWjAVMRMwEQYDVQQDEwp3ZWItZWlkLmV1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAza5qBFu5fvs47rx3o9yzBVfIxHjMotID8ppkwWVen/uFxlqsRVi+XnWkggW+K8X45inAnBAVi1rIw7GQNdacSHglyvQfwM64AallmD0+K+QgbqxcO9fvRvlAeISENBc2bGgqTIytPEON5ZmazzbOZjqY3M1QcPlPZOeUm6M9ZcZFhsxpiB4gwZUic9tnCz9eujd6k6DzNVfSRaJcpGA5hJ9aKH4vXS3x7anewna+USEXkRb4Il5zSlZR0i1yrVA1YNOxCG/+GgWvXfvXwdQ0z9BpGwNEyc0mRDNx+umaTukz9t+7/qTcB2JLTuiwM9Gqg5sDDnzPlcZSa7GnIU0MLQIDAQABoxkwFzAVBgNVHREEDjAMggp3ZWItZWlkLmV1MA0GCSqGSIb3DQEBBQUAA4IBAQAYGkBhTlet47uw3JYunYo6dj4nGWSGV4x6LYjCp5QlAmGd28HpC1RFB3ba+inwW8SP69kEOcB0sJQAZ/tV90oCATNsy/Whg/TtiHISL2pr1dyBoKDRWbgTp8jjzcp2Bj9nL14aqpj1t4K1lcoYETX41yVmyyJu6VFs80M5T3yikm2giAhszjChnjyoT2kaEKoua9EUK9SS27pVltgbbvtmeTp3ZPHtBfiDOATL6E03RZ5WfMLRefI796a+RcznnudzQHhMSwcjLpMDgIWpUU4OU7RiwrU+S3MrvgzCjkWh2MGu/OGLB+d3JZoW+eCvigoshmAsbJCMLbh4N78BCPqk"; + private static final String MISSING_KEY_USAGE_CERT = "MIICxjCCAa6gAwIBAgIJANTbd26vS6fmMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNVBAMTCndlYi1laWQuZXUwHhcNMjAwOTI0MTIyNDMzWhcNMzAwOTIyMTIyNDMzWjAVMRMwEQYDVQQDEwp3ZWItZWlkLmV1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAza5qBFu5fvs47rx3o9yzBVfIxHjMotID8ppkwWVen/uFxlqsRVi+XnWkggW+K8X45inAnBAVi1rIw7GQNdacSHglyvQfwM64AallmD0+K+QgbqxcO9fvRvlAeISENBc2bGgqTIytPEON5ZmazzbOZjqY3M1QcPlPZOeUm6M9ZcZFhsxpiB4gwZUic9tnCz9eujd6k6DzNVfSRaJcpGA5hJ9aKH4vXS3x7anewna+USEXkRb4Il5zSlZR0i1yrVA1YNOxCG/+GgWvXfvXwdQ0z9BpGwNEyc0mRDNx+umaTukz9t+7/qTcB2JLTuiwM9Gqg5sDDnzPlcZSa7GnIU0MLQIDAQABoxkwFzAVBgNVHREEDjAMggp3ZWItZWlkLmV1MA0GCSqGSIb3DQEBBQUAA4IBAQAYGkBhTlet47uw3JYunYo6dj4nGWSGV4x6LYjCp5QlAmGd28HpC1RFB3ba+inwW8SP69kEOcB0sJQAZ/tV90oCATNsy/Whg/TtiHISL2pr1dyBoKDRWbgTp8jjzcp2Bj9nL14aqpj1t4K1lcoYETX41yVmyyJu6VFs80M5T3yikm2giAhszjChnjyoT2kaEKoua9EUK9SS27pVltgbbvtmeTp3ZPHtBfiDOATL6E03RZ5WfMLRefI796a+RcznnudzQHhMSwcjLpMDgIWpUU4OU7RiwrU+S3MrvgzCjkWh2MGu/OGLB+d3JZoW+eCvigoshmAsbJCMLbh4N78BCPqk"; private static final String WRONG_PURPOSE_CERT = "MIIEBDCCA2WgAwIBAgIQGIgoZxFL7VZbyFH7MAVEkTAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE4MTAxODA5MjcyM1oXDTIzMTAxNzIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT3SZB34CUGYhQyLsLd9b2ihv35q7NT47Id9ugLIdgg3NSFDccH6rV16D2m8DKfuD2mn3V6QdaaZnbWF4YdDK1W0C9kLNsB70ob//y39pugMftepmQpJcBGPqug81tf5jujggHDMIIBvzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBHBgNVHSAEQDA+MDIGCysGAQQBg5EhAQIBMCMwIQYIKwYBBQUHAgEWFWh0dHBzOi8vd3d3LnNrLmVlL0NQUzAIBgYEAI96AQIwHwYDVR0RBBgwFoEUMzgwMDEwODU3MThAZWVzdGkuZWUwHQYDVR0OBBYEFPhJx7ro54+N8r2ByiZXzZyWBbjFMGEGCCsGAQUFBwEDBFUwUzBRBgYEAI5GAQUwRzBFFj9odHRwczovL3NrLmVlL2VuL3JlcG9zaXRvcnkvY29uZGl0aW9ucy1mb3ItdXNlLW9mLWNlcnRpZmljYXRlcy8TAkVOMCAGA1UdJQEB/wQWMBQGCCsG/wUFBwMCBggrBgEFBQcDBDAfBgNVHSMEGDAWgBTAhJkpxE6fOwI09pnhClYACCk+ezBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9haWEuZGVtby5zay5lZS9lc3RlaWQyMDE4MDUGCCsGAQUFBzAChilodHRwOi8vYy5zay5lZS9UZXN0X29mX0VTVEVJRDIwMTguZGVyLmNydDAKBggqhkjOPQQDBAOBjAAwgYgCQgFi5XSCFGgsc8SKLWwMBWS0nu/20FjEqh6OGvsI4iPctNDkinsxcYgARdfqPsNnDX+KjALKPEKZCLKRixGL2kPLMgJCAQFXP9gstThxlj/1Q5YFb7KWhPWFiKgQEi9JdvxJQNXLkWV9onEh96mRFgv4IJJpGazuoSMZtzNpyBxmM0dwnxOf"; private static final String WRONG_POLICY_CERT = "MIIEATCCA2OgAwIBAgIQOWkBWXNDJm1byFd3XsWkvjAKBggqhkjOPQQDBDBgMQswCQYDVQQGEwJFRTEbMBkGA1UECgwSU0sgSUQgU29sdXRpb25zIEFTMRcwFQYDVQRhDA5OVFJFRS0xMDc0NzAxMzEbMBkGA1UEAwwSVEVTVCBvZiBFU1RFSUQyMDE4MB4XDTE4MTAxODA5NTA0N1oXDTIzMTAxNzIxNTk1OVowfzELMAkGA1UEBhMCRUUxKjAoBgNVBAMMIUrDlUVPUkcsSkFBSy1LUklTVEpBTiwzODAwMTA4NTcxODEQMA4GA1UEBAwHSsOVRU9SRzEWMBQGA1UEKgwNSkFBSy1LUklTVEpBTjEaMBgGA1UEBRMRUE5PRUUtMzgwMDEwODU3MTgwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR5k1lXzvSeI9O/1s1pZvjhEW8nItJoG0EBFxmLEY6S7ki1vF2Q3TEDx6dNztI1Xtx96cs8r4zYTwdiQoDg7k3diUuR9nTWGxQEMO1FDo4Y9fAmiPGWT++GuOVoZQY3XxijggHBMIIBvTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIDiDBFBgNVHSAEPjA8MDAGCSsGAQQBzh8BAzAjMCEGCCsGAQUFBwIBFhVodHRwczovL3d3dy5zay5lZS9DUFMwCAYGBACPegECMB8GA1UdEQQYMBaBFDM4MDAxMDg1NzE4QGVlc3RpLmVlMB0GA1UdDgQWBBTkLL00CRAVTDEpocmV+W4m2CbmwDBhBggrBgEFBQcBAwRVMFMwUQYGBACORgEFMEcwRRY/aHR0cHM6Ly9zay5lZS9lbi9yZXBvc2l0b3J5L2NvbmRpdGlvbnMtZm9yLXVzZS1vZi1jZXJ0aWZpY2F0ZXMvEwJFTjAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUwISZKcROnzsCNPaZ4QpWAAgpPnswcwYIKwYBBQUHAQEEZzBlMCwGCCsGAQUFBzABhiBodHRwOi8vYWlhLmRlbW8uc2suZWUvZXN0ZWlkMjAxODA1BggrBgEFBQcwAoYpaHR0cDovL2Muc2suZWUvVGVzdF9vZl9FU1RFSUQyMDE4LmRlci5jcnQwCgYIKoZIzj0EAwQDgYsAMIGHAkIB9VLJjHbS2bYudRatkEeMFJAMKbJ4bAVdh0KlFxWASexF5ywpGl43WSpB6QAXzNEBMe1FIWiOIud44iexNWO1jgACQQ1+M+taZ4hyWqSNW5DCIiUP7Yu4WvH3SUjEqQHbOQshyMh5EM1pVcvOn/ZgOxLt6ETv9avnhVMw2zTd1b8u4EFk"; @@ -147,8 +147,8 @@ void whenCertificateFieldIsNotCertificate_thenParsingFails() throws AuthTokenExc } @Test - void whenCertificatePurposeIsMissing_thenValidationFails() throws AuthTokenException { - final WebEidAuthToken token = replaceTokenField(AUTH_TOKEN, "X5C", MISSING_PURPOSE_CERT); + void whenCertificateKeyUsageIsMissing_thenValidationFails() throws AuthTokenException { + final WebEidAuthToken token = replaceTokenField(AUTH_TOKEN, "X5C", MISSING_KEY_USAGE_CERT); assertThatThrownBy(() -> validator .validate(token, VALID_CHALLENGE_NONCE)) .isInstanceOf(UserCertificateMissingPurposeException.class); @@ -192,7 +192,7 @@ void whenUsingNewMobileIdCertificate_thenValidationFails() throws AuthTokenExcep final WebEidAuthToken token = replaceTokenField(AUTH_TOKEN, "X5C", NEW_MOBILE_ID_CERT); assertThatThrownBy(() -> validator .validate(token, VALID_CHALLENGE_NONCE)) - .isInstanceOf(UserCertificateMissingPurposeException.class); + .isInstanceOf(UserCertificateDisallowedPolicyException.class); } @Test diff --git a/src/test/resources/DVV TEST Certificates - G5E.crt b/src/test/resources/DVV TEST Certificates - G5E.crt new file mode 100644 index 0000000..a01b659 Binary files /dev/null and b/src/test/resources/DVV TEST Certificates - G5E.crt differ diff --git a/src/test/resources/VRK TEST CA for Test Purposes - G4.crt b/src/test/resources/VRK TEST CA for Test Purposes - G4.crt new file mode 100644 index 0000000..3159602 Binary files /dev/null and b/src/test/resources/VRK TEST CA for Test Purposes - G4.crt differ diff --git a/src/test/resources/eID TEST EC Citizen CA.cer b/src/test/resources/eID TEST EC Citizen CA.cer new file mode 100644 index 0000000..06456b7 --- /dev/null +++ b/src/test/resources/eID TEST EC Citizen CA.cer @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAq+gAwIBAgIIcND8I1qptLUwCgYIKoZIzj0EAwMwKzELMAkGA1UEBhMC +QkUxHDAaBgNVBAMME2VJRCBURVNUIEVDIFJvb3QgQ0EwIBcNMDcwNDMwMjIwMDIw +WhgPMjA4NzA0MTAyMjAwMjBaMC4xCzAJBgNVBAYTAkJFMR8wHQYDVQQDDBZlSUQg +VEVTVCBFQyBDaXRpemVuIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJAiNoOQf +Y0r8N6JVPMLedXyRZ7MwppGwQ9ZxFzLjVsbeKuUvqEFR0yKKyEidXc875m4UF5lR +pf/FSWagg2IXGWrypnRZkgnNVP6s5W2LzKdV09hd6v7O8j/8knfHOj+No4IBmTCC +AZUwHQYDVR0OBBYEFN2zf+OaGY5ZyRFWAi31+p1v3oRLMB8GA1UdIwQYMBaAFCHA +clfKHAQEGR3ZjH4+tYPrrBwCMA4GA1UdDwEB/wQEAwIBBjBIBgNVHSAEQTA/MD0G +BmA4DAEBAjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vZWlkZGV2Y2FyZHMuemV0ZXNj +YXJkcy5iZS9jZXJ0MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDBCBgNV +HR8EOzA5MDegNaAzhjFodHRwOi8vZWlkZGV2Y2FyZHMuemV0ZXNjYXJkcy5iZS9j +cmwvcm9vdGNhRUMuY3JsMIGBBggrBgEFBQcBAQR1MHMwPgYIKwYBBQUHMAKGMmh0 +dHA6Ly9laWRkZXZjYXJkcy56ZXRlc2NhcmRzLmJlL2NlcnQvcm9vdGNhRUMuY3J0 +MDEGCCsGAQUFBzABhiVodHRwOi8vZWlkZGV2Y2FyZHMuemV0ZXNjYXJkcy5iZTo4 +ODg4MBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0EAwMDaAAwZQIxAOMiiByF +0aLEA6zUrobMw7aSH5o2u1hGVMe0AL4ezYztRdfxvXVU+m1JosBVBDDjeAIwYJJN +7bLWw8BVi/lkxRjKL/+zAJP6djGywXI1pVh4HKb0D+tipq5StO+QnM8cnPmg +-----END CERTIFICATE-----