diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java index b17a4b32..d30db770 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitOperations.java @@ -120,11 +120,11 @@ public interface VaultTransitOperations { * * @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. + * @param transitRequest must not be {@literal null}. Use + * {@link VaultTransitContext#empty()} if no request options provided. * @return cipher text. */ - String encrypt(String keyName, byte[] plaintext, - @Nullable VaultTransitContext transitRequest); + String encrypt(String keyName, byte[] plaintext, VaultTransitContext transitRequest); /** * Decrypts the provided plaintext using the named key. @@ -140,11 +140,12 @@ public interface VaultTransitOperations { * * @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. + * @param transitContext must not be {@literal null}. Use + * {@link VaultTransitContext#empty()} if no request options provided. + * @return cipher text. * @return plain text. */ - byte[] decrypt(String keyName, String ciphertext, - @Nullable VaultTransitContext transitRequest); + byte[] decrypt(String keyName, String ciphertext, VaultTransitContext transitContext); /** * Rewrap the provided ciphertext using the latest version of the named key. Because @@ -165,10 +166,10 @@ public interface VaultTransitOperations { * * @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. + * @param transitContext must not be {@literal null}. Use + * {@link VaultTransitContext#empty()} if no request options provided. * @return cipher text. * @see #rotate(String) */ - String rewrap(String keyName, String ciphertext, - @Nullable VaultTransitContext transitRequest); + String rewrap(String keyName, String ciphertext, VaultTransitContext transitContext); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java index 1e83f396..725270e6 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultTransitTemplate.java @@ -26,6 +26,7 @@ import lombok.Data; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.Base64Utils; +import org.springframework.util.ObjectUtils; import org.springframework.vault.support.RawTransitKey; import org.springframework.vault.support.TransitKeyType; import org.springframework.vault.support.VaultResponse; @@ -158,18 +159,17 @@ public class VaultTransitTemplate implements VaultTransitOperations { @Override public String encrypt(String keyName, byte[] plaintext, - @Nullable VaultTransitContext transitRequest) { + VaultTransitContext transitContext) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.notNull(plaintext, "Plain text must not be null"); + Assert.notNull(transitContext, "VaultTransitContext must not be null"); Map request = new LinkedHashMap<>(); request.put("plaintext", Base64Utils.encodeToString(plaintext)); - if (transitRequest != null) { - applyTransitOptions(transitRequest, request); - } + applyTransitOptions(transitContext, request); return (String) vaultOperations .write(String.format("%s/encrypt/%s", path, keyName), request) @@ -180,7 +180,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { public String decrypt(String keyName, String ciphertext) { Assert.hasText(keyName, "KeyName must not be empty"); - Assert.hasText(keyName, "Cipher text must not be empty"); + Assert.hasText(ciphertext, "Cipher text must not be empty"); Map request = new LinkedHashMap<>(); @@ -195,18 +195,17 @@ public class VaultTransitTemplate implements VaultTransitOperations { @Override public byte[] decrypt(String keyName, String ciphertext, - @Nullable VaultTransitContext transitRequest) { + VaultTransitContext transitContext) { Assert.hasText(keyName, "KeyName must not be empty"); - Assert.hasText(keyName, "Cipher text must not be empty"); + Assert.hasText(ciphertext, "Cipher text must not be empty"); + Assert.notNull(transitContext, "VaultTransitContext must not be null"); Map request = new LinkedHashMap<>(); request.put("ciphertext", ciphertext); - if (transitRequest != null) { - applyTransitOptions(transitRequest, request); - } + applyTransitOptions(transitContext, request); String plaintext = (String) vaultOperations .write(String.format("%s/decrypt/%s", path, keyName), request) @@ -231,34 +230,32 @@ public class VaultTransitTemplate implements VaultTransitOperations { @Override public String rewrap(String keyName, String ciphertext, - @Nullable VaultTransitContext transitRequest) { + VaultTransitContext transitContext) { Assert.hasText(keyName, "KeyName must not be empty"); Assert.hasText(ciphertext, "Cipher text must not be empty"); + Assert.notNull(transitContext, "VaultTransitContext must not be null"); Map request = new LinkedHashMap<>(); request.put("ciphertext", ciphertext); - if (transitRequest != null) { - applyTransitOptions(transitRequest, request); - } + applyTransitOptions(transitContext, request); return (String) vaultOperations .write(String.format("%s/rewrap/%s", path, keyName), request) .getRequiredData().get("ciphertext"); } - private void applyTransitOptions(VaultTransitContext transitRequest, + private static void applyTransitOptions(VaultTransitContext context, Map request) { - if (transitRequest.getContext() != null) { - request.put("context", - Base64Utils.encodeToString(transitRequest.getContext())); + if (!ObjectUtils.isEmpty(context.getContext())) { + request.put("context", Base64Utils.encodeToString(context.getContext())); } - if (transitRequest.getNonce() != null) { - request.put("nonce", Base64Utils.encodeToString(transitRequest.getNonce())); + if (!ObjectUtils.isEmpty(context.getNonce())) { + request.put("nonce", Base64Utils.encodeToString(context.getNonce())); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java index ec807fca..39be1040 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultTransitContext.java @@ -15,7 +15,7 @@ */ package org.springframework.vault.support; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Transit backend encryption/decryption/rewrapping context. @@ -28,15 +28,14 @@ public class VaultTransitContext { * Empty (default) {@link VaultTransitContext} without a {@literal context} and * {@literal nonce}. */ - private static final VaultTransitContext EMPTY = new VaultTransitContext(null, null); + private static final VaultTransitContext EMPTY = new VaultTransitContext(new byte[0], + new byte[0]); - @Nullable private final byte[] context; - @Nullable private final byte[] nonce; - VaultTransitContext(@Nullable byte[] context, @Nullable byte[] nonce) { + VaultTransitContext(byte[] context, byte[] nonce) { this.context = context; this.nonce = nonce; } @@ -55,10 +54,31 @@ public class VaultTransitContext { return EMPTY; } + /** + * Create a {@link VaultTransitContext} given {@code context} bytes. + * + * @param context context bytes, must not be {@literal null}. + * @return a {@link VaultTransitContext} for {@code context}. + * @since 2.0 + */ + public static VaultTransitContext fromContext(byte[] context) { + return builder().context(context).build(); + } + + /** + * Create a {@link VaultTransitContext} given {@code nonce} bytes. + * + * @param nonce nonce bytes, must not be {@literal null}. + * @return a {@link VaultTransitContext} for {@code nonce}. + * @since 2.0 + */ + public static VaultTransitContext fromNonce(byte[] nonce) { + return builder().nonce(nonce).build(); + } + /** * @return the key derivation context. */ - @Nullable public byte[] getContext() { return context; } @@ -66,7 +86,6 @@ public class VaultTransitContext { /** * @return the */ - @Nullable public byte[] getNonce() { return nonce; } @@ -76,11 +95,8 @@ public class VaultTransitContext { */ public static class VaultTransitRequestBuilder { - @Nullable - private byte[] context; - - @Nullable - private byte[] nonce; + private byte[] context = new byte[0]; + private byte[] nonce = new byte[0]; VaultTransitRequestBuilder() { } @@ -92,7 +108,10 @@ public class VaultTransitContext { * provided if derivation is enabled. * @return {@code this} {@link VaultTransitRequestBuilder}. */ - public VaultTransitRequestBuilder context(@Nullable byte[] context) { + public VaultTransitRequestBuilder context(byte[] context) { + + Assert.notNull(context, "Context must not be null"); + this.context = context; return this; } @@ -107,7 +126,10 @@ public class VaultTransitContext { * nonce value is never reused * @return {@code this} {@link VaultTransitRequestBuilder}. */ - public VaultTransitRequestBuilder nonce(@Nullable byte[] nonce) { + public VaultTransitRequestBuilder nonce(byte[] nonce) { + + Assert.notNull(nonce, "Nonce must not be null"); + this.nonce = nonce; return this; } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/support/VaultTransitContextUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/support/VaultTransitContextUnitTests.java new file mode 100644 index 00000000..774a5f7c --- /dev/null +++ b/spring-vault-core/src/test/java/org/springframework/vault/support/VaultTransitContextUnitTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.vault.support; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link VaultTransitContext}. + * + * @author Mark Paluch + */ +public class VaultTransitContextUnitTests { + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullContext() { + VaultTransitContext.fromContext(null); + } + + @Test + public void createsFromContext() { + + byte[] bytes = new byte[] { 1 }; + + VaultTransitContext context = VaultTransitContext.fromContext(bytes); + + assertThat(context.getContext()).isEqualTo(bytes); + assertThat(context.getNonce()).isEmpty(); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullNonce() { + VaultTransitContext.fromNonce(null); + } + + @Test + public void createsFromNonce() { + + byte[] bytes = new byte[] { 1 }; + + VaultTransitContext context = VaultTransitContext.fromNonce(bytes); + + assertThat(context.getNonce()).isEqualTo(bytes); + assertThat(context.getContext()).isEmpty(); + } +}