Introduce null-safe transit operations.
Require non-null VaultTransitContext in VaultTransitOperations and refactor nonce/context bytes to null-safe values. See gh-112.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user