Skip to content

Commit

Permalink
PKI module - more testcases and crl file loading support (#2388)
Browse files Browse the repository at this point in the history
* Hardware and software keystore tests, crl file load support

added hardware and software keystore file based tests and added support to load crl file in the keystore implementations. the generated keys have 100 year expiry.

Signed-off-by: Saravana Perumal Shanmugam <perusworld@linux.com>
  • Loading branch information
perusworld authored Jun 17, 2021
1 parent b080a12 commit 413d62a
Show file tree
Hide file tree
Showing 42 changed files with 1,174 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.util.Collection;
import java.util.stream.Collectors;

public abstract class AbstractKeyStoreWrapper implements KeyStoreWrapper {

private static final String X_509 = "X.509";

private final Collection<X509CRL> crls;

protected AbstractKeyStoreWrapper(final Path crlLocation) {
super();
if (null == crlLocation) {
this.crls = null;
} else {
try (InputStream stream = new FileInputStream(crlLocation.toFile())) {
this.crls =
CertificateFactory.getInstance(X_509).generateCRLs(stream).stream()
.map(X509CRL.class::cast)
.collect(Collectors.toList());
} catch (final Exception e) {
throw new PkiException("Failed to initialize software truststore", e);
}
}
}

@Override
public Collection<X509CRL> getCRLs() {
return crls;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* (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 {
public class HardwareKeyStoreWrapper extends AbstractKeyStoreWrapper {

private static final Logger LOG = LogManager.getLogger();

Expand All @@ -50,7 +50,9 @@ public class HardwareKeyStoreWrapper implements KeyStoreWrapper {

private final java.security.Provider provider;

public HardwareKeyStoreWrapper(final String keystorePassword, final Provider provider) {
public HardwareKeyStoreWrapper(
final String keystorePassword, final Provider provider, final Path crlLocation) {
super(crlLocation);
try {
if (provider == null) {
throw new IllegalArgumentException("Provider is null");
Expand All @@ -70,7 +72,9 @@ public HardwareKeyStoreWrapper(final String keystorePassword, final Provider pro
}
}

public HardwareKeyStoreWrapper(final String keystorePassword, final Path config) {
public HardwareKeyStoreWrapper(
final String keystorePassword, final Path config, final Path crlLocation) {
super(crlLocation);
try {
if (keystorePassword == null) {
throw new IllegalArgumentException("Keystore password is null");
Expand Down Expand Up @@ -103,6 +107,7 @@ public HardwareKeyStoreWrapper(final String keystorePassword, final Path config)

@VisibleForTesting
HardwareKeyStoreWrapper(final KeyStore keystore, final String password) {
super(null);
this.keystore = keystore;
this.keystorePassword = password.toCharArray();
this.provider = null;
Expand Down Expand Up @@ -167,4 +172,9 @@ private Provider getPkcs11Provider(final String config) {
return provider.configure(config);
}
}

@VisibleForTesting
public Provider getPkcs11ProviderForConfig(final String config) {
return getPkcs11Provider(config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.util.Collection;

public interface KeyStoreWrapper {

Expand All @@ -36,4 +38,6 @@ public interface KeyStoreWrapper {
Certificate getCertificate(String certificateAlias);

Certificate[] getCertificateChain(String certificateAlias);

Collection<X509CRL> getCRLs();
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class SoftwareKeyStoreWrapper implements KeyStoreWrapper {
public class SoftwareKeyStoreWrapper extends AbstractKeyStoreWrapper {

private static final Logger LOG = LogManager.getLogger();

Expand All @@ -46,8 +46,11 @@ public class SoftwareKeyStoreWrapper implements KeyStoreWrapper {
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);
final String keystoreType,
final Path keystoreLocation,
final String keystorePassword,
final Path crlLocation) {
this(keystoreType, keystoreLocation, keystorePassword, null, null, null, crlLocation);
}

public SoftwareKeyStoreWrapper(
Expand All @@ -56,7 +59,9 @@ public SoftwareKeyStoreWrapper(
final String keystorePassword,
final String truststoreType,
final Path truststoreLocation,
final String truststorePassword) {
final String truststorePassword,
final Path crlLocation) {
super(crlLocation);

if (keystorePassword == null) {
throw new IllegalArgumentException("Keystore password is null");
Expand Down Expand Up @@ -90,6 +95,7 @@ public SoftwareKeyStoreWrapper(
final String keystorePassword,
final KeyStore truststore,
final String truststorePassword) {
super(null);
this.keystore = keystore;
this.keystorePassword = keystorePassword.toCharArray();
this.truststore = truststore;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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 java.nio.file.Path;
import java.security.cert.Certificate;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public abstract class BaseKeyStoreFileWrapperTest {
protected static final String KEYSTORE_VALID_KEY_ALIAS = "partner1client1";
protected static final String KEYSTORE_INVALID_KEY_ALIAS = "partner1clientinvalid";
protected static final String TRUSTSTORE_VALID_CERTIFICATE_ALIAS = "interca";
protected static final String TRUSTSTORE_INVALID_CERTIFICATE_ALIAS = "interca-invalid";

@Parameterized.Parameter public String keyStoreWrapperDescription;

@Parameterized.Parameter(1)
public boolean keystoreWrapperConfiguredWithTruststore;

@Parameterized.Parameter(2)
public KeyStoreWrapper keyStoreWrapper;

protected static Path toPath(final String path) throws Exception {
return null == path
? null
: Path.of(BaseKeyStoreFileWrapperTest.class.getResource(path).toURI());
}

@Test
public void getPublicKey_WithValidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getPublicKey(KEYSTORE_VALID_KEY_ALIAS))
.as("Public key is not null")
.isNotNull();
}

@Test
public void getPublicKey_WithInvalidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getPublicKey(KEYSTORE_INVALID_KEY_ALIAS))
.as("Public key is null")
.isNull();
}

@Test
public void getPrivateKey_WithValidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getPrivateKey(KEYSTORE_VALID_KEY_ALIAS))
.as("Private key is not null")
.isNotNull();
}

@Test
public void getPrivateKey_WithInvalidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getPrivateKey(KEYSTORE_INVALID_KEY_ALIAS))
.as("Private key is null")
.isNull();
}

@Test
public void getCertificate_WithValidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getCertificate(KEYSTORE_VALID_KEY_ALIAS))
.as("Certificate is not null")
.isNotNull();
}

@Test
public void getCertificate_WithInvalidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getCertificate(KEYSTORE_INVALID_KEY_ALIAS))
.as("Certificate is null")
.isNull();
}

@Test
public void getCertificateChain_WithValidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getCertificateChain(KEYSTORE_VALID_KEY_ALIAS))
.as("Certificate chain is not null")
.isNotNull();
}

@Test
public void getCertificateChain_WithInvalidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getCertificateChain(KEYSTORE_INVALID_KEY_ALIAS))
.as("Certificate is null")
.isNull();
}

@Test
public void getCertificate_FromTruststore_WithValidAlias_ReturnsExpectedValue() {
final Certificate certificate =
keyStoreWrapper.getCertificate(TRUSTSTORE_VALID_CERTIFICATE_ALIAS);
if (keystoreWrapperConfiguredWithTruststore) {
assertThat(certificate).as("Certificate is not null").isNotNull();
} else {
assertThat(certificate).as("Certificate is null").isNull();
}
}

@Test
public void getCertificate_FromTruststore_WithInvalidAlias_ReturnsExpectedValue() {
assertThat(keyStoreWrapper.getPrivateKey(TRUSTSTORE_INVALID_CERTIFICATE_ALIAS))
.as("Certificate is null")
.isNull();
}

@Test
public void getCRLS_Check() {
assertThat(keyStoreWrapper.getCRLs()).as("CRLs is not null").isNotNull();
assertThat(keyStoreWrapper.getCRLs().size()).as("CRLs size matches").isEqualTo(2);
}
}
Original file line number Diff line number Diff line change
@@ -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 java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class CryptoTestUtil {

private static final Logger LOG = LogManager.getLogger();

private CryptoTestUtil() {}

public static boolean isNSSLibInstalled() {
try {
final String nssLibPath = getNSSLibPath();
return nssLibPath != null && !nssLibPath.trim().isEmpty();
} catch (final Exception e) {
LOG.info("NSS library does not seem to be installed!", e);
}
return false;
}

public static String getNSSLibPath() throws IOException, InterruptedException {
String nssLibPath = "";
final String centOS_nssPathCmd =
"whereis libnssdbm3 | grep -o \"\\/.*libnssdbm3\\.[0-9a-z]* \" | sed 's/\\/libnssdbm3.*//g'";
final String debian_nssPathCmd =
"whereis libnss3 | grep -o \".*libnss3.[0-9a-z]\" | sed 's/lib.* \\(\\/.*\\)\\/lib.*/\\1/'";
final String macOS_nssPathCmd = "dirname `which certutil` | sed 's/bin/lib/g'";

nssLibPath = executeSystemCmd(centOS_nssPathCmd).orElse(nssLibPath);
LOG.info("centOS_nssPathCmd: {}", nssLibPath);
if ("".equals(nssLibPath)) {
nssLibPath = executeSystemCmd(debian_nssPathCmd).orElse(nssLibPath);
LOG.info("debian_nssPathCmd: {}", nssLibPath);
}
if ("".equals(nssLibPath)) {
nssLibPath = executeSystemCmd(macOS_nssPathCmd).orElse(nssLibPath);
LOG.info("macOS_nssPathCmd: {}", nssLibPath);
}
LOG.info("Detected NSS library path: {}", nssLibPath);
return nssLibPath;
}

public static Optional<String> executeSystemCmd(final String cmd)
throws IOException, InterruptedException {
final Process p = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
try {
if (p.waitFor() == 0) {
final java.util.Scanner s =
new java.util.Scanner(p.getInputStream(), StandardCharsets.UTF_8.name())
.useDelimiter("\\A");
if (s.hasNext()) {
return Optional.of(s.next().replace("\r", "").replace("\n", ""));
}
}
} finally {
if (p != null) {
p.destroy();
}
}
return Optional.empty();
}
}
Loading

0 comments on commit 413d62a

Please sign in to comment.