mirror of https://github.com/hyperledger/besu
Added PKI module (#2298)
* Added PKI keystore Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>pull/2345/head
parent
68510e500b
commit
17b2d53aa8
@ -0,0 +1,39 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
apply plugin: 'java-library' |
||||
|
||||
jar { |
||||
archiveBaseName = 'besu-pki' |
||||
manifest { |
||||
attributes( |
||||
'Specification-Title': archiveBaseName, |
||||
'Specification-Version': project.version, |
||||
'Implementation-Title': archiveBaseName, |
||||
'Implementation-Version': calculateVersion() |
||||
) |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
implementation 'com.google.guava:guava' |
||||
implementation 'org.apache.logging.log4j:log4j-api' |
||||
implementation 'org.apache.tuweni:bytes' |
||||
implementation 'org.bouncycastle:bcpkix-jdk15on' |
||||
|
||||
testImplementation 'junit:junit' |
||||
testImplementation 'org.assertj:assertj-core' |
||||
testImplementation 'org.mockito:mockito-core' |
||||
} |
@ -0,0 +1,141 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.pki; |
||||
|
||||
import static java.util.Objects.requireNonNull; |
||||
|
||||
import java.nio.file.Path; |
||||
import java.util.function.Supplier; |
||||
|
||||
public class PkiConfiguration { |
||||
|
||||
public static String DEFAULT_KEYSTORE_TYPE = "PKCS12"; |
||||
public static String DEFAULT_CERTIFICATE_ALIAS = "validator"; |
||||
|
||||
private final String keyStoreType; |
||||
private final Path keyStorePath; |
||||
private final Supplier<String> keyStorePasswordSupplier; |
||||
private final String certificateAlias; |
||||
private final String trustStoreType; |
||||
private final Path trustStorePath; |
||||
private final Supplier<String> trustStorePasswordSupplier; |
||||
|
||||
public PkiConfiguration( |
||||
final String keyStoreType, |
||||
final Path keyStorePath, |
||||
final Supplier<String> keyStorePasswordSupplier, |
||||
final String certificateAlias, |
||||
final String trustStoreType, |
||||
final Path trustStorePath, |
||||
final Supplier<String> trustStorePasswordSupplier) { |
||||
this.keyStoreType = keyStoreType; |
||||
this.keyStorePath = keyStorePath; |
||||
this.keyStorePasswordSupplier = keyStorePasswordSupplier; |
||||
this.certificateAlias = certificateAlias; |
||||
this.trustStoreType = trustStoreType; |
||||
this.trustStorePath = trustStorePath; |
||||
this.trustStorePasswordSupplier = trustStorePasswordSupplier; |
||||
} |
||||
|
||||
public String getKeyStoreType() { |
||||
return keyStoreType; |
||||
} |
||||
|
||||
public Path getKeyStorePath() { |
||||
return keyStorePath; |
||||
} |
||||
|
||||
public String getKeyStorePassword() { |
||||
return null == keyStorePasswordSupplier ? null : keyStorePasswordSupplier.get(); |
||||
} |
||||
|
||||
public String getCertificateAlias() { |
||||
return certificateAlias; |
||||
} |
||||
|
||||
public String getTrustStoreType() { |
||||
return trustStoreType; |
||||
} |
||||
|
||||
public Path getTrustStorePath() { |
||||
return trustStorePath; |
||||
} |
||||
|
||||
public String getTrustStorePassword() { |
||||
return trustStorePasswordSupplier.get(); |
||||
} |
||||
|
||||
public static final class Builder { |
||||
|
||||
private String keyStoreType = DEFAULT_KEYSTORE_TYPE; |
||||
private Path keyStorePath; |
||||
private Supplier<String> keyStorePasswordSupplier; |
||||
private String certificateAlias = DEFAULT_CERTIFICATE_ALIAS; |
||||
private String trustStoreType = DEFAULT_KEYSTORE_TYPE; |
||||
private Path trustStorePath; |
||||
private Supplier<String> trustStorePasswordSupplier; |
||||
|
||||
public Builder() {} |
||||
|
||||
public Builder withKeyStoreType(final String keyStoreType) { |
||||
this.keyStoreType = keyStoreType; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withKeyStorePath(final Path keyStorePath) { |
||||
this.keyStorePath = keyStorePath; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withKeyStorePasswordSupplier(final Supplier<String> keyStorePasswordSupplier) { |
||||
this.keyStorePasswordSupplier = keyStorePasswordSupplier; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withCertificateAlias(final String certificateAlias) { |
||||
this.certificateAlias = certificateAlias; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withTrustStoreType(final String trustStoreType) { |
||||
this.trustStoreType = trustStoreType; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withTrustStorePath(final Path trustStorePath) { |
||||
this.trustStorePath = trustStorePath; |
||||
return this; |
||||
} |
||||
|
||||
public Builder withTrustStorePasswordSupplier( |
||||
final Supplier<String> trustStorePasswordSupplier) { |
||||
this.trustStorePasswordSupplier = trustStorePasswordSupplier; |
||||
return this; |
||||
} |
||||
|
||||
public PkiConfiguration build() { |
||||
requireNonNull(keyStoreType, "Key Store Type must not be null"); |
||||
requireNonNull(keyStorePasswordSupplier, "Key Store password supplier must not be null"); |
||||
return new PkiConfiguration( |
||||
keyStoreType, |
||||
keyStorePath, |
||||
keyStorePasswordSupplier, |
||||
certificateAlias, |
||||
trustStoreType, |
||||
trustStorePath, |
||||
trustStorePasswordSupplier); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.pki; |
||||
|
||||
public class PkiException extends RuntimeException { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
public PkiException() { |
||||
super(); |
||||
} |
||||
|
||||
public PkiException(final String message) { |
||||
super(message); |
||||
} |
||||
|
||||
public PkiException(final String message, final Throwable t) { |
||||
super(message, t); |
||||
} |
||||
|
||||
public PkiException(final Throwable t) { |
||||
super(t); |
||||
} |
||||
} |
@ -0,0 +1,170 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.pki.keystore; |
||||
|
||||
import org.hyperledger.besu.pki.PkiException; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.InputStream; |
||||
import java.nio.file.Path; |
||||
import java.security.KeyStore; |
||||
import java.security.PrivateKey; |
||||
import java.security.Provider; |
||||
import java.security.PublicKey; |
||||
import java.security.Security; |
||||
import java.security.cert.Certificate; |
||||
import java.util.Optional; |
||||
import java.util.Properties; |
||||
import java.util.stream.Stream; |
||||
|
||||
import com.google.common.annotations.VisibleForTesting; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
/** |
||||
* Creates an instance of this class which is backed by a PKCS#11 keystore, such as a software |
||||
* (emulated) HSM or a physical/cloud HSM (see <a href= |
||||
* "https://docs.oracle.com/en/java/javase/11/security/pkcs11-reference-guide1.html">here</a> |
||||
*/ |
||||
public class HardwareKeyStoreWrapper implements KeyStoreWrapper { |
||||
|
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private static final String pkcs11Provider = "SunPKCS11"; |
||||
|
||||
private final KeyStore keystore; |
||||
private final transient char[] keystorePassword; |
||||
|
||||
private final java.security.Provider provider; |
||||
|
||||
public HardwareKeyStoreWrapper(final String keystorePassword, final Provider provider) { |
||||
try { |
||||
if (provider == null) { |
||||
throw new IllegalArgumentException("Provider is null"); |
||||
} |
||||
this.keystorePassword = keystorePassword.toCharArray(); |
||||
|
||||
this.provider = provider; |
||||
if (Security.getProvider(provider.getName()) == null) { |
||||
Security.addProvider(provider); |
||||
} |
||||
|
||||
keystore = KeyStore.getInstance(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, provider); |
||||
keystore.load(null, this.keystorePassword); |
||||
|
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to initialize HSM keystore", e); |
||||
} |
||||
} |
||||
|
||||
public HardwareKeyStoreWrapper(final String keystorePassword, final Path config) { |
||||
try { |
||||
if (keystorePassword == null) { |
||||
throw new IllegalArgumentException("Keystore password is null"); |
||||
} |
||||
final Properties properties = new Properties(); |
||||
final File configFile = config.toFile(); |
||||
try (InputStream ins = new FileInputStream(configFile)) { |
||||
properties.load(ins); |
||||
} |
||||
final String name = properties.getProperty("name"); |
||||
this.keystorePassword = keystorePassword.toCharArray(); |
||||
final Optional<Provider> existingProvider = |
||||
Stream.of(Security.getProviders()) |
||||
.filter(p -> p.getName().equals(String.format("%s-%s", pkcs11Provider, name))) |
||||
.findAny(); |
||||
if (existingProvider.isPresent()) { |
||||
provider = existingProvider.get(); |
||||
} else { |
||||
provider = getPkcs11Provider(configFile.getAbsolutePath()); |
||||
Security.addProvider(provider); |
||||
} |
||||
|
||||
keystore = KeyStore.getInstance(KeyStoreWrapper.KEYSTORE_TYPE_PKCS11, provider); |
||||
keystore.load(null, this.keystorePassword); |
||||
|
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to initialize HSM keystore", e); |
||||
} |
||||
} |
||||
|
||||
@VisibleForTesting |
||||
HardwareKeyStoreWrapper(final KeyStore keystore, final String password) { |
||||
this.keystore = keystore; |
||||
this.keystorePassword = password.toCharArray(); |
||||
this.provider = null; |
||||
} |
||||
|
||||
@Override |
||||
public PrivateKey getPrivateKey(final String keyAlias) { |
||||
try { |
||||
LOG.debug("Retrieving private key for alias: {}", keyAlias); |
||||
return (PrivateKey) keystore.getKey(keyAlias, this.keystorePassword); |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to get key: " + keyAlias, e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public PublicKey getPublicKey(final String keyAlias) { |
||||
try { |
||||
LOG.debug("Retrieving public key for alias: {}", keyAlias); |
||||
final Certificate certificate = keystore.getCertificate(keyAlias); |
||||
return (certificate != null) ? certificate.getPublicKey() : null; |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to get key: " + keyAlias, e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Certificate getCertificate(final String certificateAlias) { |
||||
try { |
||||
LOG.debug("Retrieving certificate for alias: {}", certificateAlias); |
||||
return keystore.getCertificate(certificateAlias); |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to get certificate: " + certificateAlias, e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Certificate[] getCertificateChain(final String certificateAlias) { |
||||
try { |
||||
LOG.debug("Retrieving certificate chain for alias: {}", certificateAlias); |
||||
return keystore.getCertificateChain(certificateAlias); |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to certificate chain for alias: " + certificateAlias, e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public KeyStore getKeyStore() { |
||||
return keystore; |
||||
} |
||||
|
||||
@Override |
||||
public KeyStore getTrustStore() { |
||||
return keystore; |
||||
} |
||||
|
||||
private Provider getPkcs11Provider(final String config) { |
||||
final Provider provider = Security.getProvider(pkcs11Provider); |
||||
if (null == provider) { |
||||
throw new IllegalArgumentException("Unable to load PKCS11 provider configuration."); |
||||
} else { |
||||
return provider.configure(config); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.pki.keystore; |
||||
|
||||
import java.security.KeyStore; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.cert.Certificate; |
||||
|
||||
public interface KeyStoreWrapper { |
||||
|
||||
String KEYSTORE_TYPE_JKS = "JKS"; |
||||
String KEYSTORE_TYPE_PKCS11 = "PKCS11"; |
||||
String KEYSTORE_TYPE_PKCS12 = "PKCS12"; |
||||
|
||||
KeyStore getKeyStore(); |
||||
|
||||
KeyStore getTrustStore(); |
||||
|
||||
PrivateKey getPrivateKey(String keyAlias); |
||||
|
||||
PublicKey getPublicKey(String keyAlias); |
||||
|
||||
Certificate getCertificate(String certificateAlias); |
||||
|
||||
Certificate[] getCertificateChain(String certificateAlias); |
||||
} |
@ -0,0 +1,215 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.pki.keystore; |
||||
|
||||
import org.hyperledger.besu.pki.PkiException; |
||||
|
||||
import java.io.FileInputStream; |
||||
import java.io.InputStream; |
||||
import java.nio.file.Path; |
||||
import java.security.GeneralSecurityException; |
||||
import java.security.Key; |
||||
import java.security.KeyStore; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.cert.Certificate; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import com.google.common.annotations.VisibleForTesting; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class SoftwareKeyStoreWrapper implements KeyStoreWrapper { |
||||
|
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private final KeyStore keystore; |
||||
private final transient char[] keystorePassword; |
||||
private KeyStore truststore; |
||||
private transient char[] truststorePassword; |
||||
|
||||
private final Map<String, PrivateKey> cachedPrivateKeys = new HashMap<>(); |
||||
private final Map<String, PublicKey> cachedPublicKeys = new HashMap<>(); |
||||
private final Map<String, Certificate> cachedCertificates = new HashMap<>(); |
||||
|
||||
public SoftwareKeyStoreWrapper( |
||||
final String keystoreType, final Path keystoreLocation, final String keystorePassword) { |
||||
this(keystoreType, keystoreLocation, keystorePassword, null, null, null); |
||||
} |
||||
|
||||
public SoftwareKeyStoreWrapper( |
||||
final String keystoreType, |
||||
final Path keystoreLocation, |
||||
final String keystorePassword, |
||||
final String truststoreType, |
||||
final Path truststoreLocation, |
||||
final String truststorePassword) { |
||||
|
||||
if (keystorePassword == null) { |
||||
throw new IllegalArgumentException("Keystore password is null"); |
||||
} |
||||
this.keystorePassword = keystorePassword.toCharArray(); |
||||
try (InputStream stream = new FileInputStream(keystoreLocation.toFile())) { |
||||
keystore = KeyStore.getInstance(keystoreType); |
||||
keystore.load(stream, this.keystorePassword); |
||||
|
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to initialize software keystore: " + keystoreLocation, e); |
||||
} |
||||
|
||||
if (truststoreType != null && truststoreLocation != null) { |
||||
this.truststorePassword = |
||||
(truststorePassword != null) ? truststorePassword.toCharArray() : null; |
||||
try (InputStream stream = new FileInputStream(truststoreLocation.toFile())) { |
||||
truststore = KeyStore.getInstance(truststoreType); |
||||
truststore.load(stream, this.truststorePassword); |
||||
|
||||
} catch (final Exception e) { |
||||
throw new PkiException( |
||||
"Failed to initialize software truststore: " + truststoreLocation, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@VisibleForTesting |
||||
SoftwareKeyStoreWrapper( |
||||
final KeyStore keystore, |
||||
final String keystorePassword, |
||||
final KeyStore truststore, |
||||
final String truststorePassword) { |
||||
this.keystore = keystore; |
||||
this.keystorePassword = keystorePassword.toCharArray(); |
||||
this.truststore = truststore; |
||||
this.truststorePassword = truststorePassword.toCharArray(); |
||||
} |
||||
|
||||
@Override |
||||
public PrivateKey getPrivateKey(final String keyAlias) { |
||||
LOG.debug("Retrieving private key for alias: {}", keyAlias); |
||||
return (PrivateKey) getKey(keyAlias, PrivateKey.class, cachedPrivateKeys); |
||||
} |
||||
|
||||
@Override |
||||
public PublicKey getPublicKey(final String keyAlias) { |
||||
LOG.debug("Retrieving public key for alias: {}", keyAlias); |
||||
return (PublicKey) getKey(keyAlias, PublicKey.class, cachedPublicKeys); |
||||
} |
||||
|
||||
@Override |
||||
public Certificate getCertificate(final String certificateAlias) { |
||||
try { |
||||
LOG.debug("Retrieving certificate for alias: {}", certificateAlias); |
||||
Certificate certificate = cachedCertificates.get(certificateAlias); |
||||
if (certificate == null) { |
||||
LOG.debug("Certificate alias: {} not cached", certificateAlias); |
||||
|
||||
certificate = keystore.getCertificate(certificateAlias); |
||||
if (certificate == null && truststore != null) { |
||||
certificate = truststore.getCertificate(certificateAlias); |
||||
} |
||||
if (certificate != null) { |
||||
LOG.debug("Certificate alias: {} found in keystore/truststore", certificateAlias); |
||||
cachedCertificates.put(certificateAlias, certificate); |
||||
cachedPublicKeys.put(certificateAlias, certificate.getPublicKey()); |
||||
return certificate; |
||||
} else { |
||||
LOG.warn("Certificate alias: {} not found in keystore/truststore", certificateAlias); |
||||
} |
||||
} |
||||
return certificate; |
||||
|
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to get certificate: " + certificateAlias, e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Certificate[] getCertificateChain(final String certificateAlias) { |
||||
try { |
||||
LOG.debug("Retrieving certificate chain for alias: {}", certificateAlias); |
||||
|
||||
Certificate[] certificateChain = keystore.getCertificateChain(certificateAlias); |
||||
if (certificateChain == null && truststore != null) { |
||||
certificateChain = truststore.getCertificateChain(certificateAlias); |
||||
} |
||||
return certificateChain; |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to certificate chain for alias: " + certificateAlias, e); |
||||
} |
||||
} |
||||
|
||||
private Key getKey( |
||||
final String keyAlias, |
||||
final Class<? extends Key> keyTypeClass, |
||||
final Map<String, ? extends Key> keyCache) { |
||||
Key cachedKey = keyCache.get(keyAlias); |
||||
if (cachedKey == null) { |
||||
LOG.debug("Key alias: {} not cached", keyAlias); |
||||
try { |
||||
cachedKey = loadAndCacheKey(this.keystore, this.keystorePassword, keyAlias, keyTypeClass); |
||||
if (cachedKey == null) { |
||||
cachedKey = |
||||
loadAndCacheKey(this.truststore, this.truststorePassword, keyAlias, keyTypeClass); |
||||
} |
||||
} catch (final Exception e) { |
||||
throw new PkiException("Failed to get key: " + keyAlias, e); |
||||
} |
||||
} |
||||
return cachedKey; |
||||
} |
||||
|
||||
@Override |
||||
public KeyStore getKeyStore() { |
||||
return keystore; |
||||
} |
||||
|
||||
@Override |
||||
public KeyStore getTrustStore() { |
||||
return truststore; |
||||
} |
||||
|
||||
private Key loadAndCacheKey( |
||||
final KeyStore keystore, |
||||
final char[] keystorePassword, |
||||
final String keyAlias, |
||||
final Class<? extends Key> keyTypeClass) |
||||
throws GeneralSecurityException { |
||||
if (keystore != null && keystore.containsAlias(keyAlias)) { |
||||
|
||||
final Key key = keystore.getKey(keyAlias, keystorePassword); |
||||
if (key != null) { |
||||
LOG.debug("Key alias: {} found in keystore/truststore", keyAlias); |
||||
if (key instanceof PrivateKey && PrivateKey.class.isAssignableFrom(keyTypeClass)) { |
||||
cachedPrivateKeys.put(keyAlias, (PrivateKey) key); |
||||
return key; |
||||
} else if (key instanceof PublicKey && PublicKey.class.isAssignableFrom(keyTypeClass)) { |
||||
cachedPublicKeys.put(keyAlias, (PublicKey) key); |
||||
return key; |
||||
} |
||||
} |
||||
|
||||
if (PublicKey.class.isAssignableFrom(keyTypeClass)) { |
||||
final Certificate certificate = getCertificate(keyAlias); |
||||
if (certificate != null) { |
||||
return certificate.getPublicKey(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
LOG.warn("Key alias: {} not found in keystore/truststore", keyAlias); |
||||
return null; |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.pki.keystore; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import java.security.KeyStore; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.cert.Certificate; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class HardwareKeyStoreWrapperTest { |
||||
|
||||
private static final String KEY_ALIAS = "keyalias"; |
||||
private static final String CERTIFICATE_ALIAS = "certalias"; |
||||
private static final char[] PASSWORD = "password".toCharArray(); |
||||
|
||||
@Mock private KeyStore keyStore; |
||||
@Mock private PrivateKey privateKey; |
||||
@Mock private PublicKey publicKey; |
||||
@Mock private Certificate certificate; |
||||
|
||||
private HardwareKeyStoreWrapper keyStoreWrapper; |
||||
|
||||
@Before |
||||
public void before() { |
||||
keyStoreWrapper = new HardwareKeyStoreWrapper(keyStore, new String(PASSWORD)); |
||||
} |
||||
|
||||
@Test |
||||
public void getPrivateKey() throws Exception { |
||||
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPrivateKey(KEY_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getPublicKey() throws Exception { |
||||
// Get public key from certificate
|
||||
when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(certificate); |
||||
when(certificate.getPublicKey()).thenReturn(publicKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPublicKey(KEY_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificate() throws Exception { |
||||
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificateChain() throws Exception { |
||||
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS)) |
||||
.thenReturn(new Certificate[] {certificate}); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS)).hasSize(1); |
||||
} |
||||
} |
@ -0,0 +1,196 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed 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. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
package org.hyperledger.besu.pki.keystore; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.hyperledger.besu.pki.keystore.KeyStoreWrapper.KEYSTORE_TYPE_PKCS12; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import java.nio.file.Path; |
||||
import java.security.KeyStore; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.cert.Certificate; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class SoftwareKeyStoreWrapperTest { |
||||
|
||||
private static final String KEY_ALIAS = "keyalias"; |
||||
private static final String CERTIFICATE_ALIAS = "certalias"; |
||||
private static final char[] PASSWORD = "password".toCharArray(); |
||||
|
||||
private SoftwareKeyStoreWrapper keyStoreWrapper; |
||||
|
||||
@Mock private KeyStore keyStore; |
||||
@Mock private KeyStore trustStore; |
||||
@Mock private PrivateKey privateKey; |
||||
@Mock private PublicKey publicKey; |
||||
@Mock private Certificate certificate; |
||||
|
||||
@Before |
||||
public void before() { |
||||
keyStoreWrapper = new SoftwareKeyStoreWrapper(keyStore, new String(PASSWORD), null, ""); |
||||
} |
||||
|
||||
@Test |
||||
public void getPrivateKey() throws Exception { |
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPrivateKey(KEY_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getPrivateKeyCaching() throws Exception { |
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey); |
||||
|
||||
keyStoreWrapper.getPrivateKey(KEY_ALIAS); |
||||
keyStoreWrapper.getPrivateKey(KEY_ALIAS); |
||||
|
||||
verify(keyStore, times(1)).getKey(eq(KEY_ALIAS), eq(PASSWORD)); |
||||
} |
||||
|
||||
@Test |
||||
public void getPrivateKeyFallbackToTrustStore() throws Exception { |
||||
keyStoreWrapper = |
||||
new SoftwareKeyStoreWrapper( |
||||
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD)); |
||||
|
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); |
||||
when(trustStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(trustStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(privateKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPrivateKey(KEY_ALIAS)).isNotNull(); |
||||
|
||||
verify(trustStore).getKey(eq(KEY_ALIAS), eq(PASSWORD)); |
||||
} |
||||
|
||||
@Test |
||||
public void getPublicKey() throws Exception { |
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPublicKey(KEY_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getPublicKeyCaching() throws Exception { |
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(keyStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey); |
||||
|
||||
keyStoreWrapper.getPublicKey(KEY_ALIAS); |
||||
keyStoreWrapper.getPublicKey(KEY_ALIAS); |
||||
|
||||
verify(keyStore, times(1)).getKey(eq(KEY_ALIAS), eq(PASSWORD)); |
||||
} |
||||
|
||||
@Test |
||||
public void getPublicKeyFallbackToTrustStore() throws Exception { |
||||
keyStoreWrapper = |
||||
new SoftwareKeyStoreWrapper( |
||||
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD)); |
||||
|
||||
when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); |
||||
when(trustStore.containsAlias(KEY_ALIAS)).thenReturn(true); |
||||
when(trustStore.getKey(KEY_ALIAS, PASSWORD)).thenReturn(publicKey); |
||||
|
||||
assertThat(keyStoreWrapper.getPublicKey(KEY_ALIAS)).isNotNull(); |
||||
|
||||
verify(trustStore).getKey(eq(KEY_ALIAS), eq(PASSWORD)); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificate() throws Exception { |
||||
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS)).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificateCaching() throws Exception { |
||||
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate); |
||||
|
||||
keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS); |
||||
keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS); |
||||
|
||||
verify(keyStore, times(1)).getCertificate(eq(CERTIFICATE_ALIAS)); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificateFallbackToTrustStore() throws Exception { |
||||
keyStoreWrapper = |
||||
new SoftwareKeyStoreWrapper( |
||||
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD)); |
||||
|
||||
when(keyStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(null); |
||||
when(trustStore.getCertificate(CERTIFICATE_ALIAS)).thenReturn(certificate); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificate(CERTIFICATE_ALIAS)).isNotNull(); |
||||
|
||||
verify(trustStore).getCertificate(eq(CERTIFICATE_ALIAS)); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificateChain() throws Exception { |
||||
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS)) |
||||
.thenReturn(new Certificate[] {certificate}); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS)).hasSize(1); |
||||
} |
||||
|
||||
@Test |
||||
public void getCertificateChainFallbackToTrustStore() throws Exception { |
||||
keyStoreWrapper = |
||||
new SoftwareKeyStoreWrapper( |
||||
keyStore, new String(PASSWORD), trustStore, new String(PASSWORD)); |
||||
|
||||
when(keyStore.getCertificateChain(CERTIFICATE_ALIAS)).thenReturn(null); |
||||
when(trustStore.getCertificateChain(CERTIFICATE_ALIAS)) |
||||
.thenReturn(new Certificate[] {certificate}); |
||||
|
||||
assertThat(keyStoreWrapper.getCertificateChain(CERTIFICATE_ALIAS)).hasSize(1); |
||||
|
||||
verify(trustStore).getCertificateChain(eq(CERTIFICATE_ALIAS)); |
||||
} |
||||
|
||||
@Test |
||||
public void loadKeyStoreFromFile() { |
||||
SoftwareKeyStoreWrapper loadedKeyStore = |
||||
new SoftwareKeyStoreWrapper( |
||||
KEYSTORE_TYPE_PKCS12, |
||||
Path.of("src/test/resources/keystore/keystore"), |
||||
"validator", |
||||
KEYSTORE_TYPE_PKCS12, |
||||
Path.of("src/test/resources/keystore/keystore"), |
||||
"validator"); |
||||
|
||||
assertThat(loadedKeyStore.getPublicKey("validator")).isNotNull(); |
||||
assertThat(loadedKeyStore.getPrivateKey("validator")).isNotNull(); |
||||
assertThat(loadedKeyStore.getCertificate("validator")).isNotNull(); |
||||
// CA -> INTERCA -> PARTNERACA -> VALIDATOR
|
||||
assertThat(loadedKeyStore.getCertificateChain("validator")).hasSize(4); |
||||
} |
||||
} |
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
||||
mock-maker-inline |
Loading…
Reference in new issue