Added transit batch encrypt and decrypt support.
We now support batch encryption and decryption via VaultTransitOperations.
List<VaultEncryptionResult> batchEncryption = transitOperations.encrypt("mykey", Arrays.asList(Plaintext.of("hello"), Plaintext.of("world")));
Ciphertext encryptedHello = batchEncryption.get(0).get();
Ciphertext encryptedWorld = batchEncryption.get(1).get();
List<VaultDecryptionResult> batchDecryption = transitOperations.decrypt("mykey", Arrays.asList(encryptedHello, encryptedWorld));
Original pull request: gh-138.
Related ticket: gh-137.
Closes gh-138.
This commit is contained in:
committed by
Mark Paluch
parent
aa8eb13f66
commit
ed920ec0ab
@@ -246,6 +246,7 @@ The following scripts need to be run prior to building the project for the tests
|
|||||||
|
|
||||||
$ ./src/test/bash/install_vault.sh
|
$ ./src/test/bash/install_vault.sh
|
||||||
$ ./src/test/bash/create_certificates.sh
|
$ ./src/test/bash/create_certificates.sh
|
||||||
|
$ ./src/test/bash/env.sh
|
||||||
$ ./src/test/bash/local_run_vault.sh
|
$ ./src/test/bash/local_run_vault.sh
|
||||||
|
|
||||||
Changes to the documentation should be made to the adocs found under `src/main/asciidoc/`
|
Changes to the documentation should be made to the adocs found under `src/main/asciidoc/`
|
||||||
|
|||||||
@@ -17,18 +17,23 @@ package org.springframework.vault.core;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.vault.support.RawTransitKey;
|
||||||
import org.springframework.vault.support.TransitKeyType;
|
import org.springframework.vault.support.TransitKeyType;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionResult;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionResult;
|
||||||
import org.springframework.vault.support.VaultTransitContext;
|
import org.springframework.vault.support.VaultTransitContext;
|
||||||
import org.springframework.vault.support.VaultTransitKey;
|
import org.springframework.vault.support.VaultTransitKey;
|
||||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||||
import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
||||||
import org.springframework.vault.support.RawTransitKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that specifies operations using the {@code transit} backend.
|
* Interface that specifies operations using the {@code transit} backend.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Sven Schürmann
|
* @author Sven Schürmann
|
||||||
|
* @author Praveendra Singh
|
||||||
* @see <a href="https://www.vaultproject.io/docs/secrets/transit/index.html">Transit
|
* @see <a href="https://www.vaultproject.io/docs/secrets/transit/index.html">Transit
|
||||||
* Secret Backend</a>
|
* Secret Backend</a>
|
||||||
*/
|
*/
|
||||||
@@ -122,6 +127,19 @@ public interface VaultTransitOperations {
|
|||||||
*/
|
*/
|
||||||
String encrypt(String keyName, byte[] plaintext, VaultTransitContext transitRequest);
|
String encrypt(String keyName, byte[] plaintext, VaultTransitContext transitRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the provided list of plaintext using the named key and context.
|
||||||
|
* The encryption is done using transit backend's batch operation.
|
||||||
|
*
|
||||||
|
* works with Vault 0.6.5 and later.
|
||||||
|
*
|
||||||
|
* @param keyName must not be empty or {@literal null}.
|
||||||
|
* @param batchRequest a list of VaultEncryptionPayload which includes plaintext and optional context
|
||||||
|
* @return list of cipher text in the same order as in plaintexts.
|
||||||
|
* throws VaultException in case of not matching context found.
|
||||||
|
*/
|
||||||
|
List<VaultEncryptionResult> encrypt(String keyName, List<VaultEncryptionPayload> batchRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypts the provided plaintext using the named key.
|
* Decrypts the provided plaintext using the named key.
|
||||||
*
|
*
|
||||||
@@ -141,6 +159,19 @@ public interface VaultTransitOperations {
|
|||||||
*/
|
*/
|
||||||
byte[] decrypt(String keyName, String ciphertext, VaultTransitContext transitRequest);
|
byte[] decrypt(String keyName, String ciphertext, VaultTransitContext transitRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the provided list of ciphertext using the named key and context.
|
||||||
|
* The decryption is done using transit backend's batch operation.
|
||||||
|
*
|
||||||
|
* works with Vault 0.6.5 and later.
|
||||||
|
*
|
||||||
|
* @param keyName must not be empty or {@literal null}.
|
||||||
|
* @param batchRequest a list of VaultDecryptionPayload which includes plaintext and optional context
|
||||||
|
* @return list of plain text in the same order as in ciphertexts.
|
||||||
|
* throws VaultException in case of not matching context found.
|
||||||
|
*/
|
||||||
|
List<VaultDecryptionResult> decrypt(String keyName, List<VaultDecryptionPayload> batchRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rewrap the provided ciphertext using the latest version of the named key. Because
|
* Rewrap the provided ciphertext using the latest version of the named key. Because
|
||||||
* this never returns plaintext, it is possible to delegate this functionality to
|
* this never returns plaintext, it is possible to delegate this functionality to
|
||||||
|
|||||||
@@ -15,30 +15,38 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.vault.core;
|
package org.springframework.vault.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.Base64Utils;
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.vault.support.RawTransitKey;
|
||||||
import org.springframework.vault.support.TransitKeyType;
|
import org.springframework.vault.support.TransitKeyType;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionResult;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionDecryptionResultHelper;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionResult;
|
||||||
import org.springframework.vault.support.VaultResponse;
|
import org.springframework.vault.support.VaultResponse;
|
||||||
import org.springframework.vault.support.VaultResponseSupport;
|
import org.springframework.vault.support.VaultResponseSupport;
|
||||||
import org.springframework.vault.support.VaultTransitContext;
|
import org.springframework.vault.support.VaultTransitContext;
|
||||||
import org.springframework.vault.support.VaultTransitKey;
|
import org.springframework.vault.support.VaultTransitKey;
|
||||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||||
import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
||||||
import org.springframework.vault.support.RawTransitKey;
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of {@link VaultTransitOperations}.
|
* Default implementation of {@link VaultTransitOperations}.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
* @author Sven Schürmann
|
* @author Sven Schürmann
|
||||||
|
* @author Praveendra Singh
|
||||||
*/
|
*/
|
||||||
public class VaultTransitTemplate implements VaultTransitOperations {
|
public class VaultTransitTemplate implements VaultTransitOperations {
|
||||||
|
|
||||||
@@ -152,7 +160,7 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
|||||||
.write(String.format("%s/encrypt/%s", path, keyName), request).getData()
|
.write(String.format("%s/encrypt/%s", path, keyName), request).getData()
|
||||||
.get("ciphertext");
|
.get("ciphertext");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encrypt(String keyName, byte[] plaintext,
|
public String encrypt(String keyName, byte[] plaintext,
|
||||||
VaultTransitContext transitRequest) {
|
VaultTransitContext transitRequest) {
|
||||||
@@ -173,6 +181,37 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
|||||||
.get("ciphertext");
|
.get("ciphertext");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<VaultEncryptionResult> encrypt(String keyName, List<VaultEncryptionPayload> batchRequest) {
|
||||||
|
|
||||||
|
Assert.hasText(keyName, "KeyName must not be empty");
|
||||||
|
Assert.notEmpty(batchRequest, "batchRequest must not be null and should have at least one entry");
|
||||||
|
|
||||||
|
List<Map<String, String>> batch = new ArrayList<Map<String, String>>();
|
||||||
|
|
||||||
|
for (VaultEncryptionPayload request : batchRequest) {
|
||||||
|
|
||||||
|
Assert.notNull(request.getPlaintext(), "Plain text must not be null");
|
||||||
|
|
||||||
|
Map<String, String> vaultRequest = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
|
vaultRequest.put("plaintext", Base64Utils.encodeToString(request.getPlaintext()));
|
||||||
|
|
||||||
|
if (request.getContext() != null) {
|
||||||
|
applyTransitOptions(request.getContext(), vaultRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.add(vaultRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<Map<String, String>>> request = new LinkedHashMap<String, List<Map<String, String>>>();
|
||||||
|
request.put("batch_input", batch);
|
||||||
|
|
||||||
|
VaultResponse vaultResponse = vaultOperations.write(String.format("%s/encrypt/%s", path, keyName), request);
|
||||||
|
|
||||||
|
return VaultEncryptionDecryptionResultHelper.fetchEncryptionResult(vaultResponse);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String decrypt(String keyName, String ciphertext) {
|
public String decrypt(String keyName, String ciphertext) {
|
||||||
|
|
||||||
@@ -211,6 +250,37 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
|||||||
|
|
||||||
return Base64Utils.decodeFromString(plaintext);
|
return Base64Utils.decodeFromString(plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<VaultDecryptionResult> decrypt(String keyName, List<VaultDecryptionPayload> batchRequest) {
|
||||||
|
|
||||||
|
Assert.hasText(keyName, "KeyName must not be empty");
|
||||||
|
Assert.notEmpty(batchRequest, "batchRequest must not be null and should have at least one entry");
|
||||||
|
|
||||||
|
List<Map<String, String>> batch = new ArrayList<Map<String, String>>();
|
||||||
|
|
||||||
|
for (VaultDecryptionPayload request : batchRequest) {
|
||||||
|
|
||||||
|
Assert.notNull(request.getCiphertext(), "Cipher text must not be null");
|
||||||
|
|
||||||
|
Map<String, String> vaultRequest = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
|
vaultRequest.put("ciphertext", request.getCiphertext());
|
||||||
|
|
||||||
|
if (request.getContext() != null) {
|
||||||
|
applyTransitOptions(request.getContext(), vaultRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.add(vaultRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<Map<String, String>>> request = new LinkedHashMap<String, List<Map<String, String>>>();
|
||||||
|
request.put("batch_input", batch);
|
||||||
|
|
||||||
|
VaultResponse vaultResponse = vaultOperations.write(String.format("%s/decrypt/%s", path, keyName), request);
|
||||||
|
|
||||||
|
return VaultEncryptionDecryptionResultHelper.fetchDecryptionResult(vaultResponse);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String rewrap(String keyName, String ciphertext) {
|
public String rewrap(String keyName, String ciphertext) {
|
||||||
@@ -303,4 +373,5 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.springframework.vault.support;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decryption Value Object used for encrypt() operations.
|
||||||
|
*
|
||||||
|
* @author Praveendra Singh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VaultDecryptionPayload {
|
||||||
|
private String ciphertext;
|
||||||
|
private VaultTransitContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* factory method helps to create decryption value object using ciphertext
|
||||||
|
* in String
|
||||||
|
*
|
||||||
|
* @param ciphertext
|
||||||
|
* to be decrypted
|
||||||
|
* @return decryption value object
|
||||||
|
*/
|
||||||
|
public static VaultDecryptionPayload of(String ciphertext) {
|
||||||
|
|
||||||
|
if (ciphertext == null) {
|
||||||
|
throw new IllegalArgumentException("The ciphertext must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VaultDecryptionPayload(ciphertext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the decryption context to the value object.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* transit decryption context
|
||||||
|
* @return decryption value object
|
||||||
|
*/
|
||||||
|
public VaultDecryptionPayload with(VaultTransitContext context) {
|
||||||
|
return new VaultDecryptionPayload(this.getCiphertext(), context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.springframework.vault.support;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.vault.VaultException;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the response from decryption operation and provides helper methods to
|
||||||
|
* deal with the data.
|
||||||
|
*
|
||||||
|
* @author Praveendra Singh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VaultDecryptionResult {
|
||||||
|
|
||||||
|
private byte[] cipherText;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the list of plaintext or throws VaultException if error
|
||||||
|
* encountered.
|
||||||
|
*
|
||||||
|
* @return plaintext
|
||||||
|
*/
|
||||||
|
public byte[] get() {
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(error)) {
|
||||||
|
throw new VaultException(error);
|
||||||
|
}
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package org.springframework.vault.support;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the response from encryption/decryption operation and provides helper
|
||||||
|
* methods to generate list of encryption/decryption objects by fetching the
|
||||||
|
* respective fields from VaultResponse.
|
||||||
|
*
|
||||||
|
* @author Praveendra Singh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class VaultEncryptionDecryptionResultHelper {
|
||||||
|
|
||||||
|
public static List<VaultEncryptionResult> fetchEncryptionResult(VaultResponse vaultResponse) {
|
||||||
|
|
||||||
|
List<VaultEncryptionResult> result = new ArrayList<VaultEncryptionResult>();
|
||||||
|
|
||||||
|
for (Map<String, String> data : getBatchData(vaultResponse)) {
|
||||||
|
|
||||||
|
VaultEncryptionResult res = new VaultEncryptionResult(data.get("ciphertext"), data.get("error"));
|
||||||
|
result.add(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<VaultDecryptionResult> fetchDecryptionResult(VaultResponse vaultResponse) {
|
||||||
|
|
||||||
|
List<VaultDecryptionResult> result = new ArrayList<VaultDecryptionResult>();
|
||||||
|
|
||||||
|
for (Map<String, String> data : getBatchData(vaultResponse)) {
|
||||||
|
|
||||||
|
VaultDecryptionResult res = new VaultDecryptionResult(Base64Utils.decodeFromString(data.get("plaintext")),
|
||||||
|
data.get("error"));
|
||||||
|
result.add(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected static List<Map<String, String>> getBatchData(VaultResponse vaultResponse) {
|
||||||
|
return (List<Map<String, String>>) vaultResponse.getData().get("batch_results");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.springframework.vault.support;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption Value Object used for encrypt() operations.
|
||||||
|
*
|
||||||
|
* @author Praveendra Singh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VaultEncryptionPayload {
|
||||||
|
private byte[] plaintext;
|
||||||
|
private VaultTransitContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* factory method helps to create encryption value object using plaintext in
|
||||||
|
* bytes
|
||||||
|
*
|
||||||
|
* @param plaintext
|
||||||
|
* data to be encrypted
|
||||||
|
*
|
||||||
|
* @return encryption value object
|
||||||
|
*/
|
||||||
|
public static VaultEncryptionPayload of(byte[] plaintext) {
|
||||||
|
|
||||||
|
if ((plaintext == null) || (plaintext.length == 0)) {
|
||||||
|
throw new IllegalArgumentException("The plaintext must not be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VaultEncryptionPayload(plaintext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* factory method helps to create encryption value object using plaintext in
|
||||||
|
* String
|
||||||
|
*
|
||||||
|
* @param plaintext
|
||||||
|
* data to be encrypted
|
||||||
|
*
|
||||||
|
* @return encryption value object
|
||||||
|
*/
|
||||||
|
public static VaultEncryptionPayload of(String plaintext) {
|
||||||
|
|
||||||
|
if (plaintext == null) {
|
||||||
|
throw new IllegalArgumentException("The plaintext must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return of(plaintext.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the encryption context to the value object.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* transit encryption context
|
||||||
|
* @return encryption value object
|
||||||
|
*/
|
||||||
|
public VaultEncryptionPayload with(VaultTransitContext context) {
|
||||||
|
return new VaultEncryptionPayload(this.getPlaintext(), context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.springframework.vault.support;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.vault.VaultException;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the response from encryption operation and provides helper methods to
|
||||||
|
* deal with the data.
|
||||||
|
*
|
||||||
|
* @author Praveendra Singh
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VaultEncryptionResult {
|
||||||
|
|
||||||
|
private String cipherText;
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the list of ciphertexts or throws VaultException if error
|
||||||
|
* encountered.
|
||||||
|
*
|
||||||
|
* @return ciphertexts
|
||||||
|
*/
|
||||||
|
public String get() {
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(error)) {
|
||||||
|
throw new VaultException(error);
|
||||||
|
}
|
||||||
|
return cipherText;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,17 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.vault.core;
|
package org.springframework.vault.core;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.vault.VaultException;
|
import org.springframework.vault.VaultException;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultDecryptionResult;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionPayload;
|
||||||
|
import org.springframework.vault.support.VaultEncryptionResult;
|
||||||
import org.springframework.vault.support.VaultMount;
|
import org.springframework.vault.support.VaultMount;
|
||||||
import org.springframework.vault.support.VaultTransitContext;
|
import org.springframework.vault.support.VaultTransitContext;
|
||||||
import org.springframework.vault.support.VaultTransitKey;
|
import org.springframework.vault.support.VaultTransitKey;
|
||||||
@@ -34,20 +43,19 @@ import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
|||||||
import org.springframework.vault.util.IntegrationTestSupport;
|
import org.springframework.vault.util.IntegrationTestSupport;
|
||||||
import org.springframework.vault.util.Version;
|
import org.springframework.vault.util.Version;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.fail;
|
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link VaultTransitTemplate} through
|
* Integration tests for {@link VaultTransitTemplate} through
|
||||||
* {@link VaultTransitOperations}.
|
* {@link VaultTransitOperations}.
|
||||||
*
|
*
|
||||||
* @author Mark Paluch
|
* @author Mark Paluch
|
||||||
|
* @author Praveendra Singh
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
||||||
public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport {
|
public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport {
|
||||||
|
|
||||||
|
private static final String BATCH_INTRODUCED_IN_VERSION = "0.6.5";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private VaultOperations vaultOperations;
|
private VaultOperations vaultOperations;
|
||||||
private VaultTransitOperations transitOperations;
|
private VaultTransitOperations transitOperations;
|
||||||
@@ -98,7 +106,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
|||||||
deleteKey("derived");
|
deleteKey("derived");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createKeyShouldCreateKey() {
|
public void createKeyShouldCreateKey() {
|
||||||
|
|
||||||
@@ -277,4 +285,170 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
|||||||
String rewrapped = transitOperations.rewrap("mykey", ciphertext, transitRequest);
|
String rewrapped = transitOperations.rewrap("mykey", ciphertext, transitRequest);
|
||||||
assertThat(rewrapped).startsWith("vault:v2");
|
assertThat(rewrapped).startsWith("vault:v2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batchEncryptionAndDecryptionTestWithoutContext() {
|
||||||
|
|
||||||
|
if (prepare().getVersion().isLessThan(Version.parse(BATCH_INTRODUCED_IN_VERSION))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transitOperations.createKey("mykey");
|
||||||
|
|
||||||
|
List<String> plaintexts = new ArrayList<String>();
|
||||||
|
plaintexts.add("one");
|
||||||
|
plaintexts.add("two");
|
||||||
|
|
||||||
|
batchEncryptionAndDecryption(plaintexts, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batchEncryptionAndDecryptionTestWithMatchingContext() {
|
||||||
|
|
||||||
|
if (prepare().getVersion().isLessThan(Version.parse(BATCH_INTRODUCED_IN_VERSION))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||||
|
.derived(true) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
transitOperations.createKey("mykey", request);
|
||||||
|
|
||||||
|
List<String> plaintexts = new ArrayList<String>();
|
||||||
|
plaintexts.add("one");
|
||||||
|
plaintexts.add("two");
|
||||||
|
|
||||||
|
List<VaultTransitContext> contexts = new ArrayList<VaultTransitContext>();
|
||||||
|
contexts.add(VaultTransitContext.builder().context("oneContext".getBytes()).build());
|
||||||
|
contexts.add(VaultTransitContext.builder().context("twoContext".getBytes()).build());
|
||||||
|
|
||||||
|
batchEncryptionAndDecryption(plaintexts, contexts, contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batchEncryptionAndDecryptionTestWithNonEqualContext() {
|
||||||
|
|
||||||
|
if (prepare().getVersion().isLessThan(Version.parse(BATCH_INTRODUCED_IN_VERSION))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||||
|
.derived(true) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
transitOperations.createKey("mykey", request);
|
||||||
|
|
||||||
|
List<String> plaintexts = new ArrayList<String>();
|
||||||
|
plaintexts.add("one");
|
||||||
|
plaintexts.add("two");
|
||||||
|
|
||||||
|
List<VaultTransitContext> encryptionContexts = new ArrayList<VaultTransitContext>();
|
||||||
|
encryptionContexts.add(VaultTransitContext.builder().context("oneContext".getBytes()).build());
|
||||||
|
encryptionContexts.add(VaultTransitContext.builder().context("twoContext".getBytes()).build());
|
||||||
|
|
||||||
|
List<VaultTransitContext> decryptionContext = new ArrayList<VaultTransitContext>();
|
||||||
|
decryptionContext.add(VaultTransitContext.builder().context("oneContext".getBytes()).build());
|
||||||
|
|
||||||
|
batchEncryptionAndDecryption(plaintexts, encryptionContexts, decryptionContext);
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return;
|
||||||
|
} catch (VaultException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void batchEncryptionAndDecryptionTestWithNonMatchingContext() {
|
||||||
|
|
||||||
|
if (prepare().getVersion().isLessThan(Version.parse(BATCH_INTRODUCED_IN_VERSION))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||||
|
.derived(true) //
|
||||||
|
.build();
|
||||||
|
|
||||||
|
transitOperations.createKey("mykey", request);
|
||||||
|
|
||||||
|
List<String> plaintexts = new ArrayList<String>();
|
||||||
|
plaintexts.add("one");
|
||||||
|
plaintexts.add("two");
|
||||||
|
|
||||||
|
List<VaultTransitContext> encryptionContexts = new ArrayList<VaultTransitContext>();
|
||||||
|
encryptionContexts.add(VaultTransitContext.builder().context("oneContext".getBytes()).build());
|
||||||
|
encryptionContexts.add(VaultTransitContext.builder().context("twoContext".getBytes()).build());
|
||||||
|
|
||||||
|
List<VaultTransitContext> decryptionContext = new ArrayList<VaultTransitContext>();
|
||||||
|
decryptionContext.add(VaultTransitContext.builder().context("oneContext".getBytes()).build());
|
||||||
|
decryptionContext.add(VaultTransitContext.builder().context("wrongTwoContext".getBytes()).build());
|
||||||
|
|
||||||
|
batchEncryptionAndDecryption(plaintexts, encryptionContexts, decryptionContext);
|
||||||
|
|
||||||
|
} catch (VaultException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void batchEncryptionAndDecryption(List<String> plaintexts, List<VaultTransitContext> encryptionContexts,
|
||||||
|
List<VaultTransitContext> decryptionContext) {
|
||||||
|
|
||||||
|
List<VaultEncryptionPayload> encryptionBatchRequest = new ArrayList<VaultEncryptionPayload>();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (String plaintext : plaintexts) {
|
||||||
|
|
||||||
|
VaultEncryptionPayload req = VaultEncryptionPayload.of(plaintext);
|
||||||
|
|
||||||
|
if (encryptionContexts != null) {
|
||||||
|
if (encryptionContexts.size() >= (index + 1)) {
|
||||||
|
req = req.with(encryptionContexts.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionBatchRequest.add(req);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<VaultEncryptionResult> cipherResult = transitOperations.encrypt("mykey", encryptionBatchRequest);
|
||||||
|
|
||||||
|
List<VaultDecryptionPayload> decryptionBatchRequest = new ArrayList<VaultDecryptionPayload>();
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
for (VaultEncryptionResult cipher : cipherResult) {
|
||||||
|
|
||||||
|
VaultDecryptionPayload req = VaultDecryptionPayload.of(cipher.get());
|
||||||
|
|
||||||
|
if (decryptionContext != null) {
|
||||||
|
if (decryptionContext.size() >= (index + 1)) {
|
||||||
|
req = req.with(decryptionContext.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptionBatchRequest.add(req);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<VaultDecryptionResult> plaintextResult = transitOperations.decrypt("mykey", decryptionBatchRequest);
|
||||||
|
|
||||||
|
Assert.assertEquals(plaintexts.size(), plaintextResult.size());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (String plaintext : plaintexts) {
|
||||||
|
String decrypted = new String(plaintextResult.get(i++).get());
|
||||||
|
Assert.assertEquals(plaintext, decrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,3 +5,8 @@
|
|||||||
=== What's new in Spring Vault 1.0
|
=== What's new in Spring Vault 1.0
|
||||||
|
|
||||||
* Initial Vault support.
|
* Initial Vault support.
|
||||||
|
|
||||||
|
[[new-features.1-1-0]]
|
||||||
|
=== What's new in Spring Vault 1.1.0
|
||||||
|
|
||||||
|
* Batch encryption & decryption support.
|
||||||
|
|||||||
Reference in New Issue
Block a user