Support CSR signing, certificate revocation and CRL retrieval.

We now support CSR signing, certificate revocation and CRL retrieval via VaultPkiTemplate.

Closes gh-125.
This commit is contained in:
Mark Paluch
2017-10-19 08:16:16 +02:00
parent f46066382b
commit b30d81fa43
10 changed files with 514 additions and 112 deletions

View File

@@ -15,10 +15,13 @@
*/
package org.springframework.vault.core;
import java.io.InputStream;
import org.springframework.vault.VaultException;
import org.springframework.vault.support.CertificateBundle;
import org.springframework.vault.support.VaultCertificateRequest;
import org.springframework.vault.support.VaultCertificateResponse;
import org.springframework.vault.support.VaultSignCertificateRequestResponse;
/**
* Interface that specifies PKI backend-related operations.
@@ -29,7 +32,7 @@ import org.springframework.vault.support.VaultCertificateResponse;
* private key and CSR, submitting to a CA, and waiting for a verification and signing
* process to complete. Vault's built-in authentication and authorization mechanisms
* provide the verification functionality.
*
*
* @author Mark Paluch
* @see <a href=
* "https://www.vaultproject.io/docs/secrets/pki/index.html">https://www.vaultproject.io/docs/secrets/pki/index.html</a>
@@ -52,4 +55,53 @@ public interface VaultPkiOperations {
*/
VaultCertificateResponse issueCertificate(String roleName,
VaultCertificateRequest certificateRequest) throws VaultException;
/**
* Signs a CSR using Vault's PKI backend given a {@code roleName}, {@code csr} and
* {@link VaultCertificateRequest}. The issuing CA certificate is returned as well, so
* that only the root CA need be in a client's trust store. Certificates use DER
* format and are base64 encoded.
*
* @param roleName must not be empty or {@literal null}.
* @param csr must not be empty or {@literal null}.
* @param certificateRequest must not be {@literal null}.
* @return the {@link VaultCertificateResponse} containing a
* {@link org.springframework.vault.support.Certificate} .
* @since 2.0
* @see <a
* href="https://www.vaultproject.io/docs/secrets/pki/index.html#pki-issue">POST
* /pki/sign/[role name]</a>
*/
VaultSignCertificateRequestResponse signCertificateRequest(String roleName,
String csr, VaultCertificateRequest certificateRequest) throws VaultException;
/**
* Revokes a certificate using its serial number. This is an alternative option to the
* standard method of revoking using Vault lease IDs. A successful revocation will
* rotate the CRL
*
* @param serialNumber must not be empty or {@literal null}.
* @since 2.0
* @see <a
* href="https://www.vaultproject.io/docs/secrets/pki/index.html#revoke-certificate">POST
* /pki/revoke</a>
*/
void revoke(String serialNumber) throws VaultException;
/**
* Retrieves the current CRL in raw form. This endpoint is suitable for usage in the
* CRL distribution points extension in a CA certificate. This is a bare endpoint that
* does not return a standard Vault data structure. Returns data {@link Encoding#DER}
* or {@link Encoding#PEM} encoded.
*
* @return {@link java.io.InputStream} containing the encoded CRL.
* @since 2.0
* @see <a href="https://www.vaultproject.io/api/secret/pki/index.html#read-crl">GET
* /pki/crl</a>
*/
InputStream getCrl(Encoding encoding) throws VaultException;
enum Encoding {
DER, PEM,
}
}

View File

