Extend Transit API to support Hashing and Signing Operations.
Transit API now has support to generate HMAC digests, signatures and verify signatures.
Plaintext plaintext = Plaintext.of("hello-world");
Signature signature = transitOperations.sign(keyName, plaintext);
boolean valid = transitOperations.verify(keyName, plaintext, signature);
Hmac hmac = transitOperations.getHmac(keyName, plaintext);
SignatureValidation validation = transitOperations.verify(keyName,
VaultSignatureVerificationRequest.create(plaintext, hmac));
Original pull request: gh-162.
Closes: gh-148.
This commit is contained in:
committed by
Mark Paluch
parent
438b8db745
commit
10488de17a
@@ -19,11 +19,16 @@ import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.vault.support.Ciphertext;
|
||||
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.TransitKeyType;
|
||||
import org.springframework.vault.support.VaultDecryptionResult;
|
||||
import org.springframework.vault.support.VaultEncryptionResult;
|
||||
import org.springframework.vault.support.VaultHmacRequest;
|
||||
import org.springframework.vault.support.VaultSignRequest;
|
||||
import org.springframework.vault.support.VaultSignatureVerificationRequest;
|
||||
import org.springframework.vault.support.VaultTransitContext;
|
||||
import org.springframework.vault.support.VaultTransitKey;
|
||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||
@@ -222,4 +227,61 @@ public interface VaultTransitOperations {
|
||||
* @see #rotate(String)
|
||||
*/
|
||||
String rewrap(String keyName, String ciphertext, VaultTransitContext transitContext);
|
||||
|
||||
/**
|
||||
* Generate HMAC digest of given data.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
Signature sign(String keyName, Plaintext plaintext);
|
||||
|
||||
/**
|
||||
* Sign a String using a key from the vault.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
Signature sign(String keyName, VaultSignRequest request);
|
||||
|
||||
/**
|
||||
* Verify the validity of a signature in the vault.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
boolean verify(String keyName, Plaintext plaintext, Signature signature);
|
||||
|
||||
/**
|
||||
* Verify the validity of a signature in the vault.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
boolean verify(String keyName, VaultSignatureVerificationRequest request);
|
||||
}
|
||||
|
||||
@@ -31,13 +31,18 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.Ciphertext;
|
||||
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.TransitKeyType;
|
||||
import org.springframework.vault.support.VaultDecryptionResult;
|
||||
import org.springframework.vault.support.VaultEncryptionResult;
|
||||
import org.springframework.vault.support.VaultHmacRequest;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultResponseSupport;
|
||||
import org.springframework.vault.support.VaultSignRequest;
|
||||
import org.springframework.vault.support.VaultSignatureVerificationRequest;
|
||||
import org.springframework.vault.support.VaultTransitContext;
|
||||
import org.springframework.vault.support.VaultTransitKey;
|
||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||
@@ -49,6 +54,7 @@ import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
||||
* @author Mark Paluch
|
||||
* @author Sven Schürmann
|
||||
* @author Praveendra Singh
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
public class VaultTransitTemplate implements VaultTransitOperations {
|
||||
|
||||
@@ -56,6 +62,8 @@ 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");
|
||||
@@ -340,6 +348,74 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
||||
.getRequiredData().get("ciphertext");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hmac generateHmac(String keyName, Plaintext plaintext) {
|
||||
|
||||
Assert.notNull(plaintext, "Plaintext must not be null");
|
||||
|
||||
VaultHmacRequest request = VaultHmacRequest.ofInput(plaintext);
|
||||
|
||||
return generateHmac(keyName, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hmac generateHmac(String keyName, VaultHmacRequest hmacRequest) {
|
||||
|
||||
Assert.hasText(keyName, "KeyName must not be empty");
|
||||
Assert.notNull(hmacRequest, "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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign(String keyName, Plaintext plaintext) {
|
||||
|
||||
Assert.notNull(plaintext, "Plaintext must not be null");
|
||||
|
||||
VaultSignRequest request = VaultSignRequest.ofInput(plaintext);
|
||||
|
||||
return sign(keyName, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign(String keyName, VaultSignRequest signRequest) {
|
||||
|
||||
Assert.hasText(keyName, "KeyName must not be empty");
|
||||
Assert.notNull(signRequest, "Plain text 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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String keyName, Plaintext plainText, Signature signature) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String keyName, VaultSignatureVerificationRequest request) {
|
||||
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
|
||||
return (boolean) vaultOperations.
|
||||
write(String.format("%s/verify/%s", path, keyName), request).getData()
|
||||
.get("valid");
|
||||
}
|
||||
|
||||
private static void applyTransitOptions(VaultTransitContext context,
|
||||
Map<String, String> request) {
|
||||
|
||||
@@ -431,6 +507,16 @@ 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<Map<String, String>> getBatchData(VaultResponse vaultResponse) {
|
||||
return (List<Map<String, String>>) vaultResponse.getRequiredData().get(
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2016-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 org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Value object representing Hmac digest with an optional {@link VaultTransitContext}.
|
||||
*
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
public class Hmac {
|
||||
|
||||
private final String hmac;
|
||||
|
||||
private final VaultTransitContext context;
|
||||
|
||||
private Hmac(String hmac, VaultTransitContext context) {
|
||||
this.hmac = hmac;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.isTrue(!ObjectUtils.isEmpty(hmac),
|
||||
"Hmac must not be null or empty");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016-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 org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Value object representing Signature with an optional {@link VaultTransitContext}.
|
||||
*
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
public class Signature {
|
||||
|
||||
private final String signature;
|
||||
|
||||
private final VaultTransitContext context;
|
||||
|
||||
private Signature(String signature, VaultTransitContext context) {
|
||||
this.signature = signature;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.isTrue(!ObjectUtils.isEmpty(signature),
|
||||
"Signature must not be null or empty");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2016-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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
/**
|
||||
* Request for a HMAC Digest.
|
||||
*
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
public class VaultHmacRequest {
|
||||
|
||||
@JsonProperty("key_version")
|
||||
private final int keyVersion;
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private final String input;
|
||||
|
||||
@JsonIgnore
|
||||
private final VaultTransitContext context;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return New instance of {@link VaultHmacRequest.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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Algorithm used for creating the digest.
|
||||
*/
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plain text input used as basis to generate the digest.
|
||||
*/
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Version of the key used. If not set the latest version is used.
|
||||
*/
|
||||
public int getKeyVersion() {
|
||||
return keyVersion;
|
||||
}
|
||||
|
||||
public VaultTransitContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public static class VaultHmacRequestBuilder {
|
||||
|
||||
private int keyVersion;
|
||||
|
||||
private String algorithm = "sha2-256";
|
||||
|
||||
private Plaintext input;
|
||||
|
||||
private VaultTransitContext context;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
Assert.notNull(input, "Input must not be empty");
|
||||
|
||||
return new VaultHmacRequest(keyVersion, algorithm, input.asString(), context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2016-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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
/**
|
||||
* Request for a signature creation request.
|
||||
*
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
public class VaultSignRequest {
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private final String input;
|
||||
|
||||
@JsonIgnore
|
||||
private final VaultTransitContext context;
|
||||
|
||||
private VaultSignRequest(String algorithm, String input, VaultTransitContext context) {
|
||||
this.algorithm = algorithm;
|
||||
this.input = Base64Utils.encodeToString(input.getBytes());
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return New instance of {@link VaultSignRequest.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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Algorithm used for creating the digest.
|
||||
*/
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plain text input used as basis to generate the digest.
|
||||
*/
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public VaultTransitContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public static class VaultSignRequestBuilder {
|
||||
|
||||
private String algorithm = "sha2-256";
|
||||
|
||||
private Plaintext input;
|
||||
|
||||
private VaultTransitContext context;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
|
||||
return new VaultSignRequest(algorithm, input.asString(), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2016-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 org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
/**
|
||||
* Request for a signature verification.
|
||||
*
|
||||
* @author Luander Ribeiro
|
||||
*/
|
||||
public class VaultSignatureVerificationRequest {
|
||||
|
||||
private final String algorithm;
|
||||
|
||||
private final String input;
|
||||
|
||||
private final String signature;
|
||||
|
||||
private final String hmac;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return New instance of
|
||||
* {@link VaultSignatureVerificationRequest.VaultSignatureVerificationRequestBuilder}
|
||||
*/
|
||||
public static VaultSignatureVerificationRequestBuilder builder() {
|
||||
return new VaultSignatureVerificationRequestBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Algorithm used for creating the digest.
|
||||
*/
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plain text input used as basis to generate the digest.
|
||||
*/
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Signature resulting of a sign operation.
|
||||
*/
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Digest resulting of a Hmac operation.
|
||||
*/
|
||||
public String getHmac() {
|
||||
return hmac;
|
||||
}
|
||||
|
||||
public static class VaultSignatureVerificationRequestBuilder {
|
||||
|
||||
private String algorithm = "sha2-256";
|
||||
|
||||
private Plaintext input;
|
||||
|
||||
private Signature signature;
|
||||
|
||||
private String hmac;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
Assert.hasText(input.asString(), "Input must not be empty");
|
||||
|
||||
return new VaultSignatureVerificationRequest(algorithm,
|
||||
input.asString(), signature.getSignature(), hmac);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,12 +29,17 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.Ciphertext;
|
||||
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.TransitKeyType;
|
||||
import org.springframework.vault.support.VaultDecryptionResult;
|
||||
import org.springframework.vault.support.VaultEncryptionResult;
|
||||
import org.springframework.vault.support.VaultHmacRequest;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
import org.springframework.vault.support.VaultSignRequest;
|
||||
import org.springframework.vault.support.VaultSignatureVerificationRequest;
|
||||
import org.springframework.vault.support.VaultTransitContext;
|
||||
import org.springframework.vault.support.VaultTransitKey;
|
||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||
@@ -517,6 +522,170 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
assertThat(decrypted.get(1).getCause()).isInstanceOf(VaultException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateHmacShouldCreateHmac() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
Hmac hmac = transitOperations.generateHmac("ecdsa-key", Plaintext.of("hello-world"));
|
||||
assertThat(hmac.getHmac()).startsWith("vault:v");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateHmacWithCustomAlgorithmShouldCreateHmac() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
VaultHmacRequest request = VaultHmacRequest.builder()
|
||||
.input("hello-world")
|
||||
.algorithm("sha2-512")
|
||||
.build();
|
||||
|
||||
Hmac hmac = transitOperations.generateHmac("ecdsa-key", request);
|
||||
assertThat(hmac.getHmac()).startsWith("vault:v");
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void generateHmacWithInvalidAlgorithmShouldFail() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
VaultHmacRequest request = VaultHmacRequest.builder()
|
||||
.input("hello-world")
|
||||
.algorithm("blah-512")
|
||||
.build();
|
||||
|
||||
transitOperations.generateHmac("ecdsa-key", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signShouldCreateSignature() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world"));
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void signWithInvalidKeyFormatShouldFail() {
|
||||
|
||||
transitOperations.createKey("mykey");
|
||||
|
||||
transitOperations.sign("mykey", Plaintext.of("hello-world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signWithCustomAlgorithShouldCreateSignature() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
VaultSignRequest request = VaultSignRequest.builder()
|
||||
.input("hello-world")
|
||||
.algorithm("sha2-512")
|
||||
.build();
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", request);
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void signWithInvalidAlgorithmShouldFail() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
VaultSignRequest request = VaultSignRequest.builder()
|
||||
.input("hello-world")
|
||||
.algorithm("blah-512")
|
||||
.build();
|
||||
|
||||
transitOperations.sign("ecdsa-key", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldVerifyValidSignature() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world"));
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
|
||||
boolean valid = transitOperations.verify("ecdsa-key",
|
||||
Plaintext.of("hello-world"), signature);
|
||||
assertThat(valid).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldVerifyValidSignatureWithCustomAlgorithm() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
VaultSignRequest request = VaultSignRequest.builder()
|
||||
.input("hello-world")
|
||||
.algorithm("sha2-512")
|
||||
.build();
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", request);
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
|
||||
VaultSignatureVerificationRequest verificationRequest =
|
||||
VaultSignatureVerificationRequest.builder()
|
||||
.algorithm("sha2-512")
|
||||
.input("hello-world")
|
||||
.signature(signature)
|
||||
.build();
|
||||
|
||||
boolean valid = transitOperations.verify("ecdsa-key", verificationRequest);
|
||||
assertThat(valid).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailToVerifyValidSignatureWithInvalidExistingCustomAlgorithm() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world"));
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
|
||||
VaultSignatureVerificationRequest verificationRequest =
|
||||
VaultSignatureVerificationRequest.builder()
|
||||
.algorithm("sha2-512")
|
||||
.input("hello-world")
|
||||
.signature(signature)
|
||||
.build();
|
||||
|
||||
boolean valid = transitOperations.verify("ecdsa-key", verificationRequest);
|
||||
assertThat(valid).isFalse();
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void shouldFailToVerifyValidSignatureWithInvalidCustomAlgorithm() {
|
||||
|
||||
VaultTransitKeyCreationRequest keyCreationRequest = VaultTransitKeyCreationRequest.ofKeyType("ecdsa-p256");
|
||||
transitOperations.createKey("ecdsa-key", keyCreationRequest);
|
||||
|
||||
Signature signature = transitOperations.sign("ecdsa-key", Plaintext.of("hello-world"));
|
||||
assertThat(signature.getSignature()).startsWith("vault:v");
|
||||
|
||||
VaultSignatureVerificationRequest verificationRequest =
|
||||
VaultSignatureVerificationRequest.builder()
|
||||
.algorithm("blah-512")
|
||||
.input("hello-world")
|
||||
.signature(signature)
|
||||
.build();
|
||||
|
||||
transitOperations.verify("ecdsa-key", verificationRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNewExportableKey() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user