Expose CA chain through CertificateBundle.
We now expose access to the CA chain by providing getX509IssuerCertificates(). Additionally, a KeyStore can be created containing the full CA chain. Closes gh-648.
This commit is contained in:
@@ -18,8 +18,12 @@ package org.springframework.vault.support;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@@ -42,12 +46,15 @@ public class CertificateBundle extends Certificate {
|
||||
|
||||
private final String privateKey;
|
||||
|
||||
private final List<String> caChain;
|
||||
|
||||
CertificateBundle(@JsonProperty("serial_number") String serialNumber,
|
||||
@JsonProperty("certificate") String certificate, @JsonProperty("issuing_ca") String issuingCaCertificate,
|
||||
@JsonProperty("private_key") String privateKey) {
|
||||
@JsonProperty("ca_chain") List<String> caChain, @JsonProperty("private_key") String privateKey) {
|
||||
|
||||
super(serialNumber, certificate, issuingCaCertificate);
|
||||
this.privateKey = privateKey;
|
||||
this.caChain = caChain;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +74,8 @@ public class CertificateBundle extends Certificate {
|
||||
Assert.hasText(issuingCaCertificate, "Issuing CA certificate must not be empty");
|
||||
Assert.hasText(privateKey, "Private key must not be empty");
|
||||
|
||||
return new CertificateBundle(serialNumber, certificate, issuingCaCertificate, privateKey);
|
||||
return new CertificateBundle(serialNumber, certificate, issuingCaCertificate,
|
||||
Collections.singletonList(issuingCaCertificate), privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,16 +110,64 @@ public class CertificateBundle extends Certificate {
|
||||
* @return the {@link KeyStore} containing the private key and certificate chain.
|
||||
*/
|
||||
public KeyStore createKeyStore(String keyAlias) {
|
||||
return createKeyStore(keyAlias, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link KeyStore} from this {@link CertificateBundle} containing the
|
||||
* private key and certificate chain. Only supported if certificate and private key
|
||||
* are DER-encoded.
|
||||
* @param keyAlias the key alias to use.
|
||||
* @param includeCaChain whether to include the certificate authority chain instead of
|
||||
* just the issuer certificate.
|
||||
* @return the {@link KeyStore} containing the private key and certificate chain.
|
||||
* @since 2.3.3
|
||||
*/
|
||||
public KeyStore createKeyStore(String keyAlias, boolean includeCaChain) {
|
||||
|
||||
Assert.hasText(keyAlias, "Key alias must not be empty");
|
||||
|
||||
try {
|
||||
return KeystoreUtil.createKeyStore(keyAlias, getPrivateKeySpec(), getX509Certificate(),
|
||||
getX509IssuerCertificate());
|
||||
|
||||
List<X509Certificate> certificates = new ArrayList<>();
|
||||
certificates.add(getX509Certificate());
|
||||
|
||||
if (includeCaChain) {
|
||||
certificates.addAll(getX509IssuerCertificates());
|
||||
}
|
||||
else {
|
||||
certificates.add(getX509IssuerCertificate());
|
||||
}
|
||||
|
||||
return KeystoreUtil.createKeyStore(keyAlias, getPrivateKeySpec(),
|
||||
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}. Only
|
||||
* supported if certificates are DER-encoded.
|
||||
* @return the issuing CA {@link X509Certificate}.
|
||||
* @since 2.3.3
|
||||
*/
|
||||
public List<X509Certificate> getX509IssuerCertificates() {
|
||||
|
||||
List<X509Certificate> certificates = new ArrayList<>();
|
||||
|
||||
for (String data : caChain) {
|
||||
try {
|
||||
byte[] bytes = Base64Utils.decodeFromString(data);
|
||||
certificates.add(KeystoreUtil.getCertificate(bytes));
|
||||
}
|
||||
catch (CertificateException e) {
|
||||
throw new VaultException("Cannot create Certificate from issuing CA certificate", e);
|
||||
}
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.vault.core;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509CRL;
|
||||
import java.security.cert.X509Certificate;
|
||||
@@ -78,10 +80,11 @@ class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
}
|
||||
|
||||
File workDir = findWorkDir(new File(System.getProperty("user.dir")));
|
||||
String caCert = Files.contentOf(new File(workDir, "ca/certs/ca.cert.pem"), "US-ASCII");
|
||||
String cert = Files.contentOf(new File(workDir, "ca/certs/intermediate.cert.pem"), "US-ASCII");
|
||||
String key = Files.contentOf(new File(workDir, "ca/private/intermediate.decrypted.key.pem"), "US-ASCII");
|
||||
|
||||
Map<String, String> pembundle = Collections.singletonMap("pem_bundle", cert + key);
|
||||
Map<String, String> pembundle = Collections.singletonMap("pem_bundle", cert + key + caCert);
|
||||
|
||||
this.vaultOperations.write("pki/config/ca", pembundle);
|
||||
|
||||
@@ -96,7 +99,7 @@ class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueCertificateShouldCreateCertificate() {
|
||||
void issueCertificateShouldCreateCertificate() throws KeyStoreException {
|
||||
|
||||
VaultCertificateRequest request = VaultCertificateRequest.create("hello.example.com");
|
||||
|
||||
@@ -109,6 +112,13 @@ class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
assertThat(data.getIssuingCaCertificate()).isNotEmpty();
|
||||
assertThat(data.getSerialNumber()).isNotEmpty();
|
||||
assertThat(data.getX509Certificate().getSubjectX500Principal().getName()).isEqualTo("CN=hello.example.com");
|
||||
assertThat(data.getX509IssuerCertificates()).hasSize(2);
|
||||
|
||||
KeyStore keyStore = data.createKeyStore("vault");
|
||||
assertThat(keyStore.getCertificateChain("vault")).hasSize(2);
|
||||
|
||||
KeyStore keyStoreWithCaChain = data.createKeyStore("vault", true);
|
||||
assertThat(keyStoreWithCaChain.getCertificateChain("vault")).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user