diff --git a/oiosaml/src/main/java/dk/gov/oio/saml/service/CredentialService.java b/oiosaml/src/main/java/dk/gov/oio/saml/service/CredentialService.java index b5f1196..8c8900c 100644 --- a/oiosaml/src/main/java/dk/gov/oio/saml/service/CredentialService.java +++ b/oiosaml/src/main/java/dk/gov/oio/saml/service/CredentialService.java @@ -90,13 +90,15 @@ private BasicX509Credential getBasicX509Credential(String keystoreLocation, Stri KeyStore ks = keyStore(keystoreLocation, keystorePassword.toCharArray()); + // OpenSAML's KeyStoreCredentialResolver looks up the key password by the alias + // supplied in the EntityIdCriterion below. Java's PKCS12 keystore lowercases + // aliases on load, so keying the password map by the keystore's stored alias + // broke whenever the configured alias used a different casing, surfacing as a + // misleading "incorrect keystore password" error (issue #73). Key it by the + // configured alias instead; PKCS12 key lookup is already case-insensitive, so + // alias resolution becomes fully case-insensitive. Map passwords = new HashMap<>(); - try { - passwords.put(ks.aliases().nextElement(), keystorePassword); - } - catch (KeyStoreException e) { - throw new InternalException("Keystore not initialized properly", e); - } + passwords.put(alias, keystorePassword); KeyStoreCredentialResolver resolver = new KeyStoreCredentialResolver(ks, passwords); CriteriaSet criteria = new CriteriaSet(); diff --git a/oiosaml/src/test/java/dk/gov/oio/saml/service/CredentialServiceTest.java b/oiosaml/src/test/java/dk/gov/oio/saml/service/CredentialServiceTest.java new file mode 100644 index 0000000..d2d0a3a --- /dev/null +++ b/oiosaml/src/test/java/dk/gov/oio/saml/service/CredentialServiceTest.java @@ -0,0 +1,47 @@ +package dk.gov.oio.saml.service; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.opensaml.core.config.InitializationService; +import org.opensaml.security.x509.BasicX509Credential; + +import dk.gov.oio.saml.config.Configuration; +import dk.gov.oio.saml.util.TestConstants; + +public class CredentialServiceTest { + + // 'mixedcase-alias.p12' holds a single key entry created with the alias + // "TestKeyAlias". Java's PKCS12 keystore lowercases aliases on load, so + // configuring the alias with any other casing used to fail resolving the key + // and surfaced as a misleading "incorrect keystore password" error (issue #73). + private static final String MIXED_CASE_KEYSTORE = "mixedcase-alias.p12"; + private static final String ALIAS_IN_DIFFERENT_CASE = "TestKeyAlias"; + + @BeforeAll + public static void initOpenSAML() throws Exception { + InitializationService.initialize(); + } + + @DisplayName("Keystore alias is resolved case-insensitively (issue #73)") + @Test + public void testKeystoreAliasIsCaseInsensitive() throws Exception { + String keystoreLocation = getClass().getClassLoader().getResource(MIXED_CASE_KEYSTORE).getFile(); + + Configuration config = new Configuration.Builder() + .setSpEntityID(TestConstants.SP_ENTITY_ID) + .setBaseUrl(TestConstants.SP_BASE_URL) + .setIdpEntityID(TestConstants.IDP_ENTITY_ID) + .setIdpMetadataUrl(TestConstants.IDP_METADATA_URL) + .setKeystoreLocation(keystoreLocation) + .setKeystorePassword("Test1234") + .setKeyAlias(ALIAS_IN_DIFFERENT_CASE) + .build(); + + CredentialService credentialService = new CredentialService(config); + BasicX509Credential credential = credentialService.getPrimaryBasicX509Credential(); + + Assertions.assertNotNull(credential, "Key should resolve regardless of the configured alias casing"); + } +} diff --git a/oiosaml/src/test/resources/mixedcase-alias.p12 b/oiosaml/src/test/resources/mixedcase-alias.p12 new file mode 100644 index 0000000..4ca5f5d Binary files /dev/null and b/oiosaml/src/test/resources/mixedcase-alias.p12 differ