@@ -15,15 +15,20 @@
*/
package org.springframework.vault.core;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.client.VaultResponses;
import org.springframework.vault.support.VaultCertificateRequest;
import org.springframework.vault.support.VaultCertificateResponse;
import org.springframework.vault.support.VaultSignCertificateRequestResponse;
import org.springframework.web.client.HttpStatusCodeException;
/**
@@ -54,13 +59,106 @@ public class VaultPkiTemplate implements VaultPkiOperations {
}
@Override
public VaultCertificateResponse issueCertificate(final String roleName,
public VaultCertificateResponse issueCertificate(String roleName,
VaultCertificateRequest certificateRequest) throws VaultException {
Assert.hasText(roleName, "Role name must not be empty");
Assert.notNull(certificateRequest, "Certificate request must not be null");
final Map<String, Object> request = new HashMap<>();
return requestCertificate(roleName, "{path}/issue/{roleName}",
createIssueRequest(certificateRequest), VaultCertificateResponse.class);
}
@Override
public VaultSignCertificateRequestResponse signCertificateRequest(String roleName,
String csr, VaultCertificateRequest certificateRequest) throws VaultException {
Assert.hasText(roleName, "Role name must not be empty");
Assert.hasText(csr, "CSR name must not be empty");
Assert.notNull(certificateRequest, "Certificate request must not be null");
Map<String, Object> body = createIssueRequest(certificateRequest);
body.put("csr", csr);
return requestCertificate(roleName, "{path}/sign/{roleName}", body,
VaultSignCertificateRequestResponse.class);
}
private <T> T requestCertificate(String roleName, String requestPath,
Map<String, Object> request, Class<T> responseType) {
request.put("format", "der");
T response = vaultOperations.doWithSession(restOperations -> {
try {
return restOperations.postForObject(requestPath, request, responseType,
path, roleName);
}
catch (HttpStatusCodeException e) {
throw VaultResponses.buildException(e);
}
});
Assert.state(response != null, "VaultCertificateResponse must not be null");
return response;
}
@Override
public void revoke(String serialNumber) throws VaultException {
Assert.hasText(serialNumber, "Serial number must not be null or empty");
vaultOperations.doWithSession(restOperations -> {
try {
restOperations.postForObject("{path}/revoke",
Collections.singletonMap("serial_number", serialNumber),
Map.class, path);
return null;
}
catch (HttpStatusCodeException e) {
throw VaultResponses.buildException(e);
}
});
}
@Override
public InputStream getCrl(Encoding encoding) throws VaultException {
Assert.notNull(encoding, "Encoding must not be null");
return vaultOperations.doWithSession(restOperations -> {
String requestPath = encoding == Encoding.DER ? "{path}/crl"
: "{path}/crl/pem";
try {
ResponseEntity<byte[]> response = restOperations.getForEntity(
requestPath, byte[].class, path);
return new ByteArrayInputStream(response.getBody());
}
catch (HttpStatusCodeException e) {
throw VaultResponses.buildException(e);
}
});
}
/**
* Create a request body stub for {@code pki/issue} and {@code pki/sign} from
* {@link VaultCertificateRequest}.
*
* @param certificateRequest must not be {@literal null}.
* @return the body as {@link Map}.
*/
private static Map<String, Object> createIssueRequest(
VaultCertificateRequest certificateRequest) {
Assert.notNull(certificateRequest, "Certificate request must not be null");
Map<String, Object> request = new HashMap<>();
request.put("common_name", certificateRequest.getCommonName());
if (!certificateRequest.getAltNames().isEmpty()) {
@@ -81,26 +179,9 @@ public class VaultPkiTemplate implements VaultPkiOperations {
request.put("ttl", certificateRequest.getTtl());
}
request.put("format", "der");
if (certificateRequest.isExcludeCommonNameFromSubjectAltNames()) {
request.put("exclude_cn_from_sans", true);
}
VaultCertificateResponse response = vaultOperations
.doWithSession(restOperations -> {
try {
return restOperations.postForObject("{path}/issue/{roleName}",
request, VaultCertificateResponse.class, path, roleName);
}
catch (HttpStatusCodeException e) {
throw VaultResponses.buildException(e);
}
});
Assert.state(response != null, "VaultCertificateResponse must not be null");
return response;
return request;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.
*/
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 com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.util.Assert;
import org.springframework.vault.VaultException;
/**
* Value object representing a certificate consisting of the certificate and the issuer
* certificate. Certificate and keys can be either DER or PEM encoded. DER-encoded
* certificates can be converted to a {@link X509Certificate}.
*
* @author Mark Paluch
* @since 2.0
* @see #getX509Certificate()
* @see #getIssuingCaCertificate()
*/
public class Certificate {
private final String serialNumber;
private final String certificate;
private final String issuingCaCertificate;
Certificate(@JsonProperty("serial_number") String serialNumber,
@JsonProperty("certificate") String certificate,
@JsonProperty("issuing_ca") String issuingCaCertificate) {
this.serialNumber = serialNumber;
this.certificate = certificate;
this.issuingCaCertificate = issuingCaCertificate;
}
/**
* Create a {@link Certificate} given a private key with certificates and the serial
* number.
*
* @param serialNumber must not be empty or {@literal null}.
* @param certificate must not be empty or {@literal null}.
* @param issuingCaCertificate must not be empty or {@literal null}.
* @return the {@link Certificate}
*/
public static Certificate of(String serialNumber, String certificate,
String issuingCaCertificate) {
Assert.hasText(serialNumber, "Serial number must not be empty");
Assert.hasText(certificate, "Certificate must not be empty");
Assert.hasText(issuingCaCertificate, "Issuing CA certificate must not be empty");
return new Certificate(serialNumber, certificate, issuingCaCertificate);
}
/**
* @return the serial number.
*/
public String getSerialNumber() {
return this.serialNumber;
}
/**
* @return encoded certificate (PEM or DER-encoded).
*/
public String getCertificate() {
return this.certificate;
}
/**
* @return encoded certificate of the issuing CA (PEM or DER-encoded).
*/
public String getIssuingCaCertificate() {
return this.issuingCaCertificate;
}
/**
* Retrieve the certificate as {@link X509Certificate}. Only supported if certificate
* is DER-encoded.
*
* @return the {@link X509Certificate}.
*/
public X509Certificate getX509Certificate() {
try {
byte[] bytes = Base64.decode(getCertificate());
return KeystoreUtil.getCertificate(bytes);
}
catch (IOException | CertificateException e) {
throw new VaultException("Cannot create Certificate from certificate", e);
}
}
/**
* Retrieve the issuing CA certificate as {@link X509Certificate}. Only supported if
* certificate is DER-encoded.
*
* @return the issuing CA {@link X509Certificate}.
*/
public X509Certificate getX509IssuerCertificate() {
try {
byte[] bytes = Base64.decode(getIssuingCaCertificate());
return KeystoreUtil.getCertificate(bytes);
}
catch (IOException | CertificateException e) {
throw new VaultException(
"Cannot create Certificate from issuing CA certificate", e);
}
}
/**
* Create a trust store as {@link KeyStore} from this {@link Certificate} containing
* the certificate chain. Only supported if certificate is DER-encoded.
*
* @return the {@link KeyStore} containing the private key and certificate chain.
*/
public KeyStore createTrustStore() {
try {
return KeystoreUtil.createKeyStore(getX509Certificate(),
getX509IssuerCertificate());
}
catch (GeneralSecurityException | IOException e) {
throw new VaultException("Cannot create KeyStore", e);
}
}
}

View File

@@ -18,7 +18,6 @@ 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;
@@ -38,24 +37,16 @@ import org.springframework.vault.VaultException;
* @see #getX509Certificate()
* @see #getIssuingCaCertificate()
*/
public class CertificateBundle {
private final String serialNumber;
private final String certificate;
private final String issuingCaCertificate;
public class CertificateBundle extends Certificate {
private final String privateKey;
private CertificateBundle(@JsonProperty("serial_number") String serialNumber,
CertificateBundle(@JsonProperty("serial_number") String serialNumber,
@JsonProperty("certificate") String certificate,
@JsonProperty("issuing_ca") String issuingCaCertificate,
@JsonProperty("private_key") String privateKey) {
this.serialNumber = serialNumber;
this.certificate = certificate;
this.issuingCaCertificate = issuingCaCertificate;
super(serialNumber, certificate, issuingCaCertificate);
this.privateKey = privateKey;
}
@@ -81,27 +72,6 @@ public class CertificateBundle {
privateKey);
}
/**
* @return the serial number.
*/
public String getSerialNumber() {
return this.serialNumber;
}
/**
* @return encoded certificate (PEM or DER-encoded).
*/
public String getCertificate() {
return this.certificate;
}
/**
* @return encoded certificate of the issuing CA (PEM or DER-encoded).
*/
public String getIssuingCaCertificate() {
return this.issuingCaCertificate;
}
/**
* @return the private key (decrypted form, PEM or DER-encoded)
*/
@@ -127,41 +97,6 @@ public class CertificateBundle {
}
}
/**
* Retrieve the certificate as {@link X509Certificate}. Only supported if certificate
* is DER-encoded.
*
* @return the {@link X509Certificate}.
*/
public X509Certificate getX509Certificate() {
try {
byte[] bytes = Base64.decode(getCertificate());
return KeystoreUtil.getCertificate(bytes);
}
catch (IOException | CertificateException e) {
throw new VaultException("Cannot create Certificate from certificate", e);
}
}
/**
* Retrieve the issuing CA certificate as {@link X509Certificate}. Only supported if
* certificate is DER-encoded.
*
* @return the issuing CA {@link X509Certificate}.
*/
public X509Certificate getX509IssuerCertificate() {
try {
byte[] bytes = Base64.decode(getIssuingCaCertificate());
return KeystoreUtil.getCertificate(bytes);
}
catch (IOException | CertificateException e) {
throw new VaultException(
"Cannot create Certificate from issuing CA certificate", e);
}
}
/**
* Create a {@link KeyStore} from this {@link CertificateBundle} containing the
* private key and certificate chain. Only supported if certificate and private key

View File

@@ -67,6 +67,29 @@ class KeystoreUtil {
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
* @since 2.0
*/
static KeyStore createKeyStore(X509Certificate... certificates)
throws GeneralSecurityException, IOException {
KeyStore keyStore = createKeyStore();
int counter = 0;
for (X509Certificate certificate : certificates) {
keyStore.setCertificateEntry(String.format("cert_%d", counter++), certificate);
}
return keyStore;
}
static X509Certificate getCertificate(byte[] source) throws CertificateException,
IOException {

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.
*/
package org.springframework.vault.support;
/**
* Value object to bind Vault HTTP PKI issue certificate API responses.
*
* @author Mark Paluch
*/
public class VaultSignCertificateRequestResponse extends
VaultResponseSupport<Certificate> {
}

View File

@@ -16,6 +16,10 @@
package org.springframework.vault.core;
import java.io.File;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -28,10 +32,14 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StreamUtils;
import org.springframework.vault.VaultException;
import org.springframework.vault.core.VaultPkiOperations.Encoding;
import org.springframework.vault.support.Certificate;
import org.springframework.vault.support.CertificateBundle;
import org.springframework.vault.support.VaultCertificateRequest;
import org.springframework.vault.support.VaultCertificateResponse;
import org.springframework.vault.support.VaultSignCertificateRequestResponse;
import org.springframework.vault.util.IntegrationTestSupport;
import static org.assertj.core.api.Assertions.assertThat;
@@ -52,7 +60,7 @@ public class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
private VaultPkiOperations pkiOperations;
@Before
public void before() throws Exception {
public void before() {
pkiOperations = vaultOperations.opsForPki();
@@ -100,6 +108,43 @@ public class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
.isEqualTo("CN=hello.example.com");
}
@Test
public void signShouldSignCsr() {
String csr = "-----BEGIN CERTIFICATE REQUEST-----\n"
+ "MIICzTCCAbUCAQAwgYcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRl\n"
+ "MRUwEwYDVQQHEwxTYW4gVmF1bHRpbm8xFTATBgNVBAoTDFNwcmluZyBWYXVsdDEY\n"
+ "MBYGA1UEAxMPY3NyLmV4YW1wbGUuY29tMRswGQYJKoZIhvcNAQkBFgxzcHJpbmdA\n"
+ "dmF1bHQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVlDBT1gAONIp4\n"
+ "GQQ7BWDeqNzlscWqu5oQyfvw6oNFZzYWGVTgX/n72biv8d1Wx30MWpVYhbL0mk9m\n"
+ "Uu15elMZHPb4F4bk8VDSiB9527SwAd/QpkNC1RsPp2h6g2LvGPJ2eidHSlLtF2To\n"
+ "A4i6z0K0++nvYKSf9Af0sod2Z51xc9uPj/oN5z/8BQuGoCBpxJqgl7N/csMICixY\n"
+ "2fQcCUbdPPqE9INIInUHe3mPE/yvxko9aYGZ5jnrdZyiQaRRKBdWpvbRLKXQ78Fz\n"
+ "vXR3G33yn9JAN6wl1A916DiXzy2xHT19vyAn1hBUj2M6KFXChQ30oxTyTOqHCMLP\n"
+ "m/BSEOsPAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAYFssueiUh3YGxnXcQ4dp\n"
+ "ZqVWeVyOuGGaFJ4BA0drwJ9Mt/iNmPUTGE2oBNnh2R7e7HwGcNysFHZZOZBEQ0Hh\n"
+ "Vn93GO7cfaTOetK0VtDqis1VFQD0eVPWf5s6UqT/+XGrFRhwJ9hM+2FQSrUDFecs\n"
+ "+/605n1rD7qOj3vkGrtwvEUrxyRaQaKpPLHmVHENqV6F1NsO3Z27f2FWWAZF2VKN\n"
+ "cCQQJNc//DbIN3J3JSElpIDBDHctoBoQVnMiwpCbSA+CaAtlWYJKnAfhTKeqnNMy\n"
+ "qf3ACZ+1sBIuqSP7dEJ2KfIezaCPQ88+PAloRB52LFa+iq3yI7F5VzkwAvQFnTi+\n"
+ "cQ==\n" + "-----END CERTIFICATE REQUEST-----";
VaultCertificateRequest request = VaultCertificateRequest
.create("hello.example.com");
VaultSignCertificateRequestResponse certificateResponse = pkiOperations
.signCertificateRequest("testrole", csr, request);
Certificate data = certificateResponse.getData();
assertThat(data.getCertificate()).isNotEmpty();
assertThat(data.getIssuingCaCertificate()).isNotEmpty();
assertThat(data.getSerialNumber()).isNotEmpty();
assertThat(data.getX509Certificate().getSubjectX500Principal().getName())
.isEqualTo("CN=csr.example.com");
assertThat(data.createTrustStore()).isNotNull();
}
@Test(expected = VaultException.class)
public void issueCertificateFail() {
@@ -107,4 +152,44 @@ public class VaultPkiTemplateIntegrationTests extends IntegrationTestSupport {
pkiOperations.issueCertificate("testrole", request);
}
@Test
public void shouldRevokeCertificate() throws Exception {
VaultCertificateRequest request = VaultCertificateRequest
.create("foo.example.com");
VaultCertificateResponse certificateResponse = pkiOperations.issueCertificate(
"testrole", request);
BigInteger serial = new BigInteger(certificateResponse.getData()
.getSerialNumber().replaceAll("\\:", ""), 16);
pkiOperations.revoke(certificateResponse.getData().getSerialNumber());
try (InputStream in = pkiOperations.getCrl(Encoding.DER)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509CRL crl = (X509CRL) cf.generateCRL(in);
assertThat(crl.getRevokedCertificate(serial)).isNotNull();
}
}
@Test
public void shouldReturnCrl() throws Exception {
try (InputStream in = pkiOperations.getCrl(Encoding.DER)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
assertThat(cf.generateCRL(in)).isInstanceOf(X509CRL.class);
}
try (InputStream crl = pkiOperations.getCrl(Encoding.PEM)) {
byte[] bytes = StreamUtils.copyToByteArray(crl);
assertThat(bytes).isNotEmpty();
}
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.vault.support;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -29,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link CertificateBundle}.
*
*
* @author Mark Paluch
*/
public class CertificateBundleUnitTests {
@@ -56,24 +55,6 @@ public class CertificateBundleUnitTests {
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
}
@Test
public void getX509CertificateShouldReturnCertificate() throws Exception {
X509Certificate x509Certificate = certificateBundle.getX509Certificate();
assertThat(x509Certificate.getSubjectDN().getName()).isEqualTo(
"CN=hello.example.com");
}
@Test
public void getX509IssuerCertificateShouldReturnCertificate() throws Exception {
X509Certificate x509Certificate = certificateBundle.getX509IssuerCertificate();
assertThat(x509Certificate.getSubjectDN().getName()).startsWith(
"CN=Intermediate CA Certificate");
}
@Test
public void getAsKeystore() throws Exception {

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2017 the original author or authors.
*
* 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.
*/
package org.springframework.vault.support;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link Certificate}.
*
* @author Mark Paluch
*/
public class CertificateUnitTests {
Certificate certificate;
@SuppressWarnings("unchecked")
@Before
public void before() throws Exception {
Map<String, String> data = new ObjectMapper().readValue(
getClass().getResource("/certificate.json"), Map.class);
certificate = Certificate.of(data.get("serial_number"), data.get("certificate"),
data.get("issuing_ca"));
}
@Test
public void getX509CertificateShouldReturnCertificate() {
X509Certificate x509Certificate = certificate.getX509Certificate();
assertThat(x509Certificate.getSubjectDN().getName()).isEqualTo(
"CN=hello.example.com");
}
@Test
public void getX509IssuerCertificateShouldReturnCertificate() {
X509Certificate x509Certificate = certificate.getX509IssuerCertificate();
assertThat(x509Certificate.getSubjectDN().getName()).startsWith(
"CN=Intermediate CA Certificate");
}
@Test
public void getAsTrustStore() throws Exception {
KeyStore keyStore = certificate.createTrustStore();
assertThat(keyStore.size()).isEqualTo(2);
}
}

View File

@@ -9,15 +9,16 @@
* <<vault.repositories,Vault repository support>> based on Spring Data KeyValue.
* Transit batch encrypt and decrypt support.
* Policy management for policies stored as JSON.
* Support CSR signing, certificate revocation and CRL retrieval.
[[new-features.1-1-0]]
=== What's new in Spring Vault 1.1.0
* <<vault.authentication.awsiam,AWS IAM authentication>>.
* Configuration of encryption/decryption versions for transit keys.
* Configuration of encryption/decryption versions for transit keys.
* Pull mode for <<vault.authentication.approle,AppRole authentication>>.
* Transit batch encrypt and decrypt support.
* TTL-based generic secret rotation.
* TTL-based generic secret rotation.
[[new-features.1-0-0]]
=== What's new in Spring Vault 1.0