Support ecdsa and ed25519 transit key metadata.
We now support key metadata retrieval via VaultTransitKey. Vault reports a different metadata structure depending on the key type. Fixes gh-149.
This commit is contained in:
@@ -454,7 +454,7 @@ public class VaultTransitTemplate implements VaultTransitOperations {
|
||||
|
||||
private boolean exportable;
|
||||
|
||||
private Map<String, Long> keys = Collections.emptyMap();
|
||||
private Map<String, Object> keys = Collections.emptyMap();
|
||||
|
||||
@JsonProperty("latest_version")
|
||||
private boolean latestVersion;
|
||||
|
||||
@@ -42,9 +42,9 @@ public interface VaultTransitKey {
|
||||
boolean isExportable();
|
||||
|
||||
/**
|
||||
* @return a {@link Map} of key version to its creation timestamp.
|
||||
* @return a {@link Map} of key version to its Vault-specific representation.
|
||||
*/
|
||||
Map<String, Long> getKeys();
|
||||
Map<String, Object> getKeys();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the key represents the latest version.
|
||||
|
||||
@@ -45,6 +45,18 @@ public class VaultTransitKeyCreationRequest {
|
||||
this.exportable = exportable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultTransitKeyCreationRequest} specifically for a {@code type}
|
||||
* .
|
||||
* @param the key type to use, must not be {@literal null} or empty.
|
||||
* @return a new {@link VaultTransitKeyCreationRequest} for the given key {@code type}
|
||||
* .
|
||||
* @since 2.0
|
||||
*/
|
||||
public static VaultTransitKeyCreationRequest ofKeyType(String type) {
|
||||
return builder().type(type).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new {@link VaultTransitKeyCreationRequestBuilder}.
|
||||
*/
|
||||
@@ -99,14 +111,14 @@ public class VaultTransitKeyCreationRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure key derivation.
|
||||
* Configure the key type.
|
||||
*
|
||||
* @param type the type of key to create, must not be empty or {@literal null}.
|
||||
* @return {@code this} {@link VaultTransitKeyCreationRequestBuilder}.
|
||||
*/
|
||||
public VaultTransitKeyCreationRequestBuilder type(String type) {
|
||||
|
||||
Assert.hasText(type, "Type must not be empty");
|
||||
Assert.hasText(type, "Type must not be null or empty");
|
||||
|
||||
this.type = type;
|
||||
return this;
|
||||
|
||||
@@ -27,19 +27,13 @@ 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.VaultException;
|
||||
import org.springframework.vault.support.RawTransitKey;
|
||||
import org.springframework.vault.support.TransitKeyType;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultTransitKey;
|
||||
import org.springframework.vault.support.VaultTransitKeyConfiguration;
|
||||
import org.springframework.vault.support.VaultTransitKeyCreationRequest;
|
||||
import org.springframework.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.vault.util.Version;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultTemplate} using the {@code transit} backend.
|
||||
@@ -51,7 +45,6 @@ import static org.junit.Assume.assumeTrue;
|
||||
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
||||
public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
|
||||
@Autowired
|
||||
private VaultOperations vaultOperations;
|
||||
|
||||
@@ -71,10 +64,6 @@ public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport
|
||||
removeKeys();
|
||||
|
||||
vaultOperations.write("transit/keys/mykey", null);
|
||||
vaultOperations.write("transit/keys/derived",
|
||||
Collections.singletonMap("derived", true));
|
||||
vaultOperations.write("transit/keys/export",
|
||||
Collections.singletonMap("exportable", true));
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -106,8 +95,6 @@ public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport
|
||||
}
|
||||
else {
|
||||
deleteKey("mykey");
|
||||
deleteKey("derived");
|
||||
deleteKey("export");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,76 +125,4 @@ public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport
|
||||
assertThat((String) decrypted.getData().get("plaintext")).isEqualTo(
|
||||
Base64.encodeBase64String("that message is secret".getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNewExportableKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
VaultTransitKeyCreationRequest vaultTransitKeyCreationRequest = VaultTransitKeyCreationRequest
|
||||
.builder().exportable(true).derived(true).build();
|
||||
|
||||
vaultTransitOperations.createKey("export-test", vaultTransitKeyCreationRequest);
|
||||
|
||||
VaultTransitKey vaultTransitKey = vaultTransitOperations.getKey("export-test");
|
||||
|
||||
assertThat(vaultTransitKey.getName()).isEqualTo("export-test");
|
||||
assertThat(vaultTransitKey.isExportable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNotExportableKeyByDefault() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
vaultTransitOperations.createKey("no-export");
|
||||
|
||||
VaultTransitKey vaultTransitKey = vaultTransitOperations.getKey("no-export");
|
||||
|
||||
assertThat(vaultTransitKey.getName()).isEqualTo("no-export");
|
||||
assertThat(vaultTransitKey.isExportable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportEncryptionKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
RawTransitKey rawTransitKey = vaultTransitOperations.exportKey("export",
|
||||
TransitKeyType.ENCRYPTION_KEY);
|
||||
|
||||
assertThat(rawTransitKey.getName()).isEqualTo("export");
|
||||
assertThat(rawTransitKey.getKeys()).isNotEmpty();
|
||||
assertThat(rawTransitKey.getKeys().get("1")).isNotBlank();
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void shouldNotAllowExportSigningKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
vaultTransitOperations.exportKey("export", TransitKeyType.SIGNING_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportHmacKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
RawTransitKey rawTransitKey = vaultTransitOperations.exportKey("export",
|
||||
TransitKeyType.HMAC_KEY);
|
||||
|
||||
assertThat(rawTransitKey.getName()).isEqualTo("export");
|
||||
assertThat(rawTransitKey.getKeys()).isNotEmpty();
|
||||
assertThat(rawTransitKey.getKeys().get("1")).isNotBlank();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.vault.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -29,6 +30,8 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.Ciphertext;
|
||||
import org.springframework.vault.support.Plaintext;
|
||||
import org.springframework.vault.support.RawTransitKey;
|
||||
import org.springframework.vault.support.TransitKeyType;
|
||||
import org.springframework.vault.support.VaultDecryptionResult;
|
||||
import org.springframework.vault.support.VaultEncryptionResult;
|
||||
import org.springframework.vault.support.VaultMount;
|
||||
@@ -58,8 +61,11 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
|
||||
@Autowired
|
||||
private VaultOperations vaultOperations;
|
||||
|
||||
private VaultTransitOperations transitOperations;
|
||||
|
||||
private Version vaultVersion;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
@@ -69,6 +75,8 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
vaultOperations.opsForSys().mount("transit", VaultMount.create("transit"));
|
||||
}
|
||||
|
||||
vaultVersion = prepare().getVersion();
|
||||
|
||||
removeKeys();
|
||||
}
|
||||
|
||||
@@ -95,7 +103,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
|
||||
private void removeKeys() {
|
||||
|
||||
if (prepare().getVersion().isGreaterThanOrEqualTo(Version.parse("0.6.4"))) {
|
||||
if (vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.4"))) {
|
||||
List<String> keys = vaultOperations.opsForTransit().getKeys();
|
||||
for (String keyName : keys) {
|
||||
deleteKey(keyName);
|
||||
@@ -103,7 +111,9 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
}
|
||||
else {
|
||||
deleteKey("mykey");
|
||||
deleteKey("derived");
|
||||
deleteKey("export");
|
||||
deleteKey("ecdsa-key");
|
||||
deleteKey("ed-key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +133,38 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
assertThat(mykey.isLatestVersion()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createKeyShouldCreateEcDsaKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.4")));
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest
|
||||
.ofKeyType("ecdsa-p256");
|
||||
|
||||
transitOperations.createKey("ecdsa-key", request);
|
||||
|
||||
VaultTransitKey mykey = transitOperations.getKey("ecdsa-key");
|
||||
|
||||
assertThat(mykey.getType()).startsWith("ecdsa");
|
||||
assertThat(mykey.getKeys()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createKeyShouldCreateEdKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.4")));
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest
|
||||
.ofKeyType("ed25519");
|
||||
|
||||
transitOperations.createKey("ed-key", request);
|
||||
|
||||
VaultTransitKey mykey = transitOperations.getKey("ed-key");
|
||||
|
||||
assertThat(mykey.getType()).startsWith("ed");
|
||||
assertThat(mykey.getKeys()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createKeyShouldCreateKeyWithOptions() {
|
||||
|
||||
@@ -145,7 +187,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldEnumerateKey() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(Version.parse("0.6.4")));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.4")));
|
||||
|
||||
assertThat(transitOperations.getKeys()).isEmpty();
|
||||
|
||||
@@ -326,8 +368,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldBatchEncrypt() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(
|
||||
BATCH_INTRODUCED_IN_VERSION));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(BATCH_INTRODUCED_IN_VERSION));
|
||||
|
||||
transitOperations.createKey("mykey");
|
||||
|
||||
@@ -341,8 +382,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldBatchDecrypt() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(
|
||||
BATCH_INTRODUCED_IN_VERSION));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(BATCH_INTRODUCED_IN_VERSION));
|
||||
|
||||
transitOperations.createKey("mykey");
|
||||
|
||||
@@ -364,8 +404,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldBatchEncryptWithContext() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(
|
||||
BATCH_INTRODUCED_IN_VERSION));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(BATCH_INTRODUCED_IN_VERSION));
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||
.derived(true) //
|
||||
@@ -389,8 +428,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldBatchDecryptWithContext() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(
|
||||
BATCH_INTRODUCED_IN_VERSION));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(BATCH_INTRODUCED_IN_VERSION));
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||
.derived(true) //
|
||||
@@ -416,8 +454,7 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
@Test
|
||||
public void shouldBatchDecryptWithWrongContext() {
|
||||
|
||||
assumeTrue(prepare().getVersion().isGreaterThanOrEqualTo(
|
||||
BATCH_INTRODUCED_IN_VERSION));
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(BATCH_INTRODUCED_IN_VERSION));
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder() //
|
||||
.derived(true) //
|
||||
@@ -446,4 +483,121 @@ public class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport
|
||||
assertThat(decrypted.get(1).isSuccessful()).isEqualTo(false);
|
||||
assertThat(decrypted.get(1).getCause()).isInstanceOf(VaultException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNewExportableKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
VaultTransitKeyCreationRequest vaultTransitKeyCreationRequest = VaultTransitKeyCreationRequest
|
||||
.builder().exportable(true).derived(true).build();
|
||||
|
||||
vaultTransitOperations.createKey("export-test", vaultTransitKeyCreationRequest);
|
||||
|
||||
VaultTransitKey vaultTransitKey = vaultTransitOperations.getKey("export-test");
|
||||
|
||||
assertThat(vaultTransitKey.getName()).isEqualTo("export-test");
|
||||
assertThat(vaultTransitKey.isExportable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNotExportableKeyByDefault() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations vaultTransitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
vaultTransitOperations.createKey("no-export");
|
||||
|
||||
VaultTransitKey vaultTransitKey = vaultTransitOperations.getKey("no-export");
|
||||
|
||||
assertThat(vaultTransitKey.getName()).isEqualTo("no-export");
|
||||
assertThat(vaultTransitKey.isExportable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportEncryptionKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
vaultOperations.write("transit/keys/export",
|
||||
Collections.singletonMap("exportable", true));
|
||||
|
||||
RawTransitKey rawTransitKey = transitOperations.exportKey("export",
|
||||
TransitKeyType.ENCRYPTION_KEY);
|
||||
|
||||
assertThat(rawTransitKey.getName()).isEqualTo("export");
|
||||
assertThat(rawTransitKey.getKeys()).isNotEmpty();
|
||||
assertThat(rawTransitKey.getKeys().get("1")).isNotBlank();
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void shouldNotAllowExportSigningKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
vaultOperations.write("transit/keys/export",
|
||||
Collections.singletonMap("exportable", true));
|
||||
|
||||
transitOperations.exportKey("export", TransitKeyType.SIGNING_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportHmacKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
vaultOperations.write("transit/keys/export",
|
||||
Collections.singletonMap("exportable", true));
|
||||
|
||||
RawTransitKey rawTransitKey = transitOperations.exportKey("export",
|
||||
TransitKeyType.HMAC_KEY);
|
||||
|
||||
assertThat(rawTransitKey.getName()).isEqualTo("export");
|
||||
assertThat(rawTransitKey.getKeys()).isNotEmpty();
|
||||
assertThat(rawTransitKey.getKeys().get("1")).isNotBlank();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportEcDsaKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations transitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder()
|
||||
.type("ecdsa-p256").exportable(true).build();
|
||||
|
||||
transitOperations.createKey("ecdsa-key", request);
|
||||
|
||||
RawTransitKey hmacKey = transitOperations.exportKey("ecdsa-key",
|
||||
TransitKeyType.HMAC_KEY);
|
||||
RawTransitKey signingKey = transitOperations.exportKey("ecdsa-key",
|
||||
TransitKeyType.SIGNING_KEY);
|
||||
|
||||
assertThat(hmacKey.getKeys()).isNotEmpty();
|
||||
assertThat(signingKey.getKeys()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExportEdKey() {
|
||||
|
||||
assumeTrue(vaultVersion.isGreaterThanOrEqualTo(Version.parse("0.6.5")));
|
||||
|
||||
VaultTransitOperations transitOperations = vaultOperations.opsForTransit();
|
||||
|
||||
VaultTransitKeyCreationRequest request = VaultTransitKeyCreationRequest.builder()
|
||||
.type("ed25519").exportable(true).build();
|
||||
|
||||
transitOperations.createKey("ed-key", request);
|
||||
|
||||
RawTransitKey hmacKey = transitOperations.exportKey("ed-key",
|
||||
TransitKeyType.HMAC_KEY);
|
||||
RawTransitKey signingKey = transitOperations.exportKey("ed-key",
|
||||
TransitKeyType.SIGNING_KEY);
|
||||
|
||||
assertThat(hmacKey.getKeys()).isNotEmpty();
|
||||
assertThat(signingKey.getKeys()).isNotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user