Added PKI module (#2298)

* Added PKI keystore

Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
pull/2345/head
Lucas Saldanha 4 years ago committed by GitHub
parent 68510e500b
commit 17b2d53aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      besu/build.gradle
  2. 39
      pki/build.gradle
  3. 141
      pki/src/main/java/org/hyperledger/besu/pki/PkiConfiguration.java
  4. 36
      pki/src/main/java/org/hyperledger/besu/pki/PkiException.java
  5. 170
      pki/src/main/java/org/hyperledger/besu/pki/keystore/HardwareKeyStoreWrapper.java
  6. 39
      pki/src/main/java/org/hyperledger/besu/pki/keystore/KeyStoreWrapper.java
  7. 215
      pki/src/main/java/org/hyperledger/besu/pki/keystore/SoftwareKeyStoreWrapper.java
  8. 81
      pki/src/test/java/org/hyperledger/besu/pki/keystore/HardwareKeyStoreWrapperTest.java
  9. 196
      pki/src/test/java/org/hyperledger/besu/pki/keystore/SoftwareKeyStoreWrapperTest.java
  10. BIN
      pki/src/test/resources/keystore/keystore
  11. BIN
      pki/src/test/resources/keystore/truststore
  12. 1
      pki/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
  13. 1
      settings.gradle

@ -29,6 +29,7 @@ jar {
dependencies { dependencies {
implementation project(':config') implementation project(':config')
implementation project(':pki')
implementation project(':consensus:clique') implementation project(':consensus:clique')
implementation project(':consensus:common') implementation project(':consensus:common')
implementation project(':consensus:ibft') implementation project(':consensus:ibft')

@ -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);
}
}

@ -45,6 +45,7 @@ include 'ethereum:trie'
include 'metrics:core' include 'metrics:core'
include 'metrics:rocksdb' include 'metrics:rocksdb'
include 'nat' include 'nat'
include 'pki'
include 'plugin-api' include 'plugin-api'
include 'plugins:rocksdb' include 'plugins:rocksdb'
include 'privacy-contracts' include 'privacy-contracts'

Loading…
Cancel
Save