This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch WSS-673 in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit fccbad991c39a7352c275dd76a778ce30200f5cf Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Jun 5 08:30:04 2020 +0100 WSS-673 - Add caching for private keys in Merlin --- .../org/apache/wss4j/common/crypto/Merlin.java | 28 +++++++ .../org/apache/wss4j/common/crypto/MerlinTest.java | 89 +++++++++++++++++++++ .../src/test/resources/keys/wss40.p12 | Bin 0 -> 2557 bytes 3 files changed, 117 insertions(+) diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java index 00ccc77..2163025 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/Merlin.java @@ -55,8 +55,10 @@ import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import javax.security.auth.callback.Callback; @@ -101,6 +103,7 @@ public class Merlin extends CryptoBase { public static final String KEYSTORE_TYPE = "keystore.type"; public static final String KEYSTORE_ALIAS = "keystore.alias"; public static final String KEYSTORE_PRIVATE_PASSWORD = "keystore.private.password"; + public static final String KEYSTORE_PRIVATE_KEY_CACHING = "keystore.private.caching"; /* * TrustStore configuration types @@ -129,6 +132,8 @@ public class Merlin extends CryptoBase { protected PasswordEncryptor passwordEncryptor; private boolean certProviderHandlesNameConstraints = false; + private boolean enablePrivateKeyCaching = true; + private Map<String, PrivateKey> privateKeyCache = new ConcurrentHashMap<>(); public Merlin() { // default constructor @@ -200,6 +205,7 @@ public class Merlin extends CryptoBase { if (cpNameConstraintsProp != null) { certProviderHandlesNameConstraints = Boolean.parseBoolean(cpNameConstraintsProp); } + // // Load the KeyStore // @@ -234,6 +240,11 @@ public class Merlin extends CryptoBase { privatePasswordSet = true; } } + + String privateKeyCachingProp = properties.getProperty(prefix + KEYSTORE_PRIVATE_KEY_CACHING); + if (privateKeyCachingProp != null) { + enablePrivateKeyCaching = Boolean.parseBoolean(privateKeyCachingProp); + } } else { LOG.debug("The KeyStore is not loaded as KEYSTORE_FILE is null"); } @@ -704,6 +715,13 @@ public class Merlin extends CryptoBase { pwd = decryptPassword(pwd, passwordEncryptor); } } + if (enablePrivateKeyCaching) { + Key privateKey = privateKeyCache.get(identifier); + if (privateKey != null) { + return (PrivateKey) privateKey; + } + } + Key keyTmp = keystore.getKey(identifier, pwd == null ? new char[]{} : pwd.toCharArray()); if (!(keyTmp instanceof PrivateKey)) { @@ -713,6 +731,10 @@ public class Merlin extends CryptoBase { throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty", new Object[] {msg}); } + + if (enablePrivateKeyCaching) { + privateKeyCache.put(identifier, (PrivateKey) keyTmp); + } return (PrivateKey) keyTmp; } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException ex) { throw new WSSecurityException( @@ -1519,4 +1541,10 @@ public class Merlin extends CryptoBase { public void setPasswordEncryptor(PasswordEncryptor passwordEncryptor) { this.passwordEncryptor = passwordEncryptor; } + + public void clearCache() { + if (enablePrivateKeyCaching) { + privateKeyCache.clear(); + } + } } diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/MerlinTest.java b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/MerlinTest.java new file mode 100644 index 0000000..a53443f --- /dev/null +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/MerlinTest.java @@ -0,0 +1,89 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.wss4j.common.crypto; + +import java.io.InputStream; +import java.security.KeyStore; + +import org.apache.wss4j.common.util.Loader; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.RepeatedTest; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Some tests for the Merlin Crypto provider + */ +public class MerlinTest { + + private static Merlin jksCrypto = new Merlin(); + private static Merlin pkcs12Crypto = new Merlin(); + + @BeforeAll + public static void setup() throws Exception { + WSProviderConfig.init(); + KeyStore keyStore = loadKeyStore("keys/wss40.jks", "security"); + jksCrypto.setKeyStore(keyStore); + + KeyStore pkcs12KeyStore = loadKeyStore("keys/wss40.p12", "security"); + pkcs12Crypto.setKeyStore(pkcs12KeyStore); + } + + @AfterAll + public static void cleanup() { + jksCrypto.clearCache(); + pkcs12Crypto.clearCache(); + } + + @RepeatedTest(1000) + public void testGetPrivateKeyJKS() throws Exception { + assertNotNull(jksCrypto.getPrivateKey("wss40", "security")); + } + + @RepeatedTest(1000) + public void testGetPrivateKeyPKCS12() throws Exception { + assertNotNull(pkcs12Crypto.getPrivateKey("wss40", "security")); + } + + @RepeatedTest(1000) + public void testGetCertificateJKS() throws Exception { + CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); + cryptoType.setAlias("wss40"); + assertNotNull(jksCrypto.getX509Certificates(cryptoType)); + } + + @RepeatedTest(1000) + public void testGetCertificatePKCS12() throws Exception { + CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); + cryptoType.setAlias("wss40"); + assertNotNull(pkcs12Crypto.getX509Certificates(cryptoType)); + } + + private static KeyStore loadKeyStore(String path, String password) throws Exception { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + ClassLoader loader = Loader.getClassLoader(MerlinTest.class); + InputStream input = Merlin.loadInputStream(loader, path); + keyStore.load(input, password.toCharArray()); + input.close(); + + return keyStore; + } +} \ No newline at end of file diff --git a/ws-security-common/src/test/resources/keys/wss40.p12 b/ws-security-common/src/test/resources/keys/wss40.p12 new file mode 100644 index 0000000..40b3924 Binary files /dev/null and b/ws-security-common/src/test/resources/keys/wss40.p12 differ
