From cec3d28fafe1eb4821c359c6a98cea99b66e6123 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 25 Oct 2017 11:33:32 +0200 Subject: [PATCH] Polishing. Remove VaultTransitContext from Hmac and Signature types and requests. Introduce SignatureValidation to encapsulate validation outcome. Refactor request objects to pure value objects. Build request body inside of VaultTransitTemplate from request objects. Refactor request types to use Plaintext/Signature/Hmac types instead of String primitives. Remove Hmac/Signature factory methods from bytes to keep Vault responses encapsulated. Reorder field order/parameters in signatures/methods according their parameter significance. Extend Javadoc, add tests, add author tags. Guard tests to run against the appropriate Vault version. Original pull request: gh-162. Closes: gh-162. --- .../vault/core/VaultTransitOperations.java | 86 ++++-- .../vault/core/VaultTransitTemplate.java | 113 ++++--- .../springframework/vault/support/Hmac.java | 78 ++--- .../vault/support/Signature.java | 79 ++--- .../vault/support/SignatureValidation.java | 66 ++++ .../vault/support/VaultHmacRequest.java | 216 +++++++------ .../vault/support/VaultSignRequest.java | 174 +++++------ .../VaultSignatureVerificationRequest.java | 285 ++++++++++-------- .../VaultTransitTemplateIntegrationTests.java | 224 ++++++++------ 9 files changed, 718 insertions(+), 603 deletions(-) create mode 100644 spring-vault-core/src/main/java/org/springframework/vault/support/SignatureValidation.java diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java index 00f769a5..ecaa5062 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java @@ -23,6 +23,7 @@ import org.springframework.vault.support.Hmac; import org.springframework.vault.support.Plaintext; import org.springframework.vault.support.RawTransitKey; import org.springframework.vault.support.Signature; +import org.springframework.vault.support.SignatureValidation; import org.springframework.vault.support.TransitKeyType; import org.springframework.vault.support.VaultDecryptionResult; import org.springframework.vault.support.VaultEncryptionResult; @@ -40,6 +41,7 @@ import org.springframework.vault.support.VaultTransitKeyCreationRequest; * @author Mark Paluch * @author Sven Schürmann * @author Praveendra Singh + * @author Luander Ribeiro * @see Transit * Secret Backend */ @@ -229,59 +231,77 @@ public interface VaultTransitOperations { String rewrap(String keyName, String ciphertext, VaultTransitContext transitContext); /** - * Generate HMAC digest of given data. + * Create a HMAC using {@code keyName} of given {@link Plaintext} using the default + * hash algorithm. The key can be of any type supported by transit; the raw key will + * be marshaled into bytes to be used for the HMAC function. If the key is of a type + * that supports rotation, the latest (current) version will be used. + * + * @param keyName must not be empty or {@literal null}. + * @param plaintext must not be {@literal null}. + * @return the digest of given data the default hash algorithm and the named key. + * @since 2.0 + */ + Hmac getHmac(String keyName, Plaintext plaintext); + + /** + * Create a HMAC using {@code keyName} of given {@link VaultHmacRequest} using the + * default hash algorithm. The key can be of any type supported by transit; the raw + * key will be marshaled into bytes to be used for the HMAC function. If the key is of + * a type that supports rotation, configured {@link VaultHmacRequest#getKeyVersion()} + * will be used. + * + * @param keyName must not be empty or {@literal null}. + * @param request the {@link VaultHmacRequest}, must not be {@literal null}. + * @return the digest of given data the default hash algorithm and the named key. + * @since 2.0 + */ + Hmac getHmac(String keyName, VaultHmacRequest request); + + /** + * Create a cryptographic signature using {@code keyName} of the given + * {@link Plaintext} and the default hash algorithm. The key must be of a type that + * supports signing. * * @param keyName must not be empty or {@literal null}. * @param plaintext must not be empty or {@literal null}. - * @return the digest of given data using sha2-256 hash algorithm and the named key. - */ - Hmac generateHmac(String keyName, Plaintext plaintext); - - /** - * Generate HMAC digest of given data. - * - * @param keyName must not be empty or {@literal null}. - * @param request {@link VaultHmacRequest} must not be empty or {@literal null}. - * @return the digest of given data using the specified hash algorithm and the named key. - */ - Hmac generateHmac(String keyName, VaultHmacRequest request); - - /** - * Sign a String using a key from the vault using the SHA-256 algorithm. - * - * @param keyName must not be empty or {@literal null}. - * @param plaintext must not be empty or {@literal null}. - * @return Signature of the payload + * @return Signature for {@link Plaintext}. + * @since 2.0 */ Signature sign(String keyName, Plaintext plaintext); /** - * Sign a String using a key from the vault. + * Create a cryptographic signature using {@code keyName} of the given + * {@link VaultSignRequest} and the specified hash algorithm. The key must be of a + * type that supports signing. * * @param keyName must not be empty or {@literal null}. - * @param request {@link VaultSignRequest} - * must not be empty or {@literal null}. - * @return Signature of the payload + * @param request {@link VaultSignRequest} must not be empty or {@literal null}. + * @return Signature for {@link VaultSignRequest}. + * @since 2.0 */ Signature sign(String keyName, VaultSignRequest request); /** - * Verify the validity of a signature in the vault. + * Verify the cryptographic signature using {@code keyName} of the given + * {@link Plaintext} and {@link Signature}. * * @param keyName must not be empty or {@literal null}. - * @param plaintext must not be empty or {@literal null}. - * @param signature Signature to be verified - * @return true if the signature is valid, false otherwise + * @param plaintext must not be {@literal null}. + * @param signature Signature to be verified, must not be {@literal null}. + * @return {@literal true} if the signature is valid, {@literal false} otherwise. + * @since 2.0 */ boolean verify(String keyName, Plaintext plaintext, Signature signature); /** - * Verify the validity of a signature in the vault. + * Verify the cryptographic signature using {@code keyName} of the given + * {@link VaultSignRequest}. * * @param keyName must not be empty or {@literal null}. - * @param request {@link VaultSignatureVerificationRequest} - * must not be empty or {@literal null}. - * @return true if the signature is valid, false otherwise + * @param request {@link VaultSignatureVerificationRequest} must not be + * {@literal null}. + * @return the resulting {@link SignatureValidation}. + * @since 2.0 */ - boolean verify(String keyName, VaultSignatureVerificationRequest request); + SignatureValidation verify(String keyName, VaultSignatureVerificationRequest request); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java index c3fb36f2..e403ffb5 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java @@ -35,6 +35,7 @@ import org.springframework.vault.support.Hmac; import org.springframework.vault.support.Plaintext; import org.springframework.vault.support.RawTransitKey; import org.springframework.vault.support.Signature; +import org.springframework.vault.support.SignatureValidation; import org.springframework.vault.support.TransitKeyType; import org.springframework.vault.support.VaultDecryptionResult; import org.springframework.vault.support.VaultEncryptionResult; @@ -62,8 +63,6 @@ public class VaultTransitTemplate implements VaultTransitOperations { private final String path; - private static final String DEFAULT_SIGN_ALGORITHM = "sha2-256"; - public VaultTransitTemplate(VaultOperations vaultOperations, String path) { Assert.notNull(vaultOperations, "VaultOperations must not be null"); @@ -349,33 +348,48 @@ public class VaultTransitTemplate implements VaultTransitOperations { } @Override - public Hmac generateHmac(String keyName, Plaintext plaintext) { + public Hmac getHmac(String keyName, Plaintext plaintext) { + Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(plaintext, "Plaintext must not be null"); - VaultHmacRequest request = VaultHmacRequest.ofInput(plaintext); + VaultHmacRequest request = VaultHmacRequest.create(plaintext); - return generateHmac(keyName, request); + return getHmac(keyName, request); } @Override - public Hmac generateHmac(String keyName, VaultHmacRequest hmacRequest) { + public Hmac getHmac(String keyName, VaultHmacRequest hmacRequest) { Assert.hasText(keyName, "KeyName must not be empty"); - Assert.notNull(hmacRequest, "Request must not be null"); + Assert.notNull(hmacRequest, "HMAC request must not be null"); - String hmac = (String) vaultOperations. - write(String.format("%s/hmac/%s", path, keyName), hmacRequest).getData() - .get("hmac"); - return toHmac(hmac, hmacRequest.getContext()); + Map request = new LinkedHashMap<>(); + request.put("input", + Base64Utils.encodeToString(hmacRequest.getPlaintext().getPlaintext())); + + if (StringUtils.hasText(hmacRequest.getAlgorithm())) { + request.put("algorithm", hmacRequest.getAlgorithm()); + } + + if (hmacRequest.getKeyVersion() != null) { + request.put("key_version ", hmacRequest.getKeyVersion()); + } + + String hmac = (String) vaultOperations + .write(String.format("%s/hmac/%s", path, keyName), request) + .getRequiredData().get("hmac"); + + return Hmac.of(hmac); } @Override public Signature sign(String keyName, Plaintext plaintext) { + Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(plaintext, "Plaintext must not be null"); - VaultSignRequest request = VaultSignRequest.ofInput(plaintext); + VaultSignRequest request = VaultSignRequest.create(plaintext); return sign(keyName, request); } @@ -384,36 +398,67 @@ public class VaultTransitTemplate implements VaultTransitOperations { public Signature sign(String keyName, VaultSignRequest signRequest) { Assert.hasText(keyName, "KeyName must not be empty"); - Assert.notNull(signRequest, "Plain text must not be null"); + Assert.notNull(signRequest, "Sign request must not be null"); - String signature = (String) vaultOperations. - write(String.format("%s/sign/%s", path, keyName), signRequest).getData() - .get("signature"); - return toSignature(signature, signRequest.getContext()); + Map request = new LinkedHashMap<>(); + request.put("input", + Base64Utils.encodeToString(signRequest.getPlaintext().getPlaintext())); + + if (StringUtils.hasText(signRequest.getAlgorithm())) { + request.put("algorithm", signRequest.getAlgorithm()); + } + + String signature = (String) vaultOperations + .write(String.format("%s/sign/%s", path, keyName), request) + .getRequiredData().get("signature"); + + return Signature.of(signature); } @Override public boolean verify(String keyName, Plaintext plainText, Signature signature) { + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(plainText, "Plaintext must not be null"); Assert.notNull(signature, "Signature must not be null"); - Assert.notNull(plainText, "Input must not be null"); - VaultSignatureVerificationRequest request = - VaultSignatureVerificationRequest.builder() - .input(plainText) - .signature(signature) - .build(); - return verify(keyName, request); + VaultSignatureVerificationRequest request = VaultSignatureVerificationRequest + .create(plainText, signature); + return verify(keyName, request).isValid(); } @Override - public boolean verify(String keyName, VaultSignatureVerificationRequest request) { + public SignatureValidation verify(String keyName, + VaultSignatureVerificationRequest verificationRequest) { - Assert.notNull(request, "Request must not be null"); + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(verificationRequest, + "Signature verification request must not be null"); - return (boolean) vaultOperations. - write(String.format("%s/verify/%s", path, keyName), request).getData() - .get("valid"); + Map request = new LinkedHashMap<>(); + request.put("input", Base64Utils.encodeToString(verificationRequest + .getPlaintext().getPlaintext())); + + if (verificationRequest.getHmac() != null) { + request.put("hmac", verificationRequest.getHmac().getHmac()); + } + + if (verificationRequest.getSignature() != null) { + request.put("signature", verificationRequest.getSignature().getSignature()); + } + + if (StringUtils.hasText(verificationRequest.getAlgorithm())) { + request.put("algorithm", verificationRequest.getAlgorithm()); + } + + Map response = vaultOperations.write( + String.format("%s/verify/%s", path, keyName), request).getRequiredData(); + + if (response.containsKey("valid") && Boolean.valueOf("" + response.get("valid"))) { + return SignatureValidation.valid(); + } + + return SignatureValidation.invalid(); } private static void applyTransitOptions(VaultTransitContext context, @@ -507,16 +552,6 @@ public class VaultTransitTemplate implements VaultTransitOperations { .of(plaintext); } - private static Hmac toHmac(String plaintext, VaultTransitContext context) { - return context != null ? Hmac.of(plaintext).with(context) : Hmac - .of(plaintext); - } - - private static Signature toSignature(String plaintext, VaultTransitContext context) { - return context != null ? Signature.of(plaintext).with(context) : Signature - .of(plaintext); - } - @SuppressWarnings("unchecked") private static List> getBatchData(VaultResponse vaultResponse) { return (List>) vaultResponse.getRequiredData().get( diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/Hmac.java b/spring-vault-core/src/main/java/org/springframework/vault/support/Hmac.java index 24d1a529..bc76a023 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/Hmac.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/Hmac.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * 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. @@ -16,71 +16,41 @@ package org.springframework.vault.support; import lombok.EqualsAndHashCode; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; +import lombok.ToString; -import java.util.Arrays; +import org.springframework.util.Assert; /** - * Value object representing Hmac digest with an optional {@link VaultTransitContext}. + * Value object representing Hmac digest. * * @author Luander Ribeiro + * @author Mark Paluch + * @since 2.0 */ @EqualsAndHashCode +@ToString public class Hmac { - private final String hmac; + private final String hmac; - private final VaultTransitContext context; + private Hmac(String hmac) { + this.hmac = hmac; + } - private Hmac(String hmac, VaultTransitContext context) { - this.hmac = hmac; - this.context = context; - } + /** + * Factory method to create a {@link Hmac} from the given {@code hmac}. + * + * @param hmac the Hmac digest, must not be {@literal null} or empty. + * @return the {@link Hmac} encapsulating {@code hmac}. + */ + public static Hmac of(String hmac) { - /** - * Factory method to create {@link Hmac} from a byte sequence. - * - * @param hmac the Hmac digest, must not be {@literal null} or empty. - * @return the {@link Hmac} for {@code plaintext}. - */ - public static Hmac of(byte[] hmac) { + Assert.hasText(hmac, "Hmac digest must not be null or empty"); - Assert.isTrue(!ObjectUtils.isEmpty(hmac), - "Hmac must not be null or empty"); + return new Hmac(hmac); + } - return new Hmac(Arrays.toString(hmac), VaultTransitContext.empty()); - } - - /** - * Factory method to create {@link Hmac} from the given {@code hmac}. - * - * @param hmac the Hmac digest, must not be {@literal null} or empty. - * @return the {@link Hmac} for {@code hmac}. - */ - public static Hmac of(String hmac) { - - Assert.hasText(hmac, "Hmac digest must not be null or empty"); - - return new Hmac(hmac, VaultTransitContext.empty()); - } - - public String getHmac() { - return hmac; - } - - public VaultTransitContext getContext() { - return context; - } - - /** - * Create a new {@link Hmac} object from this Hmac digest associated with the - * given {@link VaultTransitContext}. - * - * @param context transit context. - * @return the new {@link Hmac} object. - */ - public Hmac with(VaultTransitContext context) { - return new Hmac(getHmac(), context); - } + public String getHmac() { + return hmac; + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/Signature.java b/spring-vault-core/src/main/java/org/springframework/vault/support/Signature.java index 0d0be0fa..24c83779 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/Signature.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/Signature.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * 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. @@ -16,72 +16,41 @@ package org.springframework.vault.support; import lombok.EqualsAndHashCode; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; +import lombok.ToString; -import java.util.Arrays; +import org.springframework.util.Assert; /** - * Value object representing Signature with an optional {@link VaultTransitContext}. + * Value object representing a Signature. * * @author Luander Ribeiro + * @author Mark Paluch + * @since 2.0 */ @EqualsAndHashCode +@ToString public class Signature { - private final String signature; + private final String signature; - private final VaultTransitContext context; + private Signature(String signature) { + this.signature = signature; + } - private Signature(String signature, VaultTransitContext context) { - this.signature = signature; - this.context = context; - } + /** + * Factory method to create a {@link Signature} from the given {@code signature}. + * + * @param signature the signature, must not be {@literal null} or empty. + * @return the {@link Signature} encapsulating {@code signature}. + */ + public static Signature of(String signature) { - /** - * Factory method to create {@link Signature} from the given {@code signature}. - * - * @param signature the signature, must not be {@literal null} or empty. - * @return the {@link Signature} for {@code signature}. - */ - public static Signature of(byte[] signature) { + Assert.hasText(signature, "Signature must not be null or empty"); - Assert.isTrue(!ObjectUtils.isEmpty(signature), - "Signature must not be null or empty"); + return new Signature(signature); + } - return new Signature(Arrays.toString(signature), VaultTransitContext.empty()); - } - - /** - * Factory method to create {@link Signature} from the given {@code signature}. - * - * @param signature the signature, must not be {@literal null} or empty. - * @return the {@link Signature} for {@code signature}. - */ - public static Signature of(String signature) { - - Assert.hasText(signature, "Signature must not be null or empty"); - - return new Signature(signature, VaultTransitContext.empty()); - } - - public String getSignature() { - return signature; - } - - public VaultTransitContext getContext() { - return context; - } - - /** - * Create a new {@link Signature} object from this signature associated with the - * given {@link VaultTransitContext}. - * - * @param context transit context. - * @return the new {@link Signature} object. - */ - public Signature with(VaultTransitContext context) { - return new Signature(getSignature(), context); - } + public String getSignature() { + return signature; + } } - diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/SignatureValidation.java b/spring-vault-core/src/main/java/org/springframework/vault/support/SignatureValidation.java new file mode 100644 index 00000000..b1bfb472 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/SignatureValidation.java @@ -0,0 +1,66 @@ +/* + * 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 lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * Value object representing the result of a {@link Signature} validation. + * + * @author Mark Paluch + * @since 2.0 + */ +@EqualsAndHashCode +@ToString +public class SignatureValidation { + + private static final SignatureValidation VALID = new SignatureValidation(true); + + private static final SignatureValidation INVALID = new SignatureValidation(false); + + private final boolean state; + + private SignatureValidation(boolean state) { + this.state = state; + } + + /** + * Factory method to create a {@link SignatureValidation} object representing a + * successfully validated signature. + * + * @return a {@link SignatureValidation} object representing a successfully validated + * signature. + */ + public static SignatureValidation valid() { + return VALID; + } + + /** + * Factory method to create a {@link SignatureValidation} object representing a failed + * signature validation. + * + * @return a {@link SignatureValidation} object representing a failed signature + * validation. + */ + public static SignatureValidation invalid() { + return INVALID; + } + + public boolean isValid() { + return state; + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHmacRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHmacRequest.java index 331fa8d3..3c5f664d 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHmacRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultHmacRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * 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. @@ -15,151 +15,137 @@ */ package org.springframework.vault.support; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; /** * Request for a HMAC Digest. * * @author Luander Ribeiro + * @author Mark Paluch + * @since 2.0 */ public class VaultHmacRequest { - @JsonProperty("key_version") - private final int keyVersion; + private final Plaintext plaintext; - private final String algorithm; + private final @Nullable String algorithm; - private final String input; + private final @Nullable Integer keyVersion; - @JsonIgnore - private final VaultTransitContext context; + private VaultHmacRequest(Plaintext plaintext, @Nullable String algorithm, + @Nullable Integer keyVersion) { - private VaultHmacRequest(int keyVersion, String algorithm, - String input, VaultTransitContext context) { - this.algorithm = algorithm; - this.input = Base64Utils.encodeToString(input.getBytes()); - this.keyVersion = keyVersion; - this.context = context; - } + this.algorithm = algorithm; + this.plaintext = plaintext; + this.keyVersion = keyVersion; + } - /** - * @return New instance of {@link VaultHmacRequest.VaultHmacRequestBuilder} - */ - public static VaultHmacRequestBuilder builder() { - return new VaultHmacRequestBuilder(); - } + /** + * @return a new instance of {@link VaultHmacRequestBuilder}. + */ + public static VaultHmacRequestBuilder builder() { + return new VaultHmacRequestBuilder(); + } - /** - * Create a new {@link VaultHmacRequest} specifically for an {@code input}. - * Uses {@code sha2-256} algorithm. - * - * @return a new {@link VaultHmacRequest} for the given {@code input}. - */ - public static VaultHmacRequest ofInput(Plaintext input) { - return builder().input(input).build(); - } + /** + * Create a new {@link VaultHmacRequest} given {@link Plaintext}. Uses the default + * signature algorithm. + * + * @return a new {@link VaultHmacRequest} for the given {@link Plaintext input}. + */ + public static VaultHmacRequest create(Plaintext input) { + return builder().plaintext(input).build(); + } - /** - * @return Algorithm used for creating the digest. - */ - public String getAlgorithm() { - return algorithm; - } + /** + * @return plain text input used as basis to generate the digest. + */ + public Plaintext getPlaintext() { + return plaintext; + } - /** - * @return plain text input used as basis to generate the digest. - */ - public String getInput() { - return input; - } + /** + * @return algorithm used for creating the digest or {@literal null} to use the + * default algorithm. + */ + @Nullable + public String getAlgorithm() { + return algorithm; + } - /** - * @return Version of the key used. If not set the latest version is used. - */ - public int getKeyVersion() { - return keyVersion; - } + /** + * @return version of the key used or {@literal null} to use the the latest version. + */ + @Nullable + public Integer getKeyVersion() { + return keyVersion; + } - public VaultTransitContext getContext() { - return context; - } + /** + * Builder to build a {@link VaultHmacRequest}. + */ + public static class VaultHmacRequestBuilder { - public static class VaultHmacRequestBuilder { + private @Nullable Plaintext plaintext; - private int keyVersion; + private @Nullable String algorithm; - private String algorithm = "sha2-256"; + private @Nullable Integer keyVersion; - private Plaintext input; + /** + * Configure the input to be used to create the digest. + * + * @param input base input to create the digest, must not be {@literal null}. + * @return {@code this} {@link VaultHmacRequestBuilder}. + */ + public VaultHmacRequestBuilder plaintext(Plaintext input) { - private VaultTransitContext context; + Assert.notNull(input, "Plaintext must not be null"); - /** - * Configure the algorithm to be used for the operation. - * - * @param algorithm Specify the algorithm to be used for the operation. If not set, - * sha2-256 is used. - * Supported algorithms are: - * sha2-224, sha2-256, sha2-384, sha2-512 - * @return {@code this} - */ - public VaultHmacRequestBuilder algorithm(String algorithm) { - this.algorithm = algorithm; - return this; - } + this.plaintext = input; + return this; + } - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this}. - */ - public VaultHmacRequestBuilder input(Plaintext input) { - this.input = input; - this.context = input.getContext(); - return this; - } + /** + * Configure the algorithm to be used for the operation. + * + * @param algorithm Specify the algorithm to be used for the operation. Supported + * algorithms are: {@literal sha2-224}, {@literal sha2-256}, {@literal sha2-384}, + * {@literal sha2-512}. Defaults to {@literal sha2-256} if not set. + * @return {@code this} {@link VaultHmacRequestBuilder}. + */ + public VaultHmacRequestBuilder algorithm(String algorithm) { - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this} - */ - public VaultHmacRequestBuilder input(String input) { - this.input = Plaintext.of(input); - this.context = VaultTransitContext.empty(); - return this; - } + Assert.hasText(algorithm, "Algorithm must not be null or empty"); - /** - * Configure the version to be used for the operation. - * - * @param version key version to be used. If not set, uses the latest version. - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultHmacRequestBuilder keyVersion(int version) { - this.keyVersion = version; - return this; - } + this.algorithm = algorithm; + return this; + } - /** - * Build a new {@link VaultHmacRequest} instance. Requires - * {@link #input(String)} or {@link #input(Plaintext)} to be configured. - * - * @return a new {@link VaultHmacRequest}. - */ - public VaultHmacRequest build() { + /** + * Configure the key version to be used for the operation. + * + * @param version key version to be used. If not set, uses the latest version. + * @return {@code this} {@link VaultHmacRequestBuilder}. + */ + public VaultHmacRequestBuilder keyVersion(int version) { - Assert.notNull(input, "Input must not be empty"); + this.keyVersion = version; + return this; + } - return new VaultHmacRequest(keyVersion, algorithm, input.asString(), context); - } + /** + * Build a new {@link VaultHmacRequest} instance. Requires + * {@link #plaintext(Plaintext)} to be configured. + * + * @return a new {@link VaultHmacRequest}. + */ + public VaultHmacRequest build() { + Assert.notNull(plaintext, "Plaintext input must not be null"); - - } + return new VaultHmacRequest(plaintext, algorithm, keyVersion); + } + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java index 100db915..55b53301 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * 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. @@ -15,121 +15,111 @@ */ package org.springframework.vault.support; -import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; /** * Request for a signature creation request. * * @author Luander Ribeiro + * @author Mark Paluch + * @since 2.0 */ public class VaultSignRequest { - private final String algorithm; + private final Plaintext plaintext; - private final String input; + private final @Nullable String algorithm; - @JsonIgnore - private final VaultTransitContext context; + private VaultSignRequest(Plaintext plaintext, @Nullable String algorithm) { - private VaultSignRequest(String algorithm, String input, VaultTransitContext context) { - this.algorithm = algorithm; - this.input = Base64Utils.encodeToString(input.getBytes()); - this.context = context; - } + this.plaintext = plaintext; + this.algorithm = algorithm; + } - /** - * @return New instance of {@link VaultSignRequest.VaultSignRequestBuilder} - */ - public static VaultSignRequestBuilder builder() { - return new VaultSignRequestBuilder(); - } + /** + * @return a new instance of {@link VaultSignRequestBuilder}. + */ + public static VaultSignRequestBuilder builder() { + return new VaultSignRequestBuilder(); + } - /** - * Create a new {@link VaultSignRequest} specifically for an {@code input}. - * Uses {@code sha2-256} algorithm. - * - * @return a new {@link VaultSignRequest} for the given {@code input}. - */ - public static VaultSignRequest ofInput(Plaintext input) { - return builder().input(input).build(); - } + /** + * Create a new {@link VaultSignRequest} given {@link Plaintext}. Uses the default + * algorithm. + * + * @return a new {@link VaultSignRequest} for the given {@link Plaintext input}. + */ + public static VaultSignRequest create(Plaintext input) { + return builder().plaintext(input).build(); + } - /** - * @return Algorithm used for creating the digest. - */ - public String getAlgorithm() { - return algorithm; - } + /** + * @return plain text input used as basis to generate the signature. + */ + public Plaintext getPlaintext() { + return plaintext; + } - /** - * @return plain text input used as basis to generate the digest. - */ - public String getInput() { - return input; - } + /** + * @return algorithm used for creating the signature or {@literal null} to use the + * default algorithm. + */ + @Nullable + public String getAlgorithm() { + return algorithm; + } - public VaultTransitContext getContext() { - return context; - } + /** + * Builder to build a {@link VaultSignRequest}. + */ + public static class VaultSignRequestBuilder { - public static class VaultSignRequestBuilder { + private @Nullable Plaintext plaintext; - private String algorithm = "sha2-256"; + private @Nullable String algorithm; - private Plaintext input; + /** + * Configure the input to be used to create the signature. + * + * @param input base input to create the signature, must not be {@literal null}. + * @return {@code this} {@link VaultSignRequestBuilder}. + */ + public VaultSignRequestBuilder plaintext(Plaintext input) { - private VaultTransitContext context; + Assert.notNull(input, "Plaintext must not be null"); - /** - * Configure the algorithm to be used for the operation. - * - * @param algorithm Specify the algorithm to be used for the operation. If not set, - * sha2-256 is used. - * Supported algorithms are: - * sha2-224, sha2-256, sha2-384, sha2-512 - * @return {@code this} - */ - public VaultSignRequestBuilder algorithm(String algorithm) { - this.algorithm = algorithm; - this.context = VaultTransitContext.empty(); - return this; - } + this.plaintext = input; + return this; + } - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this}. - */ - public VaultSignRequestBuilder input(Plaintext input) { - this.input = input; - this.context = input.getContext(); - return this; - } + /** + * Configure the algorithm to be used for the operation. + * + * @param algorithm Specify the algorithm to be used for the operation. Supported + * algorithms are: {@literal sha2-224}, {@literal sha2-256}, {@literal sha2-384}, + * {@literal sha2-512}. Defaults to {@literal sha2-256} if not set. + * @return {@code this} {@link VaultSignRequestBuilder}. + */ + public VaultSignRequestBuilder algorithm(String algorithm) { - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this} - */ - public VaultSignRequestBuilder input(String input) { - this.input = Plaintext.of(input); - return this; - } + Assert.hasText(algorithm, "Algorithm must not be null or empty"); - /** - * Build a new {@link VaultHmacRequest} instance. Requires - * {@link #input(String)} or {@link #input(Plaintext)} to be configured. - * - * @return a new {@link VaultHmacRequest}. - */ - public VaultSignRequest build() { - Assert.notNull(input, "Input must not be empty"); + this.algorithm = algorithm; + return this; + } - return new VaultSignRequest(algorithm, input.asString(), context); - } - } + /** + * Build a new {@link VaultSignRequest} instance. Requires + * {@link #plaintext(Plaintext)} to be configured. + * + * @return a new {@link VaultSignRequest}. + */ + public VaultSignRequest build() { + + Assert.notNull(plaintext, "Plaintext input must not be null"); + + return new VaultSignRequest(plaintext, algorithm); + } + } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java index d7a32d9b..3487a612 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java @@ -15,164 +15,195 @@ */ package org.springframework.vault.support; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; /** * Request for a signature verification. * * @author Luander Ribeiro + * @author Mark Paluch + * @since 2.0 */ public class VaultSignatureVerificationRequest { - private final String algorithm; + private final Plaintext plaintext; - private final String input; + private final @Nullable Signature signature; - private final String signature; + private final @Nullable Hmac hmac; - private final String hmac; + private final @Nullable String algorithm; - private VaultSignatureVerificationRequest(String algorithm, String input, - String signature, String hmac) { - this.algorithm = algorithm; - this.input = Base64Utils.encodeToString(input.getBytes()); - this.signature = String.valueOf(signature); - this.hmac = hmac; - } + private VaultSignatureVerificationRequest(Plaintext plaintext, + @Nullable Signature signature, @Nullable Hmac hmac, @Nullable String algorithm) { - /** - * @return New instance of - * {@link VaultSignatureVerificationRequest.VaultSignatureVerificationRequestBuilder} - */ - public static VaultSignatureVerificationRequestBuilder builder() { - return new VaultSignatureVerificationRequestBuilder(); - } + this.plaintext = plaintext; + this.signature = signature; + this.hmac = hmac; + this.algorithm = algorithm; + } - /** - * Create a new {@link VaultHmacRequest} specifically for a {@code algorithm}. - * - * @param algorithm Specify the algorithm to be used for the operation. If not set, - * sha2-256 is used. - * Supported algorithms are: - * sha2-224, sha2-256, sha2-384, sha2-512 - * @return a new {@link VaultHmacRequest} for the given {@code algorithm}. - */ - public VaultSignatureVerificationRequest ofAlgorithm(String algorithm) { - return builder().algorithm(algorithm).build(); - } + /** + * @return a new instance of {@link VaultSignatureVerificationRequestBuilder}. + */ + public static VaultSignatureVerificationRequestBuilder builder() { + return new VaultSignatureVerificationRequestBuilder(); + } - /** - * @return Algorithm used for creating the digest. - */ - public String getAlgorithm() { - return algorithm; - } + /** + * Create a new {@link VaultSignatureVerificationRequest} given {@link Plaintext} and + * {@link Signature}. + * + * @param plaintext the plaintext, must not be {@literal null}. + * @param signature the signature, must not be {@literal null}. + * @return a new {@link VaultSignatureVerificationRequest} for {@link Plaintext} and + * {@link Signature}. + */ + public static VaultSignatureVerificationRequest create(Plaintext plaintext, + Signature signature) { + return builder().plaintext(plaintext).signature(signature).build(); + } - /** - * @return plain text input used as basis to generate the digest. - */ - public String getInput() { - return input; - } + /** + * Create a new {@link VaultSignatureVerificationRequest} given {@link Plaintext} and + * {@link Hmac}. + * + * @param plaintext the plaintext, must not be {@literal null}. + * @param hmac the hmac, must not be {@literal null}. + * @return a new {@link VaultSignatureVerificationRequest} for {@link Plaintext} and + * {@link Hmac}. + */ + public static VaultSignatureVerificationRequest create(Plaintext plaintext, Hmac hmac) { + return builder().plaintext(plaintext).hmac(hmac).build(); + } - /** - * @return Signature resulting of a sign operation. - */ - public String getSignature() { - return signature; - } + /** + * @return plain text input used as basis to verify the signature. + */ + public Plaintext getPlaintext() { + return plaintext; + } - /** - * @return Digest resulting of a Hmac operation. - */ - public String getHmac() { - return hmac; - } + /** + * @return signature resulting of a sign operation, can be {@literal null} if HMAC is + * used. + */ + @Nullable + public Signature getSignature() { + return signature; + } - public static class VaultSignatureVerificationRequestBuilder { + /** + * @return digest resulting of a Hmac operation, can be {@literal null} if Signature + * is used. + */ + @Nullable + public Hmac getHmac() { + return hmac; + } - private String algorithm = "sha2-256"; + /** + * @return algorithm used for verifying the signature or {@literal null} to use the + * default algorithm. + */ + @Nullable + public String getAlgorithm() { + return algorithm; + } - private Plaintext input; + /** + * Builder to build a {@link VaultSignatureVerificationRequest}. + */ + public static class VaultSignatureVerificationRequestBuilder { - private Signature signature; + private @Nullable Plaintext input; - private String hmac; + private @Nullable Signature signature; - /** - * Configure the algorithm to be used for the operation. - * - * @param algorithm Specify the algorithm to be used for the operation. If not set, - * sha2-256 is used. - * Supported algorithms are: - * sha2-224, sha2-256, sha2-384, sha2-512 - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultSignatureVerificationRequestBuilder algorithm(String algorithm) { - this.algorithm = algorithm; - return this; - } + private @Nullable Hmac hmac; - /** - * Configure the signature to be verified. - * - * @param signature to be verified. - * Either signature or hmac must not be empty of {@literal null} - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultSignatureVerificationRequestBuilder signature(Signature signature) { - this.signature = signature; - return this; - } + private @Nullable String algorithm; - /** - * Configure the hmac to be verified. - * - * @param hmac to be verified. - * Either signature or hmac must not be empty of {@literal null} - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultSignatureVerificationRequestBuilder hmac(String hmac) { - this.hmac = hmac; - return this; - } + /** + * Configure the {@link Plaintext} input to be used to verify the signature. + * + * @param input base input, must not be {@literal null}. + * @return {@code this} {@link VaultSignatureVerificationRequestBuilder}. + */ + public VaultSignatureVerificationRequestBuilder plaintext(Plaintext input) { - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultSignatureVerificationRequestBuilder input(Plaintext input) { - this.input = input; - return this; - } + Assert.notNull(input, "Plaintext must not be null"); - /** - * Configure the input to be used to create the digest. - * - * @param input base input to create the digest, must not be empty or {@literal null}. - * @return {@code this} {@link VaultHmacRequest.VaultHmacRequestBuilder}. - */ - public VaultSignatureVerificationRequestBuilder input(String input) { - this.input = Plaintext.of(input); - return this; - } + this.input = input; + return this; + } - /** - * Build a new {@link VaultHmacRequest} instance. Requires - * {@link #input(String)} or {@link #input(Plaintext)} to be configured. - * - * @return a new {@link VaultHmacRequest}. - */ - public VaultSignatureVerificationRequest build() { + /** + * Configure the {@link Signature} to be verified. Signature verification requires + * either a {@link Signature} or a {@link #hmac(Hmac)} to be configured. Clears + * any previously configured {@link HMAC}. + * + * @param signature to be verified, must not be {@literal null}. + * @return {@code this} {@link VaultSignatureVerificationRequestBuilder}. + */ + public VaultSignatureVerificationRequestBuilder signature(Signature signature) { - Assert.hasText(input.asString(), "Input must not be empty"); + Assert.notNull(signature, "Signature must not be null"); - return new VaultSignatureVerificationRequest(algorithm, - input.asString(), signature.getSignature(), hmac); - } + this.hmac = null; + this.signature = signature; + return this; + } - } + /** + * Configure the {@link Hmac} to be verified. Signature verification requires + * either a {@link Hmac} or a {@link #signature(Signature)} to be configured. + * Clears any previously configured {@link Signature}. + * + * @param hmac to be verified, must not be {@literal null}. + * @return {@code this} {@link VaultSignatureVerificationRequestBuilder}. + */ + public VaultSignatureVerificationRequestBuilder hmac(Hmac hmac) { + + Assert.notNull(hmac, "HMAC must not be null"); + + this.signature = null; + this.hmac = hmac; + return this; + } + + /** + * Configure the algorithm to be used for the operation. + * + * @param algorithm Specify the algorithm to be used for the operation. Supported + * algorithms are: {@literal sha2-224}, {@literal sha2-256}, {@literal sha2-384}, + * {@literal sha2-512}. Defaults to {@literal sha2-256} if not set. + * @return {@code this} {@link VaultSignatureVerificationRequestBuilder}. + */ + public VaultSignatureVerificationRequestBuilder algorithm(String algorithm) { + + Assert.hasText(algorithm, "Algorithm must not be null or empty"); + + this.algorithm = algorithm; + return this; + } + + /** + * Build a new {@link VaultSignatureVerificationRequest} instance. Requires + * {@link #plaintext(Plaintext)} and one of {@link #hmac(Hmac)}, + * {@link #signature(Signature)} to be configured. + * + * @return a new {@link VaultHmacRequest}. + */ + public VaultSignatureVerificationRequest build() { + + Assert.notNull(input, "Plaintext input must not be null"); + Assert.isTrue(hmac != null || signature != null, + "Either Signature or Hmac must not be null"); + + return new VaultSignatureVerificationRequest(input, signature, hmac, + algorithm); + } + } } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java index e52da3b3..63fd0a0a 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java @@ -33,6 +33,7 @@ import org.springframework.vault.support.Hmac; import org.springframework.vault.support.Plaintext; import org.springframework.vault.support.RawTransitKey; import org.springframework.vault.support.Signature; +import org.springframework.vault.support.SignatureValidation; import org.springframework.vault.support.TransitKeyType; import org.springframework.vault.support.VaultDecryptionResult; import org.springframework.vault.support.VaultEncryptionResult; @@ -57,6 +58,7 @@ import static org.junit.Assume.assumeTrue; * * @author Mark Paluch * @author Praveendra Singh + * @author Luander Ribeiro */ @RunWith(SpringRunner.class) @ContextConfiguration(classes = VaultIntegrationTestConfiguration.class) @@ -64,6 +66,11 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport private static final Version BATCH_INTRODUCED_IN_VERSION = Version.parse("0.6.5"); + private static final Version SIGN_VERIFY_INTRODUCED_IN_VERSION = Version + .parse("0.6.2"); + + private static final Version ED25519_INTRODUCED_IN_VERSION = Version.parse("0.7.3"); + @Autowired private VaultOperations vaultOperations; @@ -151,12 +158,9 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.4"))); - VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest - .ofKeyType("ecdsa-p256"); + String keyName = createEcdsaP256Key(); - transitOperations.createKey("ecdsa-key", request); - - VaultTransitKey mykey = transitOperations.getKey("ecdsa-key"); + VaultTransitKey mykey = transitOperations.getKey(keyName); assertThat(mykey.getType()).startsWith("ecdsa"); assertThat(mykey.getKeys()).isNotEmpty(); @@ -165,7 +169,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void createKeyShouldCreateEdKey() { - assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.7.3"))); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(ED25519_INTRODUCED_IN_VERSION)); VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest .ofKeyType("ed25519"); @@ -525,50 +529,78 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void generateHmacShouldCreateHmac() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - Hmac hmac = transitOperations.generateHmac("ecdsa-key", Plaintext.of("hello-world")); - assertThat(hmac.getHmac()).startsWith("vault:v"); + String keyName = createEcdsaP256Key(); + + Hmac hmac = transitOperations.getHmac(keyName, Plaintext.of("hello-world")); + assertThat(hmac.getHmac()).isNotEmpty(); + } + + @Test + public void generateHmacShouldCreateHmacForRotatedKey() { + + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); + + String keyName = createEcdsaP256Key(); + transitOperations.rotate(keyName); + + VaultHmacRequest request = VaultHmacRequest.builder() + .plaintext(Plaintext.of("hello-world")).keyVersion(2).build(); + + Hmac hmac = transitOperations.getHmac(keyName, request); + assertThat(hmac.getHmac()).isNotEmpty(); } @Test public void generateHmacWithCustomAlgorithmShouldCreateHmac() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); + + String keyName = createEcdsaP256Key(); VaultHmacRequest request = VaultHmacRequest.builder() - .input("hello-world") - .algorithm("sha2-512") - .build(); + .plaintext(Plaintext.of("hello-world")).algorithm("sha2-512").build(); - Hmac hmac = transitOperations.generateHmac("ecdsa-key", request); - assertThat(hmac.getHmac()).startsWith("vault:v"); + Hmac hmac = transitOperations.getHmac(keyName, request); + assertThat(hmac.getHmac()).isNotEmpty(); } @Test(expected = VaultException.class) public void generateHmacWithInvalidAlgorithmShouldFail() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + String keyName = createEcdsaP256Key(); VaultHmacRequest request = VaultHmacRequest.builder() - .input("hello-world") - .algorithm("blah-512") - .build(); + .plaintext(Plaintext.of("hello-world")).algorithm("blah-512").build(); - transitOperations.generateHmac("ecdsa-key", request); + transitOperations.getHmac(keyName, request); } @Test public void signShouldCreateSignature() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world")); - assertThat(signature.getSignature()).startsWith("vault:v"); + String keyName = createEcdsaP256Key(); + + Signature signature = transitOperations + .sign(keyName, Plaintext.of("hello-world")); + assertThat(signature.getSignature()).isNotEmpty(); + } + + @Test + public void signShouldCreateSignatureUsingEd25519() { + + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(ED25519_INTRODUCED_IN_VERSION)); + + VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest + .ofKeyType("ed25519"); + transitOperations.createKey("ed-key", keyCreationRequest); + + Signature signature = transitOperations.sign("ed-key", + Plaintext.of("hello-world")); + assertThat(signature.getSignature()).isNotEmpty(); } @Test(expected = VaultException.class) @@ -580,110 +612,116 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport } @Test - public void signWithCustomAlgorithShouldCreateSignature() { + public void signWithCustomAlgorithmShouldCreateSignature() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - VaultSignRequest request = VaultSignRequest.builder() - .input("hello-world") - .algorithm("sha2-512") - .build(); + String keyName = createEcdsaP256Key(); - Signature signature = transitOperations.sign("ecdsa-key", request); - assertThat(signature.getSignature()).startsWith("vault:v"); + Plaintext plaintext = Plaintext.of("hello-world"); + VaultSignRequest request = VaultSignRequest.builder().plaintext(plaintext) + .algorithm("sha2-512").build(); + + Signature signature = transitOperations.sign(keyName, request); + assertThat(signature.getSignature()).isNotEmpty(); } @Test(expected = VaultException.class) public void signWithInvalidAlgorithmShouldFail() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + String keyName = createEcdsaP256Key(); VaultSignRequest request = VaultSignRequest.builder() - .input("hello-world") - .algorithm("blah-512") - .build(); + .plaintext(Plaintext.of("hello-world")).algorithm("blah-512").build(); - transitOperations.sign("ecdsa-key", request); + transitOperations.sign(keyName, request); } @Test public void shouldVerifyValidSignature() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world")); - assertThat(signature.getSignature()).startsWith("vault:v"); + String keyName = createEcdsaP256Key(); - boolean valid = transitOperations.verify("ecdsa-key", - Plaintext.of("hello-world"), signature); + Plaintext plaintext = Plaintext.of("hello-world"); + Signature signature = transitOperations.sign(keyName, plaintext); + + boolean valid = transitOperations.verify(keyName, plaintext, signature); assertThat(valid).isTrue(); } + @Test + public void shouldVerifyValidHmac() { + + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); + + String keyName = createEcdsaP256Key(); + + Plaintext plaintext = Plaintext.of("hello-world"); + Hmac hmac = transitOperations.getHmac(keyName, plaintext); + + SignatureValidation valid = transitOperations.verify(keyName, + VaultSignatureVerificationRequest.create(plaintext, hmac)); + assertThat(valid).isEqualTo(SignatureValidation.valid()); + } + @Test public void shouldVerifyValidSignatureWithCustomAlgorithm() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - VaultSignRequest request = VaultSignRequest.builder() - .input("hello-world") - .algorithm("sha2-512") - .build(); + String keyName = createEcdsaP256Key(); - Signature signature = transitOperations.sign("ecdsa-key", request); - assertThat(signature.getSignature()).startsWith("vault:v"); + Plaintext plaintext = Plaintext.of("hello-world"); + VaultSignRequest request = VaultSignRequest.builder().plaintext(plaintext) + .algorithm("sha2-512").build(); - VaultSignatureVerificationRequest verificationRequest = - VaultSignatureVerificationRequest.builder() - .algorithm("sha2-512") - .input("hello-world") - .signature(signature) - .build(); + Signature signature = transitOperations.sign(keyName, request); - boolean valid = transitOperations.verify("ecdsa-key", verificationRequest); - assertThat(valid).isTrue(); + VaultSignatureVerificationRequest verificationRequest = VaultSignatureVerificationRequest + .builder().algorithm("sha2-512").plaintext(plaintext) + .signature(signature).build(); + + SignatureValidation valid = transitOperations + .verify(keyName, verificationRequest); + assertThat(valid).isEqualTo(SignatureValidation.valid()); } @Test public void shouldFailToVerifyValidSignatureWithInvalidExistingCustomAlgorithm() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world")); - assertThat(signature.getSignature()).startsWith("vault:v"); + String keyName = createEcdsaP256Key(); - VaultSignatureVerificationRequest verificationRequest = - VaultSignatureVerificationRequest.builder() - .algorithm("sha2-512") - .input("hello-world") - .signature(signature) - .build(); + Plaintext plaintext = Plaintext.of("hello-world"); + Signature signature = transitOperations.sign(keyName, plaintext); - boolean valid = transitOperations.verify("ecdsa-key", verificationRequest); - assertThat(valid).isFalse(); + VaultSignatureVerificationRequest verificationRequest = VaultSignatureVerificationRequest + .builder().algorithm("sha2-512").plaintext(plaintext) + .signature(signature).build(); + + SignatureValidation valid = transitOperations + .verify(keyName, verificationRequest); + assertThat(valid).isEqualTo(SignatureValidation.invalid()); } @Test(expected = VaultException.class) public void shouldFailToVerifyValidSignatureWithInvalidCustomAlgorithm() { - VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256"); - transitOperations.createKey("ecdsa-key", keyCreationRequest); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(SIGN_VERIFY_INTRODUCED_IN_VERSION)); - Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world")); - assertThat(signature.getSignature()).startsWith("vault:v"); + String keyName = createEcdsaP256Key(); - VaultSignatureVerificationRequest verificationRequest = - VaultSignatureVerificationRequest.builder() - .algorithm("blah-512") - .input("hello-world") - .signature(signature) - .build(); + Plaintext plaintext = Plaintext.of("hello-world"); + Signature signature = transitOperations.sign(keyName, plaintext); - transitOperations.verify("ecdsa-key", verificationRequest); + VaultSignatureVerificationRequest verificationRequest = VaultSignatureVerificationRequest + .builder().algorithm("blah-512").plaintext(plaintext) + .signature(signature).build(); + + transitOperations.verify(keyName, verificationRequest); } @Test @@ -785,7 +823,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport @Test public void shouldExportEdKey() { - assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.7.3"))); + assumeTrue(vaultVersion.isGreaterThanOrEqualTo(ED25519_INTRODUCED_IN_VERSION)); VaultTransitOperations transitOperations = vaultOperations.opsForTransit(); @@ -802,4 +840,14 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport assertThat(hmacKey.getKeys()).isNotEmpty(); assertThat(signingKey.getKeys()).isNotEmpty(); } + + private String createEcdsaP256Key() { + + String keyName = "ecdsa-key"; + VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest + .ofKeyType("ecdsa-p256"); + transitOperations.createKey(keyName, keyCreationRequest); + + return keyName; + } }