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; + } }