From 75f5cf98b0a51730b29acd98b122be36d89a7069 Mon Sep 17 00:00:00 2001 From: Alex Bremora Date: Sat, 8 Jan 2022 23:51:48 +0100 Subject: [PATCH] Add missing properties in `VaultCertificateRequest`. Original pull request: gh-685. Fixes gh-682 --- .../vault/core/VaultPkiTemplate.java | 4 + .../support/VaultCertificateRequest.java | 88 ++++++++++++++++--- .../VaultCertificateRequestUnitTests.java | 8 +- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultPkiTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultPkiTemplate.java index a9d63a31..0d337f81 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultPkiTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultPkiTemplate.java @@ -174,6 +174,10 @@ public class VaultPkiTemplate implements VaultPkiOperations { StringUtils.collectionToDelimitedString(certificateRequest.getUriSubjectAltNames(), ",")); } + if (!certificateRequest.getOtherSans().isEmpty()) { + request.put("other_sans", StringUtils.collectionToDelimitedString(certificateRequest.getOtherSans(), ",")); + } + if (certificateRequest.getTtl() != null) { request.put("ttl", certificateRequest.getTtl().get(ChronoUnit.SECONDS)); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java index 75656f13..35b64b27 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultCertificateRequest.java @@ -31,6 +31,8 @@ import org.springframework.util.Assert; */ public class VaultCertificateRequest { + private static String DEFAULT_FORMAT = "der"; + /** * The CN of the certificate. Should match the host name. */ @@ -51,6 +53,14 @@ public class VaultCertificateRequest { */ private final List uriSubjectAltNames; + /** + * Specifies custom OID/UTF8-string Subject Alternative Names. These must match values + * specified on the role in {@literal allowed_other_sans}. The format is the same as + * OpenSSL: {@literal ;:} where the only current valid type is UTF8. + * @since 2.4 + */ + private final List otherSans; + /** * Requested Time to Live */ @@ -58,17 +68,24 @@ public class VaultCertificateRequest { private final Duration ttl; /** - * Specifies the format for returned data. Can be pem, der, or pem_bundle; defaults to - * pem + * Specifies the format for returned data. Can be {@literal pem}, {@literal der}, or + * {@literal pem_bundle}; defaults to {@literal der} (in vault api the default is + * {@literal pem}). If der, the output is base64 encoded. If {@literal pem_bundle}, + * the certificate field will contain the private key and certificate, concatenated; + * if the issuing CA is not a Vault-derived self-signed root, this will be included as + * well. + * @since 2.4 */ private final String format; /** - * Specifies the format for marshaling the private key. Defaults to der which will - * return either base64-encoded DER or PEM-encoded DER, depending on the value of - * format. The other option is pkcs8 which will return the key marshalled as - * PEM-encoded PKCS8. + * Specifies the format for marshaling the private key. Defaults to {@literal der} + * which will return either base64-encoded DER or PEM-encoded DER, depending on the + * value of {@literal format}. The other option is {@literal pkcs8} which will return + * the key marshalled as PEM-encoded PKCS8. + * @since 2.4 */ + @Nullable private final String privateKeyFormat; /** @@ -79,17 +96,19 @@ public class VaultCertificateRequest { private final boolean excludeCommonNameFromSubjectAltNames; private VaultCertificateRequest(String commonName, List altNames, List ipSubjectAltNames, - List uriSubjectAltNames, @Nullable Duration ttl, String format, String privateKeyFormat, + List uriSubjectAltNames, List otherSans, @Nullable Duration ttl, + String format, @Nullable String privateKeyFormat, boolean excludeCommonNameFromSubjectAltNames) { this.commonName = commonName; this.altNames = altNames; this.ipSubjectAltNames = ipSubjectAltNames; this.uriSubjectAltNames = uriSubjectAltNames; + this.otherSans = otherSans; this.ttl = ttl; + this.excludeCommonNameFromSubjectAltNames = excludeCommonNameFromSubjectAltNames; this.format = format; this.privateKeyFormat = privateKeyFormat; - this.excludeCommonNameFromSubjectAltNames = excludeCommonNameFromSubjectAltNames; } /** @@ -124,6 +143,10 @@ public class VaultCertificateRequest { return this.uriSubjectAltNames; } + public List getOtherSans() { + return this.otherSans; + } + @Nullable public Duration getTtl() { return this.ttl; @@ -133,6 +156,7 @@ public class VaultCertificateRequest { return format; } + @Nullable public String getPrivateKeyFormat() { return privateKeyFormat; } @@ -152,11 +176,14 @@ public class VaultCertificateRequest { private List uriSubjectAltNames = new ArrayList<>(); + private List otherSans = new ArrayList<>(); + @Nullable private Duration ttl; - private String format; + private String format = DEFAULT_FORMAT; + @Nullable private String privateKeyFormat; private boolean excludeCommonNameFromSubjectAltNames; @@ -259,6 +286,35 @@ public class VaultCertificateRequest { return this; } + /** + * Configure custom OID/UTF8-string subject alternative names. Replaces previously + * configured other subject alt names. + * @param otherSans must not be {@literal null}. + * @return {@code this} {@link VaultCertificateRequestBuilder}. + * @since 2.4 + */ + public VaultCertificateRequestBuilder otherSans(Iterable otherSans) { + + Assert.notNull(otherSans, "Other subject alt names must not be null"); + + this.otherSans = toList(uriSubjectAltNames); + return this; + } + + /** + * Add custom OID/UTF8-string subject alternative name. + * @param otherSans must not be empty or {@literal null}. + * @return {@code this} {@link VaultCertificateRequestBuilder}. + * @since 2.4 + */ + public VaultCertificateRequestBuilder withOtherSans(String otherSans) { + + Assert.hasText(otherSans, "Other subject alt name must not be empty"); + + this.otherSans.add(otherSans); + return this; + } + /** * Configure a TTL. * @param ttl the time to live, in seconds, must not be negative. @@ -392,8 +448,20 @@ public class VaultCertificateRequest { uriSubjectAltNames = java.util.Collections.unmodifiableList(new ArrayList<>(this.uriSubjectAltNames)); } + List otherSans; + switch (this.otherSans.size()) { + case 0: + otherSans = java.util.Collections.emptyList(); + break; + case 1: + otherSans = java.util.Collections.singletonList(this.otherSans.get(0)); + break; + default: + otherSans = java.util.Collections.unmodifiableList(new ArrayList<>(this.otherSans)); + } + return new VaultCertificateRequest(this.commonName, altNames, ipSubjectAltNames, uriSubjectAltNames, - this.ttl, this.format, this.privateKeyFormat, this.excludeCommonNameFromSubjectAltNames); + otherSans, this.ttl, this.format, this.privateKeyFormat, this.excludeCommonNameFromSubjectAltNames); } private static List toList(Iterable iter) { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/support/VaultCertificateRequestUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/support/VaultCertificateRequestUnitTests.java index 58b2ec79..b065b7e1 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/support/VaultCertificateRequestUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/support/VaultCertificateRequestUnitTests.java @@ -49,15 +49,19 @@ class VaultCertificateRequestUnitTests { .withAltName("alt") // .withIpSubjectAltName("127.0.0.1") // .withUriSubjectAltName("hello.world") // + .withOtherSans("email;UTF-8:me@example.com") // .excludeCommonNameFromSubjectAltNames() // + .format("pem") // + .privateKeyFormat("der") // .build(); assertThat(request.getCommonName()).isEqualTo("hello.com"); assertThat(request.getAltNames()).hasSize(1).contains("alt"); assertThat(request.getIpSubjectAltNames()).containsOnly("127.0.0.1"); assertThat(request.getUriSubjectAltNames()).containsOnly("hello.world"); + assertThat(request.getOtherSans()).containsOnly("email;UTF-8:me@example.com"); assertThat(request.isExcludeCommonNameFromSubjectAltNames()).isTrue(); - assertThat(request.getCommonName()).isEqualTo("hello.com"); + assertThat(request.getFormat()).isEqualTo("pem"); + assertThat(request.getPrivateKeyFormat()).isEqualTo("der"); } - }