Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overload createKeyStore to allow password parameter to be set Fixes gh-708 #711

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
*
* @author Mark Paluch
* @author Alex Bremora
* @author Bogdan Cardos
* @see #getPrivateKeySpec()
* @see #getX509Certificate()
* @see #getIssuingCaCertificate()
Expand Down Expand Up @@ -147,13 +148,13 @@ public String getPrivateKeyType() {
*/
public String getRequiredPrivateKeyType() {

String privateKeyType = getPrivateKeyType();
String requiredPrivateKeyType = getPrivateKeyType();

if (privateKeyType == null) {
if (requiredPrivateKeyType == null) {
throw new IllegalStateException("Private key type is not set");
}

return privateKeyType;
return requiredPrivateKeyType;
}

/**
Expand Down Expand Up @@ -181,6 +182,18 @@ public KeyStore createKeyStore(String keyAlias) {
return createKeyStore(keyAlias, false);
}

/**
* Create a {@link KeyStore} from this {@link CertificateBundle} containing the
* private key and certificate chain.
* @param keyAlias the key alias to use.
* @param password the password to use.
* @return the {@link KeyStore} containing the private key and certificate chain.
* @since 3.0.0
*/
public KeyStore createKeyStore(String keyAlias, String password) {
return createKeyStore(keyAlias, false, password);
}

/**
* Create a {@link KeyStore} from this {@link CertificateBundle} containing the
* private key and certificate chain.
Expand Down Expand Up @@ -214,6 +227,41 @@ public KeyStore createKeyStore(String keyAlias, boolean includeCaChain) {
}
}

/**
* Create a {@link KeyStore} from this {@link CertificateBundle} containing the
* private key and certificate chain.
* @param keyAlias the key alias to use.
* @param includeCaChain whether to include the certificate authority chain instead of
* just the issuer certificate.
* @param password the password to use.
* @return the {@link KeyStore} containing the private key and certificate chain.
* @since 3.0.0
*/
public KeyStore createKeyStore(String keyAlias, boolean includeCaChain, String password) {
sodrac marked this conversation as resolved.
Show resolved Hide resolved

Assert.hasText(keyAlias, "Key alias must not be empty");
Assert.hasText(password, "Password must not be empty");

try {

List<X509Certificate> certificates = new ArrayList<>();
certificates.add(getX509Certificate());

if (includeCaChain) {
certificates.addAll(getX509IssuerCertificates());
}
else {
certificates.add(getX509IssuerCertificate());
}

return KeystoreUtil.createKeyStore(keyAlias, getPrivateKeySpec(), password,
certificates.toArray(new X509Certificate[0]));
}
catch (GeneralSecurityException | IOException e) {
throw new VaultException("Cannot create KeyStore", e);
}
}

/**
* Retrieve the issuing CA certificates as list of {@link X509Certificate}.
* @return the issuing CA {@link X509Certificate}.
Expand All @@ -223,7 +271,7 @@ public List<X509Certificate> getX509IssuerCertificates() {

List<X509Certificate> certificates = new ArrayList<>();

for (String data : caChain) {
for (String data : this.caChain) {
try {
certificates.addAll(getCertificates(data));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* with the certificate chain and its private key.
*
* @author Mark Paluch
* @author Bogdan Cardos
*/
class KeystoreUtil {

Expand Down Expand Up @@ -73,11 +74,16 @@ class KeystoreUtil {
/**
* Create a {@link KeyStore} containing the {@link KeySpec} and {@link X509Certificate
* certificates} using the given {@code keyAlias}.
* @param keyAlias
* @param certificates
* @return
* @throws GeneralSecurityException
* @throws IOException
* @param keyAlias the key alias to use.
* @param privateKeySpec the private key to use.
* @param certificates the certificate chain to use.
* @return the {@link KeyStore} containing the private key and certificate chain.
* @throws GeneralSecurityException if exception occur when creating the instance of
* the {@link KeyStore}
* @throws IOException if there is an I/O or format problem with the keystore data, if
* a password is required but not given, or if the given password was incorrect. If
* the error is due to a wrong password, the {@link Throwable#getCause cause} of the
* {@code IOException} should be an {@code UnrecoverableKeyException}
*/
static KeyStore createKeyStore(String keyAlias, KeySpec privateKeySpec, X509Certificate... certificates)
throws GeneralSecurityException, IOException {
Expand All @@ -97,13 +103,52 @@ static KeyStore createKeyStore(String keyAlias, KeySpec privateKeySpec, X509Cert
return keyStore;
}

/**
* Create a {@link KeyStore} containing the {@link KeySpec} and {@link X509Certificate
* certificates} using the given {@code keyAlias} and {@code keyPassword}.
* @param keyAlias the key alias to use.
* @param privateKeySpec the private key to use.
* @param keyPassword the password to use.
* @param certificates the certificate chain to use.
* @return the {@link KeyStore} containing the private key and certificate chain.
* @throws GeneralSecurityException if exception occur when creating the instance of
* the {@link KeyStore}
* @throws IOException if there is an I/O or format problem with the keystore data, if
* a password is required but not given, or if the given password was incorrect. If
* the error is due to a wrong password, the {@link Throwable#getCause cause} of the
* {@code IOException} should be an {@code UnrecoverableKeyException}
*/
static KeyStore createKeyStore(String keyAlias, KeySpec privateKeySpec, String keyPassword,
X509Certificate... certificates) throws GeneralSecurityException, IOException {

PrivateKey privateKey = (privateKeySpec instanceof RSAPrivateKeySpec
|| privateKeySpec instanceof PKCS8EncodedKeySpec) ? RSA_KEY_FACTORY.generatePrivate(privateKeySpec)
: EC_KEY_FACTORY.generatePrivate(privateKeySpec);

char[] password = (keyPassword == null || keyPassword.isBlank()) ? new char[0] : keyPassword.toCharArray();

KeyStore keyStore = createKeyStore();

List<X509Certificate> certChain = new ArrayList<>();
Collections.addAll(certChain, certificates);

keyStore.setKeyEntry(keyAlias, privateKey, password,
certChain.toArray(new java.security.cert.Certificate[certChain.size()]));

return keyStore;
}

/**
* Create a {@link KeyStore} containing the {@link X509Certificate certificates}
* stored with as {@code cert_0, cert_1...cert_N}.
* @param certificates
* @return
* @throws GeneralSecurityException
* @throws IOException
* @param certificates the certificate chain to use.
* @return the {@link KeyStore} containing the certificate chain.
* @throws GeneralSecurityException if exception occur when creating the instance of
* the {@link KeyStore}
* @throws IOException if there is an I/O or format problem with the keystore data, if
* a password is required but not given, or if the given password was incorrect. If
* the error is due to a wrong password, the {@link Throwable#getCause cause} of the
* {@code IOException} should be an {@code UnrecoverableKeyException}
* @since 2.0
*/
static KeyStore createKeyStore(X509Certificate... certificates) throws GeneralSecurityException, IOException {
Expand Down Expand Up @@ -133,8 +178,12 @@ static List<X509Certificate> getCertificates(byte[] source) throws CertificateEx
/**
* Create an empty {@link KeyStore}.
* @return the {@link KeyStore}.
* @throws GeneralSecurityException
* @throws IOException
* @throws GeneralSecurityException if exception occur when creating the instance of
* the {@link KeyStore}
* @throws IOException if there is an I/O or format problem with the keystore data, if
* a password is required but not given, or if the given password was incorrect. If
* the error is due to a wrong password, the {@link Throwable#getCause cause} of the
* {@code IOException} should be an {@code UnrecoverableKeyException}
*/
private static KeyStore createKeyStore() throws GeneralSecurityException, IOException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,14 +276,12 @@ byte[] getContent() {

enum PemObjectType {

CERTIFICATE_REQUEST("CERTIFICATE REQUEST"), NEW_CERTIFICATE_REQUEST("NEW CERTIFICATE REQUEST"), CERTIFICATE(
"CERTIFICATE"), TRUSTED_CERTIFICATE("TRUSTED CERTIFICATE"), X509_CERTIFICATE(
"X509 CERTIFICATE"), X509_CRL("X509 CRL"), PKCS7("PKCS7"), CMS("CMS"), ATTRIBUTE_CERTIFICATE(
"ATTRIBUTE CERTIFICATE"), EC_PARAMETERS(
"EC PARAMETERS"), PUBLIC_KEY("PUBLIC KEY"), RSA_PUBLIC_KEY(
"RSA PUBLIC KEY"), RSA_PRIVATE_KEY("RSA PRIVATE KEY"), EC_PRIVATE_KEY(
"EC PRIVATE KEY"), ENCRYPTED_PRIVATE_KEY(
"ENCRYPTED PRIVATE KEY"), PRIVATE_KEY("PRIVATE KEY");
CERTIFICATE_REQUEST("CERTIFICATE REQUEST"), NEW_CERTIFICATE_REQUEST("NEW CERTIFICATE REQUEST"),
CERTIFICATE("CERTIFICATE"), TRUSTED_CERTIFICATE("TRUSTED CERTIFICATE"), X509_CERTIFICATE("X509 CERTIFICATE"),
X509_CRL("X509 CRL"), PKCS7("PKCS7"), CMS("CMS"), ATTRIBUTE_CERTIFICATE("ATTRIBUTE CERTIFICATE"),
EC_PARAMETERS("EC PARAMETERS"), PUBLIC_KEY("PUBLIC KEY"), RSA_PUBLIC_KEY("RSA PUBLIC KEY"),
RSA_PRIVATE_KEY("RSA PRIVATE KEY"), EC_PRIVATE_KEY("EC PRIVATE KEY"),
ENCRYPTED_PRIVATE_KEY("ENCRYPTED PRIVATE KEY"), PRIVATE_KEY("PRIVATE KEY");

// cache
private static final PemObjectType[] constants = values();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ void before() {

this.vaultOperations.write("pki/config/ca", pembundle);

Map<String, String> role = new HashMap<String, String>();
Map<String, String> role = new HashMap<>();
role.put("allowed_domains", "localhost,example.com");
role.put("allow_subdomains", "true");
role.put("allow_localhost", "true");
Expand Down Expand Up @@ -150,8 +150,14 @@ void issueCertificateShouldCreateCertificate() throws KeyStoreException {
KeyStore keyStore = data.createKeyStore("vault");
assertThat(keyStore.getCertificateChain("vault")).hasSize(2);

KeyStore keyStoreWithPassword = data.createKeyStore("vault", "mypassword");
assertThat(keyStoreWithPassword.getCertificateChain("vault")).hasSize(2);

KeyStore keyStoreWithCaChain = data.createKeyStore("vault", true);
assertThat(keyStoreWithCaChain.getCertificateChain("vault")).hasSize(3);

KeyStore keyStoreWithCaChainAndPassword = data.createKeyStore("vault", true, "mypassword");
assertThat(keyStoreWithCaChainAndPassword.getCertificateChain("vault")).hasSize(3);
}

@ParameterizedTest
Expand All @@ -175,8 +181,15 @@ void issueCertificateUsingFormat(KeyFixture keyFixture) throws Exception {
KeyStore keyStore = data.createKeyStore("vault");
assertThat(keyStore.getCertificateChain("vault")).hasSize(2);

KeyStore keyStoreWithPassword = data.createKeyStore("vault", "mypassword");
assertThat(keyStoreWithPassword.getCertificateChain("vault")).hasSize(2);

KeyStore keyStoreWithCaChain = data.createKeyStore("vault", true);
assertThat(keyStoreWithCaChain.getCertificateChain("vault")).hasSize(3);

KeyStore keyStoreWithCaChainAndPassword = data.createKeyStore("vault", true, "mypassword");
assertThat(keyStoreWithCaChainAndPassword.getCertificateChain("vault")).hasSize(3);

}

static Stream<KeyFixture> keyTypeFixtures() {
Expand Down Expand Up @@ -213,7 +226,7 @@ static class KeyFixture {

@Override
public String toString() {
return String.format("[%s, %s, %s]", format, privateKeyFormat, keyType);
return String.format("[%s, %s, %s]", this.format, this.privateKeyFormat, this.keyType);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ class CertificateBundleUnitTests {

CertificateBundle certificateBundle;

@SuppressWarnings("unchecked")
@BeforeEach
void before() {
certificateBundle = loadCertificateBundle("certificate.json");
this.certificateBundle = loadCertificateBundle("certificate.json");
}

@Test
Expand Down Expand Up @@ -103,9 +102,12 @@ void getAsKeystore() throws Exception {

CertificateBundle bundle = loadCertificateBundle("certificate.json");
KeyStore keyStore = bundle.createKeyStore("mykey");

assertThat(keyStore.size()).isEqualTo(1);
assertThat(keyStore.getCertificateChain("mykey")).hasSize(2);

KeyStore keyStoreWithPassword = bundle.createKeyStore("mykey", "mypassword");
assertThat(keyStoreWithPassword.size()).isEqualTo(1);
assertThat(keyStoreWithPassword.getCertificateChain("mykey")).hasSize(2);
}

@ParameterizedTest
Expand All @@ -116,8 +118,10 @@ void createKeystore(String path) {

CertificateBundle bundle = loadCertificateBundle(path);
KeyStore keyStore = bundle.createKeyStore("localhost");

assertThat(keyStore).isNotNull();

KeyStore keyStoreWithPassword = bundle.createKeyStore("localhost", "mypassword");
assertThat(keyStoreWithPassword).isNotNull();
}

@ParameterizedTest
Expand All @@ -126,11 +130,12 @@ void shouldCreateKeystore(String path) {

CertificateBundle bundle = loadCertificateBundle(path);
KeyStore keyStore = bundle.createKeyStore("localhost");

assertThat(keyStore).isNotNull();

KeyStore keyStoreWithPassword = bundle.createKeyStore("localhost", "mypassword");
assertThat(keyStoreWithPassword).isNotNull();
}

@SuppressWarnings("unchecked")
CertificateBundle loadCertificateBundle(String path) {

try {
Expand Down