forked from hyperledger/besu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added PKI keystore Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
- Loading branch information
1 parent
68510e5
commit 17b2d53
Showing
13 changed files
with
920 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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' | ||
} |
141 changes: 141 additions & 0 deletions
141
pki/src/main/java/org/hyperledger/besu/pki/PkiConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
pki/src/main/java/org/hyperledger/besu/pki/PkiException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
170 changes: 170 additions & 0 deletions
170
pki/src/main/java/org/hyperledger/besu/pki/keystore/HardwareKeyStoreWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
Oops, something went wrong.