diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultOperations.java
index 7995067c..eebe7440 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultOperations.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultOperations.java
@@ -84,6 +84,21 @@ public interface VaultOperations {
*/
VaultTokenOperations opsForToken();
+ /**
+ * @return the operations interface to interact with the Vault transform backend.
+ * @since 2.3
+ */
+ VaultTransformOperations opsForTransform();
+
+ /**
+ * Return {@link VaultTransformOperations} if the transit backend is mounted on a
+ * different path than {@code transform}.
+ * @param path the mount path
+ * @return the operations interface to interact with the Vault transform backend.
+ * @since 2.3
+ */
+ VaultTransformOperations opsForTransform(String path);
+
/**
* @return the operations interface to interact with the Vault transit backend.
*/
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
index fd993289..d1775733 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTemplate.java
@@ -320,6 +320,16 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
return new VaultTokenTemplate(this);
}
+ @Override
+ public VaultTransformOperations opsForTransform() {
+ return opsForTransform("transform");
+ }
+
+ @Override
+ public VaultTransformOperations opsForTransform(String path) {
+ return new VaultTransformTemplate(this, path);
+ }
+
@Override
public VaultTransitOperations opsForTransit() {
return opsForTransit("transit");
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformOperations.java
new file mode 100644
index 00000000..79f240af
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformOperations.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020 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
+ *
+ * https://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.core;
+
+import org.springframework.vault.support.*;
+
+import java.util.List;
+
+/**
+ * Interface that specifies operations using the {@code transform} backend.
+ *
+ * @author Lauren Voswinkel
+ * @see Transform
+ * Secrets Engine
+ * @since 2.3
+ */
+public interface VaultTransformOperations {
+ /**
+ * Encodes the provided plaintext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param plaintext must not be empty or {@literal null}.
+ * @return cipher text.
+ */
+ String encode(String roleName, String plaintext);
+
+ /**
+ * Encodes the provided plaintext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param plaintext must not be {@literal null}.
+ * @return cipher text.
+ */
+ TransformCiphertext encode(String roleName, TransformPlaintext plaintext);
+
+ /**
+ * Encodes the provided plaintext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param plaintext must not be empty or {@literal null}.
+ * @param transformRequest must not be {@literal null}. Use
+ * {@link VaultTransformContext#empty()} if no request options provided.
+ * @return cipher text.
+ */
+ String encode(String roleName, byte[] plaintext, VaultTransformContext transformRequest);
+
+ /**
+ * Encode the provided batch of plaintext using the role given and transformation in
+ * each list item. The encryption is done using transformation secret backend's batch
+ * operation.
+ * @param roleName must not be empty or {@literal null}.
+ * @param batchRequest a list of {@link Plaintext} which includes plaintext and an
+ * optional context.
+ * @return the encrypted result in the order of {@code batchRequest} plaintexts.
+ */
+ List encode(String roleName, List batchRequest);
+
+ /**
+ * Decode the provided ciphertext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param ciphertext must not be empty or {@literal null}.
+ * @return plain text.
+ */
+ String decode(String roleName, String ciphertext);
+
+ /**
+ * Decode the provided ciphertext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param ciphertext must not be {@literal null}.
+ * @return plain text.
+ */
+ TransformPlaintext decode(String roleName, TransformCiphertext ciphertext);
+
+ /**
+ * Decode the provided ciphertext using the named role.
+ * @param roleName must not be empty or {@literal null}.
+ * @param ciphertext must not be empty or {@literal null}.
+ * @param transformContext must not be {@literal null}. Use
+ * {@link VaultTransformContext#empty()} if no request options provided.
+ * @return plain text.
+ */
+ String decode(String roleName, String ciphertext, VaultTransformContext transformContext);
+
+ /**
+ * Decode the provided batch of ciphertext using the role given and transformation in
+ * each list item. The decryption is done using transformation secret backend's batch
+ * operation.
+ * @param roleName must not be empty or {@literal null}.
+ * @param batchRequest a list of {@link Ciphertext} which includes plaintext and an
+ * optional context.
+ * @return the decrypted result in the order of {@code batchRequest} ciphertexts.
+ */
+ List decode(String roleName, List batchRequest);
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformTemplate.java
new file mode 100644
index 00000000..feab723c
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransformTemplate.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2020 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
+ *
+ * https://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.core;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.support.*;
+
+import java.util.*;
+
+/**
+ * Default implementation of {@link VaultTransformOperations}.
+ *
+ * @author Lauren Voswinkel
+ * @since 2.3
+ */
+public class VaultTransformTemplate implements VaultTransformOperations {
+
+ private final VaultOperations vaultOperations;
+
+ private final String path;
+
+ /**
+ * Create a new {@link VaultTransformTemplate} given {@link VaultOperations} and the
+ * mount {@code path}.
+ * @param vaultOperations must not be {@literal null}.
+ * @param path must not be empty or {@literal null}.
+ */
+ public VaultTransformTemplate(VaultOperations vaultOperations, String path) {
+
+ Assert.notNull(vaultOperations, "VaultOperations must not be null");
+ Assert.hasText(path, "Path must not be empty");
+
+ this.vaultOperations = vaultOperations;
+ this.path = path;
+ }
+
+ @Override
+ public String encode(String roleName, String plaintext) {
+
+ Assert.hasText(roleName, "Role name must not be empty");
+ Assert.notNull(plaintext, "Plaintext must not be null");
+
+ Map request = new LinkedHashMap<>();
+
+ request.put("value", plaintext);
+
+ return (String) this.vaultOperations.write(String.format("%s/encode/%s", this.path, roleName), request)
+ .getRequiredData().get("encoded_value");
+ }
+
+ @Override
+ public TransformCiphertext encode(String roleName, TransformPlaintext plaintext) {
+
+ Assert.hasText(roleName, "Role name must not be empty");
+ Assert.notNull(plaintext, "Plaintext must not be null");
+
+ String ciphertext = encode(roleName, plaintext.getPlaintext(), plaintext.getContext());
+
+ return toCiphertext(ciphertext, plaintext.getContext());
+ }
+
+ @Override
+ public String encode(String roleName, byte[] plaintext, VaultTransformContext transformContext) {
+
+ Assert.hasText(roleName, "Role name must not be empty");
+ Assert.notNull(plaintext, "Plaintext must not be null");
+ Assert.notNull(transformContext, "VaultTransformContext must not be null");
+
+ Map request = new LinkedHashMap<>();
+
+ String value = new String(plaintext);
+ request.put("value", value);
+
+ applyTransformOptions(transformContext, request);
+
+ return (String) this.vaultOperations.write(String.format("%s/encode/%s", this.path, roleName), request)
+ .getRequiredData().get("encoded_value");
+ }
+
+ @Override
+ public List encode(String roleName, List batchRequest) {
+
+ Assert.hasText(roleName, "Role name must not be empty");
+ Assert.notEmpty(batchRequest, "BatchRequest must not be null and must have at least one entry");
+
+ List