diff --git a/domismp-tests/domismp-docker/images/domismp-weblogic122/build.sh b/domismp-tests/domismp-docker/images/domismp-weblogic122/build.sh index 2fa71713b4c08fba6677573002d97ee005d614ae..4b017064737a53d62a647ba5ccdf1f81a49ffdba 100755 --- a/domismp-tests/domismp-docker/images/domismp-weblogic122/build.sh +++ b/domismp-tests/domismp-docker/images/domismp-weblogic122/build.sh @@ -51,7 +51,7 @@ cleanExternalImageResources() { } composeBuildImage() { - echo "Build ${IMAGE_NAME_DOMIBUS_SOAPUI} image..." + echo "Build ${IMAGE_SMP_WEBLOGIC122} image..." docker compose -f docker-compose.build.yml build } diff --git a/domismp-tests/domismp-docker/images/domismp-weblogic141/build.sh b/domismp-tests/domismp-docker/images/domismp-weblogic141/build.sh index 19b743bfffeec45b6406e910963c1accf649603e..dfbdaa74fbfb2eba267280b85196a9ce4a82117d 100755 --- a/domismp-tests/domismp-docker/images/domismp-weblogic141/build.sh +++ b/domismp-tests/domismp-docker/images/domismp-weblogic141/build.sh @@ -51,7 +51,7 @@ cleanExternalImageResources() { } composeBuildImage() { - echo "Build ${IMAGE_NAME_DOMIBUS_SOAPUI} image..." + echo "Build ${IMAGE_SMP_WEBLOGIC141} image..." docker compose -f docker-compose.build.yml build } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java index f4e842296a7451786ad58b9c3712f91a0c4fa2c8..1d9d87d70fcf312c7e8d0eec31ce766056102eb9 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -32,7 +32,11 @@ import org.springframework.stereotype.Service; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; -import java.io.*; +import javax.net.ssl.X509KeyManager; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.security.*; import java.security.cert.Certificate; @@ -62,7 +66,7 @@ public class UIKeystoreService extends BasicKeystoreService { this.configurationService = configurationService; } - private final Map<String, Key> keystoreKeys = new HashMap<>(); + private final List<String> keystoreKeys = new ArrayList<>(); // list of aliases with private keys private final Map<String, X509Certificate> keystoreCertificates = new HashMap<>(); private final List<CertificateRO> certificateROList = new ArrayList<>(); @@ -97,19 +101,20 @@ public class UIKeystoreService extends BasicKeystoreService { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, keystoreSecToken.toCharArray()); keyManagersTemp = kmf.getKeyManagers(); - } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException exception) { + } catch (KeyStoreException | NoSuchAlgorithmException | + UnrecoverableKeyException exception) { LOG.error("Error occurred while initialize keyManagers : " + keystoreFile.getAbsolutePath() + " Error: " + ExceptionUtils.getRootCauseMessage(exception), exception); return; } // load keys for signature - Map<String, Key> hmKeys = new HashMap<>(); + List<String> keyList = new ArrayList<>(); Map<String, X509Certificate> hmCertificates = new HashMap<>(); try { List<String> aliases = list(keyStore.aliases()); for (String alias : aliases) { - loadKeyAndCert(keyStore, alias, keystoreSecToken, hmKeys, hmCertificates); + loadKeyAndCert(keyStore, alias, keyList, hmCertificates); } } catch (Exception exception) { LOG.error("Could not load signing certificate amd private keys Error: " + ExceptionUtils.getRootCauseMessage(exception), exception); @@ -123,7 +128,7 @@ public class UIKeystoreService extends BasicKeystoreService { keystoreKeys.clear(); keystoreCertificates.clear(); - keystoreKeys.putAll(hmKeys); + keystoreKeys.addAll(keyList); keystoreCertificates.putAll(hmCertificates); // add last file date lastUpdateKeystoreFileTime = keystoreFile.lastModified(); @@ -172,15 +177,17 @@ public class UIKeystoreService extends BasicKeystoreService { return keyStore; } - private void loadKeyAndCert(KeyStore keyStore, String alias, String keySecurityToken, Map<String, Key> hmKeys, Map<String, X509Certificate> hmCertificates) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { - Key key = keyStore.getKey(alias, keySecurityToken.toCharArray()); + private void loadKeyAndCert(KeyStore keyStore, String alias, List<String> keyList, Map<String, X509Certificate> hmCertificates) throws KeyStoreException { + Certificate certificate = keyStore.getCertificate(alias); if (!(certificate instanceof X509Certificate)) { LOG.warn("Wrong certificate type found in keystore, entry alias: [{}]. Entry is ignored", alias); return; } // add to cache - hmKeys.put(alias, key); + if (keyStore.isKeyEntry(alias)) { + keyList.add(alias); + } hmCertificates.put(alias, (X509Certificate) certificate); } @@ -196,7 +203,7 @@ public class UIKeystoreService extends BasicKeystoreService { CertificateRO certificateRO = convertToRo(cert); basicCertificateValidation(cert, certificateRO); certificateRO.setAlias(alias); - certificateRO.setContainingKey(keystoreKeys.get(alias) != null); + certificateRO.setContainingKey(keystoreKeys.contains(alias)); certificateROList.add(certificateRO); }); } @@ -208,30 +215,52 @@ public class UIKeystoreService extends BasicKeystoreService { return conversionService.convert(d, CertificateRO.class); } + /** + * Get key from keystore by the alias from the registered keyManagers + * + * @param keyAlias alias of the key or null for the only key in the keystore + * @return key from keystore + * @throws SMPRuntimeException if the key for alias is not found in the keystore + */ public Key getKey(String keyAlias) { if (isKeyStoreChanged()) { refreshData(); } - if (keystoreKeys.isEmpty()) { - throw new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, "Could not retrieve key: [" + keyAlias + "] from empty keystore: [" + configurationService.getKeystoreFile() +"]!"); + if (keystoreKeys.isEmpty() || keyManagers == null || keyManagers.length < 1) { + throw new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, "Could not retrieve key: [" + keyAlias + "] from empty keystore: [" + configurationService.getKeystoreFile() + "]!"); } + final String searchAlias = getKeyAlias(keyAlias); + // get all X509KeyManager + return Arrays.stream(keyManagers) + .filter(X509KeyManager.class::isInstance) + .map(X509KeyManager.class::cast) + .map(km -> km.getPrivateKey(searchAlias)) + .findFirst() + .orElseThrow(() -> new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, + "Could not retrieve key: [" + keyAlias + "] from empty keystore: [" + configurationService.getKeystoreFile() + "]!")); + } - if (keystoreKeys.size() == 1) { - // for backward compatibility... - // don't care about configured alias in single-domain setup - // and return the only key - LOG.warn("Returning the only key in keystore regardless the configuration"); - return keystoreKeys.values().iterator().next(); + /** + * Get key from keystore by the alias from the registered keyManagers. + * Legacy behaviour: If the alias is not provided and there is only one key in the keystore, the key is returned. + * + * @param keyAlias alias of the key or null + * @return non null key alias + * @throws SMPRuntimeException if the keyAlias is not found in the keystore + */ + private String getKeyAlias(String keyAlias) { + String trimAlias = StringUtils.trim(keyAlias); + if (StringUtils.isBlank(trimAlias) && keystoreKeys.size() == 1) { + trimAlias = keystoreKeys.get(0); } - if (isBlank(keyAlias) || !keystoreKeys.containsKey(keyAlias)) { + if (isBlank(trimAlias) || !keystoreKeys.contains(trimAlias)) { throw new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, "Wrong configuration, missing key pair from keystore or wrong alias: " + keyAlias); } - - return keystoreKeys.get(keyAlias); + return trimAlias; } public X509Certificate getCert(String certAlias) { @@ -256,7 +285,7 @@ public class UIKeystoreService extends BasicKeystoreService { /** * Import keys smp keystore * - * @param newKeystore new keystore file to import + * @param newKeystore new keystore file to import * @param password password for new keystore file */ public List<CertificateRO> importKeys(KeyStore newKeystore, String password) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException { @@ -276,7 +305,7 @@ public class UIKeystoreService extends BasicKeystoreService { /** * Returns entries having certificates that are already present in the current keystore. * - * @param newKeystore new keystore file to import + * @param newKeystore new keystore file to import * @return the set of duplicate certificates * @throws KeyStoreException when not able to read the keystore aliases */ @@ -312,10 +341,10 @@ public class UIKeystoreService extends BasicKeystoreService { * Store keystore * * @param keyStore to store - * @throws IOException if the keystore can not be persisted - * @throws CertificateException if keystore cannot be stored + * @throws IOException if the keystore can not be persisted + * @throws CertificateException if keystore cannot be stored * @throws NoSuchAlgorithmException if keystore type algorithm is not supported - * @throws KeyStoreException if keystore cannot be stored + * @throws KeyStoreException if keystore cannot be stored */ private void storeKeystore(KeyStore keyStore) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException { File keystoreFilePath = configurationService.getKeystoreFile(); diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java index b607edaa84485683969998de5ba485051ebe2046..5711a5601b5145bf53f0924f8e934445912088f2 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java @@ -45,11 +45,13 @@ import java.util.UUID; import static eu.europa.ec.edelivery.smp.testutil.TestConstants.SIMPLE_EXTENSION_XML; public class TestDBUtils { + // valid key alias for testing from the keystore + public static final String TEST_KEY_ALIAS = "single_domain_key"; public static DBDomain createDBDomain(String domainCode) { DBDomain domain = new DBDomain(); domain.setDomainCode(domainCode); - domain.setSignatureKeyAlias(anyString()); + domain.setSignatureKeyAlias(TEST_KEY_ALIAS); domain.setSmlClientKeyAlias(anyString()); domain.setSmlSubdomain(anyString()); domain.setSmlSmpId(anyString());