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:
Mark Paluch
2017-10-02 15:01:01 +02:00
parent 4c079a0680
commit a38414d3c5
5 changed files with 184 additions and 103 deletions

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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();
}
}