diff --git a/spring-vault-core/pom.xml b/spring-vault-core/pom.xml index dfa38614..8c7ee83f 100644 --- a/spring-vault-core/pom.xml +++ b/spring-vault-core/pom.xml @@ -13,8 +13,6 @@ Spring Vault Core Components jar - http://projects.spring.io/spring-vault/ - com.fasterxml.jackson.core 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 6fd9af84..20cd1953 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 @@ -59,6 +59,19 @@ public interface VaultOperations { */ VaultTokenOperations opsForToken(); + /** + * @return the operations interface to interact with the Vault transit backend. + */ + VaultTransitOperations opsForTransit(); + + /** + * Returns {@link VaultTransitOperations} if the transit backend is mounted on a different path than {@code transit}. + * + * @param path the mount path + * @return the operations interface to interact with the Vault transit backend. + */ + VaultTransitOperations opsForTransit(String path); + /** * Read from a secret backend. Reading data using this method is suitable for secret backends that do not require a * request body. @@ -102,7 +115,8 @@ public interface VaultOperations { void delete(String path); /** - * Executes a Vault {@link ClientCallback}. Allows to interact with Vault using {@link VaultClient} without requiring a session. + * Executes a Vault {@link ClientCallback}. Allows to interact with Vault using {@link VaultClient} without requiring + * a session. * * @param clientCallback the request. * @return the {@link ClientCallback} return value. 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 9158f2d3..49402cd1 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 @@ -119,14 +119,24 @@ public class VaultTemplate implements InitializingBean, VaultOperations { Assert.notNull(sessionManager, "SessionManager must not be null"); } + @Override + public VaultSysOperations opsForSys() { + return new VaultSysTemplate(this); + } + @Override public VaultTokenOperations opsForToken() { return new VaultTokenTemplate(this); } @Override - public VaultSysOperations opsForSys() { - return new VaultSysTemplate(this); + public VaultTransitOperations opsForTransit() { + return opsForTransit("transit"); + } + + @Override + public VaultTransitOperations opsForTransit(String path) { + return new VaultTransitTemplate(this, path); } @Override 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 new file mode 100644 index 00000000..3169dfb0 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java @@ -0,0 +1,140 @@ +/* + * Copyright 2016 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.core; + +import org.springframework.vault.support.VaultTransitKeyConfiguration; +import org.springframework.vault.support.VaultTransitKey; +import org.springframework.vault.support.VaultTransitKeyCreationRequest; +import org.springframework.vault.support.VaultTransitRequest; + +/** + * Interface that specifies operations using the {@code transit} backend. + * + * @author Mark Paluch + * @see Transit Secret Backend + */ +public interface VaultTransitOperations { + + /** + * Creates a new named encryption key given a {@code name}. + * + * @param keyName must not be empty or {@literal null}. + */ + void createKey(String keyName); + + /** + * Creates a new named encryption key given a {@code name} and {@link VaultTransitKeyCreationRequest}. The key options + * set here cannot be changed after key creation. + * + * @param keyName must not be empty or {@literal null}. + * @param createKeyRequest must not be {@literal null}. + */ + void createKey(String keyName, VaultTransitKeyCreationRequest createKeyRequest); + + /** + * Creates a new named encryption key given a {@code name}. + * + * @param keyName must not be empty or {@literal null}. + * @param keyConfiguration must not be {@literal null}. + */ + void configureKey(String keyName, VaultTransitKeyConfiguration keyConfiguration); + + /** + * Returns information about a named encryption key. + * + * @param keyName must not be empty or {@literal null}. + * @return the {@link VaultTransitKey}. + */ + VaultTransitKey getKey(String keyName); + + /** + * Deletes a named encryption key. It will no longer be possible to decrypt any data encrypted with the named key. + * + * @param keyName must not be empty or {@literal null}. + */ + void deleteKey(String keyName); + + /** + * Rotates the version of the named key. After rotation, new plaintext requests will be encrypted with the new version + * of the key. To upgrade ciphertext to be encrypted with the latest version of the key, use + * {@link #rewrap(String, String)}. + * + * @param keyName + * @see #rewrap(String, String) + */ + void rotate(String keyName); + + /** + * Encrypts the provided plaintext using the named key. + * + * @param keyName must not be empty or {@literal null}. + * @param plaintext must not be empty or {@literal null}. + * @return cipher text. + */ + String encrypt(String keyName, String plaintext); + + /** + * Encrypts the provided plaintext using the named key. + * + * @param keyName must not be empty or {@literal null}. + * @param plaintext must not be empty or {@literal null}. + * @param transitRequest may be {@literal null} if no request options provided. + * @return cipher text. + */ + String encrypt(String keyName, byte[] plaintext, VaultTransitRequest transitRequest); + + /** + * Decrypts the provided plaintext using the named key. + * + * @param keyName must not be empty or {@literal null}. + * @param ciphertext must not be empty or {@literal null}. + * @return plain text. + */ + String decrypt(String keyName, String ciphertext); + + /** + * Decrypts the provided plaintext using the named key. + * + * @param keyName must not be empty or {@literal null}. + * @param ciphertext must not be empty or {@literal null}. + * @param transitRequest may be {@literal null} if no request options provided. + * @return plain text. + */ + byte[] decrypt(String keyName, String ciphertext, VaultTransitRequest transitRequest); + + /** + * 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 untrusted users or scripts. + * + * @param keyName must not be empty or {@literal null}. + * @param ciphertext must not be empty or {@literal null}. + * @return cipher text. + * @see #rotate(String) + */ + String rewrap(String keyName, String ciphertext); + + /** + * 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 untrusted users or scripts. + * + * @param keyName must not be empty or {@literal null}. + * @param ciphertext must not be empty or {@literal null}. + * @param transitRequest may be {@literal null} if no request options provided. + * @return cipher text. + * @see #rotate(String) + */ + String rewrap(String keyName, String ciphertext, VaultTransitRequest transitRequest); +} 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 new file mode 100644 index 00000000..dbcdc3ef --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java @@ -0,0 +1,215 @@ +/* + * Copyright 2016 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.core; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.util.Assert; +import org.springframework.util.Base64Utils; +import org.springframework.vault.support.VaultTransitKeyConfiguration; +import org.springframework.vault.support.VaultResponseSupport; +import org.springframework.vault.support.VaultTransitKey; +import org.springframework.vault.support.VaultTransitKeyCreationRequest; +import org.springframework.vault.support.VaultTransitRequest; + +/** + * Default implementation of {@link VaultTransitOperations}. + * + * @author Mark Paluch + */ +public class VaultTransitTemplate implements VaultTransitOperations { + + private final VaultOperations vaultOperations; + + private final String path; + + public VaultTransitTemplate(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 void createKey(final String keyName) { + + Assert.hasText(keyName, "KeyName must not be empty"); + + vaultOperations.write(String.format("%s/keys/%s", path, keyName), null); + } + + @Override + public void createKey(final String keyName, final VaultTransitKeyCreationRequest createKeyRequest) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(createKeyRequest, "VaultTransitKeyCreationRequest must not be empty"); + + vaultOperations.write(String.format("%s/keys/%s", path, keyName), createKeyRequest); + } + + @Override + public void configureKey(final String keyName, final VaultTransitKeyConfiguration keyConfiguration) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(keyConfiguration, "VaultKeyConfiguration must not be empty"); + + vaultOperations.write(String.format("%s/keys/%s/config", path, keyName), keyConfiguration); + } + + @Override + public VaultTransitKey getKey(final String keyName) { + + Assert.hasText(keyName, "KeyName must not be empty"); + + VaultResponseSupport result = vaultOperations.read(String.format("%s/keys/%s", path, keyName), + VaultTransitKey.class); + + if (result != null) { + return result.getData(); + } + + return null; + } + + @Override + public void deleteKey(String keyName) { + + Assert.hasText(keyName, "KeyName must not be empty"); + + vaultOperations.delete(String.format("%s/keys/%s", path, keyName)); + } + + @Override + public void rotate(String keyName) { + + Assert.hasText(keyName, "KeyName must not be empty"); + + vaultOperations.write(String.format("%s/keys/%s/rotate", path, keyName), null); + } + + @Override + public String encrypt(String keyName, String plaintext) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(plaintext, "Plain text must not be null"); + + Map request = new LinkedHashMap(); + + request.put("plaintext", Base64Utils.encodeToString(plaintext.getBytes())); + + return (String) vaultOperations.write(String.format("%s/encrypt/%s", path, keyName), request).getData() + .get("ciphertext"); + } + + @Override + public String encrypt(String keyName, byte[] plaintext, VaultTransitRequest transitRequest) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.notNull(plaintext, "Plain text must not be null"); + + Map request = new LinkedHashMap(); + + request.put("plaintext", Base64Utils.encodeToString(plaintext)); + + if (transitRequest != null) { + applyTransitOptions(transitRequest, request); + } + + return (String) vaultOperations.write(String.format("%s/encrypt/%s", path, keyName), request).getData() + .get("ciphertext"); + } + + @Override + public String decrypt(String keyName, String ciphertext) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.hasText(keyName, "Cipher text must not be empty"); + + Map request = new LinkedHashMap(); + + request.put("ciphertext", ciphertext); + + String plaintext = (String) vaultOperations.write(String.format("%s/decrypt/%s", path, keyName), request).getData() + .get("plaintext"); + + return new String(Base64Utils.decodeFromString(plaintext)); + } + + @Override + public byte[] decrypt(String keyName, String ciphertext, VaultTransitRequest transitRequest) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.hasText(keyName, "Cipher text must not be empty"); + + Map request = new LinkedHashMap(); + + request.put("ciphertext", ciphertext); + + if (transitRequest != null) { + applyTransitOptions(transitRequest, request); + } + + String plaintext = (String) vaultOperations.write(String.format("%s/decrypt/%s", path, keyName), request).getData() + .get("plaintext"); + + return Base64Utils.decodeFromString(plaintext); + } + + @Override + public String rewrap(String keyName, String ciphertext) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.hasText(ciphertext, "Cipher text must not be empty"); + + Map request = new LinkedHashMap(); + request.put("ciphertext", ciphertext); + + return (String) vaultOperations.write(String.format("%s/rewrap/%s", path, keyName), request).getData() + .get("ciphertext"); + } + + @Override + public String rewrap(String keyName, String ciphertext, VaultTransitRequest transitRequest) { + + Assert.hasText(keyName, "KeyName must not be empty"); + Assert.hasText(ciphertext, "Cipher text must not be empty"); + + Map request = new LinkedHashMap(); + + request.put("ciphertext", ciphertext); + + if (transitRequest != null) { + applyTransitOptions(transitRequest, request); + } + + return (String) vaultOperations.write(String.format("%s/rewrap/%s", path, keyName), request).getData() + .get("ciphertext"); + } + + private void applyTransitOptions(VaultTransitRequest transitRequest, Map request) { + + if (transitRequest.getContext() != null) { + request.put("context", Base64Utils.encodeToString(transitRequest.getContext())); + } + + if (transitRequest.getNonce() != null) { + request.put("nonce", Base64Utils.encodeToString(transitRequest.getNonce())); + } + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java index 890436b1..0570a551 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultMount.java @@ -17,6 +17,8 @@ package org.springframework.vault.support; import java.util.Map; +import org.springframework.util.Assert; + /** * Value object to bind Vault HTTP Mount API requests/responses. * @@ -30,9 +32,20 @@ public class VaultMount { private Map config; + /** + * Creates a new {@link VaultMount}. + */ public VaultMount() {} + /** + * Creates a new {@link VaultMount} given a {@code type}. + * + * @param type must not be empty or {@literal null}. + */ public VaultMount(String type) { + + Assert.hasText(type, "Type must not be empty"); + this.type = type; } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java new file mode 100644 index 00000000..44231206 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKey.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 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 java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Value object to bind Vault HTTP Transit Key API responses. + * + * @author Mark Paluch + */ +public class VaultTransitKey { + + @JsonProperty("cipher_mode") private String cipherMode; + + @JsonProperty("deletion_allowed") private boolean deletionAllowed; + + private boolean derived; + + private Map keys; + + @JsonProperty("latest_version") private boolean latestVersion; + + @JsonProperty("min_decryption_version") private int minDecryptionVersion; + + private String name; + + public String getCipherMode() { + return cipherMode; + } + + public void setCipherMode(String cipherMode) { + this.cipherMode = cipherMode; + } + + public boolean isDeletionAllowed() { + return deletionAllowed; + } + + public void setDeletionAllowed(boolean deletionAllowed) { + this.deletionAllowed = deletionAllowed; + } + + public boolean isDerived() { + return derived; + } + + public void setDerived(boolean derived) { + this.derived = derived; + } + + public Map getKeys() { + return keys; + } + + public void setKeys(Map keys) { + this.keys = keys; + } + + public boolean isLatestVersion() { + return latestVersion; + } + + public void setLatestVersion(boolean latestVersion) { + this.latestVersion = latestVersion; + } + + public int getMinDecryptionVersion() { + return minDecryptionVersion; + } + + public void setMinDecryptionVersion(int minDecryptionVersion) { + this.minDecryptionVersion = minDecryptionVersion; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java new file mode 100644 index 00000000..fb01cd72 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016 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.JsonProperty; + +/** + * Value object to bind Vault HTTP Transit Key Config API requests. + * + * @author Mark Paluch + */ +public class VaultTransitKeyConfiguration { + + @JsonProperty("deletion_allowed") private Boolean deletionAllowed; + + @JsonProperty("latest_version") private Integer latestVersion; + + public VaultTransitKeyConfiguration() { + } + + public VaultTransitKeyConfiguration(Boolean deletionAllowed, Integer latestVersion) { + this.deletionAllowed = deletionAllowed; + this.latestVersion = latestVersion; + } + + public Boolean getDeletionAllowed() { + return deletionAllowed; + } + + public void setDeletionAllowed(Boolean deletionAllowed) { + this.deletionAllowed = deletionAllowed; + } + + public Integer getLatestVersion() { + return latestVersion; + } + + public void setLatestVersion(Integer latestVersion) { + this.latestVersion = latestVersion; + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java new file mode 100644 index 00000000..2e8953a4 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitKeyCreationRequest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 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.JsonProperty; + +/** + * Transit backend key creation request options. + * + * @author Mark Paluch + */ +public class VaultTransitKeyCreationRequest { + + private Boolean derived; + + @JsonProperty("convergent_encryption") private Boolean convergentEncryption; + + /** + * Creates a new {@link VaultTransitKeyCreationRequest}. + */ + public VaultTransitKeyCreationRequest() {} + + /** + * Creates a new {@link VaultTransitKeyCreationRequest} and sets {@code derived} and {@code convergentEncryption} + * properties. + * + * @param derived + * @param convergentEncryption + */ + public VaultTransitKeyCreationRequest(boolean derived, boolean convergentEncryption) { + this.derived = derived; + this.convergentEncryption = convergentEncryption; + } + + public Boolean getDerived() { + return derived; + } + + public void setDerived(Boolean derived) { + this.derived = derived; + } + + public Boolean getConvergentEncryption() { + return convergentEncryption; + } + + public void setConvergentEncryption(Boolean convergentEncryption) { + this.convergentEncryption = convergentEncryption; + } +} diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java new file mode 100644 index 00000000..8427ecf4 --- /dev/null +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 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; + +/** + * Transit backend encryption/decryption/rewrapping request options. + * + * @author Mark Paluch + */ +public class VaultTransitRequest { + + private byte[] context; + + private byte[] nonce; + + public byte[] getContext() { + return context; + } + + public void setContext(byte[] context) { + this.context = context; + } + + public byte[] getNonce() { + return nonce; + } + + public void setNonce(byte[] nonce) { + this.nonce = nonce; + } +} diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultIntegrationTestConfiguration.java b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultIntegrationTestConfiguration.java index ebd28354..cedebf85 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/core/VaultIntegrationTestConfiguration.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultIntegrationTestConfiguration.java @@ -15,10 +15,8 @@ */ package org.springframework.vault.core; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.vault.authentication.ClientAuthentication; -import org.springframework.vault.authentication.DefaultSessionManager; import org.springframework.vault.authentication.TokenAuthentication; import org.springframework.vault.client.VaultEndpoint; import org.springframework.vault.config.AbstractVaultConfiguration; @@ -48,26 +46,3 @@ class VaultIntegrationTestConfiguration extends AbstractVaultConfiguration { return Settings.createSslConfiguration(); } } - -class test { - - @Bean - public VaultTemplate vaultTemplate() { - - VaultTemplate vaultTemplate = new VaultTemplate(); - vaultTemplate.setSessionManager(sessionManager()); - vaultTemplate.setVaultClientFactory(clientFactory()); - - return vaultTemplate; - } - - @Bean - public DefaultVaultClientFactory clientFactory() { - return new DefaultVaultClientFactory(); - } - - @Bean - public DefaultSessionManager sessionManager() { - return new DefaultSessionManager(new TokenAuthentication("…")); - } -} 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 new file mode 100644 index 00000000..31a99729 --- /dev/null +++ b/spring-vault-core/src/test/java/org/springframework/vault/core/VaultTransitTemplateIntegrationTests.java @@ -0,0 +1,201 @@ +/* + * Copyright 2016 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.core; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.vault.client.VaultException; +import org.springframework.vault.support.VaultTransitKeyConfiguration; +import org.springframework.vault.support.VaultMount; +import org.springframework.vault.support.VaultTransitKey; +import org.springframework.vault.support.VaultTransitKeyCreationRequest; +import org.springframework.vault.support.VaultTransitRequest; +import org.springframework.vault.util.IntegrationTestSupport; + +/** + * Integration tests for {@link VaultTransitTemplate} through {@link VaultTransitOperations}. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class) +public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport { + + @Autowired private VaultOperations vaultOperations; + private VaultTransitOperations transitOperations; + + @Before + public void before() throws Exception { + transitOperations = vaultOperations.opsForTransit(); + + if (!vaultOperations.opsForSys().getMounts().containsKey("transit/")) { + vaultOperations.opsForSys().mount("transit", new VaultMount("transit")); + } + + try { + transitOperations.configureKey("mykey", new VaultTransitKeyConfiguration(true, null)); + } catch (Exception e) {} + + try { + transitOperations.deleteKey("mykey"); + } catch (Exception e) {} + } + + @Test + public void createKeyShouldCreateKey() throws Exception { + + transitOperations.createKey("mykey"); + + VaultTransitKey mykey = transitOperations.getKey("mykey"); + + assertThat(mykey.getCipherMode()).isEqualTo("aes-gcm"); + assertThat(mykey.getName()).isEqualTo("mykey"); + assertThat(mykey.isDeletionAllowed()).isFalse(); + assertThat(mykey.isDerived()).isFalse(); + assertThat(mykey.getMinDecryptionVersion()).isEqualTo(1); + assertThat(mykey.isLatestVersion()).isTrue(); + } + + @Test + public void createKeyShouldCreateKeyWithOptions() throws Exception { + + VaultTransitKeyCreationRequest request = new VaultTransitKeyCreationRequest(); + request.setConvergentEncryption(true); + request.setDerived(true); + + transitOperations.createKey("mykey", request); + + VaultTransitKey mykey = transitOperations.getKey("mykey"); + + assertThat(mykey.getCipherMode()).isEqualTo("aes-gcm"); + assertThat(mykey.getName()).isEqualTo("mykey"); + assertThat(mykey.isDeletionAllowed()).isFalse(); + assertThat(mykey.isDerived()).isTrue(); + assertThat(mykey.getMinDecryptionVersion()).isEqualTo(1); + assertThat(mykey.isLatestVersion()).isTrue(); + } + + @Test + public void getKeyShouldReturnNullIfKeyNotExists() throws Exception { + + VaultTransitKey key = transitOperations.getKey("hello-world"); + assertThat(key).isNull(); + } + + @Test + public void deleteKeyShouldFailIfKeyNotExists() throws Exception { + + try { + transitOperations.deleteKey("hello-world"); + fail("Missing VaultException"); + } catch (VaultException e) { + assertThat(e).hasMessageContaining("could not delete"); + } + } + + @Test + public void deleteKeyShouldDeleteKey() throws Exception { + + transitOperations.createKey("mykey"); + transitOperations.configureKey("mykey", new VaultTransitKeyConfiguration(true, null)); + transitOperations.deleteKey("mykey"); + + assertThat(transitOperations.getKey("mykey")).isNull(); + } + + @Test + public void encryptShouldCreateCiphertext() throws Exception { + + transitOperations.createKey("mykey"); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world"); + assertThat(ciphertext).startsWith("vault:v"); + } + + @Test + public void encryptShouldCreateCiphertextWithNonceAndContext() throws Exception { + + transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + + VaultTransitRequest transitRequest = new VaultTransitRequest(); + transitRequest.setContext("blubb".getBytes()); + transitRequest.setNonce("123456789012".getBytes()); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); + assertThat(ciphertext).startsWith("vault:v1:"); + } + + @Test + public void decryptShouldCreatePlaintext() throws Exception { + + transitOperations.createKey("mykey"); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world"); + String plaintext = transitOperations.decrypt("mykey", ciphertext); + + assertThat(plaintext).isEqualTo("hello-world"); + } + + @Test + public void decryptShouldCreatePlaintextWithNonceAndContext() throws Exception { + + transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + + VaultTransitRequest transitRequest = new VaultTransitRequest(); + transitRequest.setContext("blubb".getBytes()); + transitRequest.setNonce("123456789012".getBytes()); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); + + byte[] plaintext = transitOperations.decrypt("mykey", ciphertext, transitRequest); + assertThat(new String(plaintext)).isEqualTo("hello-world"); + } + + @Test + public void encryptAndRewrapShouldCreateCiphertext() throws Exception { + + transitOperations.createKey("mykey"); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world"); + transitOperations.rotate("mykey"); + + String rewrapped = transitOperations.rewrap("mykey", ciphertext); + + assertThat(rewrapped).startsWith("vault:v2:"); + } + + @Test + public void encryptAndRewrapShouldCreateCiphertextWithNonceAndContext() throws Exception { + + transitOperations.createKey("mykey", new VaultTransitKeyCreationRequest(true, true)); + + VaultTransitRequest transitRequest = new VaultTransitRequest(); + transitRequest.setContext("blubb".getBytes()); + transitRequest.setNonce("123456789012".getBytes()); + + String ciphertext = transitOperations.encrypt("mykey", "hello-world".getBytes(), transitRequest); + transitOperations.rotate("mykey"); + + String rewrapped = transitOperations.rewrap("mykey", ciphertext, transitRequest); + assertThat(rewrapped).startsWith("vault:v2"); + } +}