From 253e8665d41249f2f229a147a18d576f77cb21f9 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 28 May 2023 19:23:34 +0200 Subject: [PATCH] Support `prehashed` for the Transit engine. Closes gh-745 Original pull request: gh-791 --- .../vault/core/VaultTransitTemplate.java | 4 +- .../vault/support/Plaintext.java | 2 +- .../vault/support/VaultSignRequest.java | 32 +++++++++-- .../VaultSignatureVerificationRequest.java | 22 +++++++- .../VaultTransitTemplateIntegrationTests.java | 54 ++++++++++++++++++- 5 files changed, 103 insertions(+), 11 deletions(-) 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 7d855c88..e56fed3a 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 @@ -425,7 +425,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { static Map toRequestBody(VaultSignRequest signRequest) { - Map request = new LinkedHashMap<>(3); + Map request = new LinkedHashMap<>(4); PropertyMapper mapper = PropertyMapper.get(); mapper.from(signRequest.getPlaintext()::getPlaintext) @@ -433,6 +433,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { .to("input", request); mapper.from(signRequest::getHashAlgorithm).whenHasText().to("hash_algorithm", request); mapper.from(signRequest::getSignatureAlgorithm).whenHasText().to("signature_algorithm", request); + mapper.from(signRequest::isPrehashed).to("prehashed", request); return request; } @@ -482,6 +483,7 @@ public class VaultTransitTemplate implements VaultTransitOperations { .to("signature", request); mapper.from(verificationRequest::getHashAlgorithm).whenHasText().to("hash_algorithm", request); mapper.from(verificationRequest::getSignatureAlgorithm).whenHasText().to("signature_algorithm", request); + mapper.from(verificationRequest::isPrehashed).to("prehashed", request); return request; } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/Plaintext.java b/spring-vault-core/src/main/java/org/springframework/vault/support/Plaintext.java index a36938f0..c6cabaad 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/Plaintext.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/Plaintext.java @@ -17,8 +17,8 @@ package org.springframework.vault.support; import java.nio.charset.Charset; import java.util.Arrays; +import java.util.Base64; import java.util.Objects; - import org.springframework.util.Assert; /** diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java index 010dc3c4..c483d8f8 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignRequest.java @@ -15,8 +15,6 @@ */ package org.springframework.vault.support; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -36,11 +34,15 @@ public class VaultSignRequest { private final @Nullable String signatureAlgorithm; - private VaultSignRequest(Plaintext plaintext, @Nullable String hashAlgorithm, @Nullable String signatureAlgorithm) { + private final boolean prehashed; + + private VaultSignRequest(Plaintext plaintext, @Nullable String hashAlgorithm, @Nullable String signatureAlgorithm, + boolean prehashed) { this.plaintext = plaintext; this.hashAlgorithm = hashAlgorithm; this.signatureAlgorithm = signatureAlgorithm; + this.prehashed = prehashed; } /** @@ -97,6 +99,13 @@ public class VaultSignRequest { return getSignatureAlgorithm(); } + /** + * @return true if the input is already hashed. + */ + public boolean isPrehashed() { + return this.prehashed; + } + /** * Builder to build a {@link VaultSignRequest}. */ @@ -108,6 +117,8 @@ public class VaultSignRequest { private @Nullable String signatureAlgorithm; + private boolean prehashed; + /** * Configure the input to be used to create the signature. * @param input base input to create the signature, must not be {@literal null}. @@ -166,16 +177,27 @@ public class VaultSignRequest { return signatureAlgorithm(algorithm); } + /** + * Set to true when the input is already hashed. If the key type is rsa-2048, + * rsa-3072 or rsa-4096, then the algorithm used to hash the input should be + * indicated by the hash_algorithm parameter. + * @param prehashed whether the input is already hashed + * @return {@code this} {@link VaultSignRequestBuilder}. + */ + public VaultSignRequestBuilder prehashed(boolean prehashed) { + this.prehashed = prehashed; + return this; + } + /** * Build a new {@link VaultSignRequest} instance. Requires * {@link #plaintext(Plaintext)} to be configured. * @return a new {@link VaultSignRequest}. */ public VaultSignRequest build() { - Assert.notNull(this.plaintext, "Plaintext input must not be null"); - return new VaultSignRequest(this.plaintext, this.hashAlgorithm, this.signatureAlgorithm); + return new VaultSignRequest(this.plaintext, this.hashAlgorithm, this.signatureAlgorithm, this.prehashed); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java index d9c04d73..43fa0215 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/support/VaultSignatureVerificationRequest.java @@ -15,6 +15,7 @@ */ package org.springframework.vault.support; +import org.checkerframework.checker.units.qual.A; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -39,14 +40,17 @@ public class VaultSignatureVerificationRequest { private final @Nullable String signatureAlgorithm; + private final boolean prehashed; + private VaultSignatureVerificationRequest(Plaintext plaintext, @Nullable Signature signature, @Nullable Hmac hmac, - @Nullable String hashAlgorithm, @Nullable String signatureAlgorithm) { + @Nullable String hashAlgorithm, @Nullable String signatureAlgorithm, boolean prehashed) { this.plaintext = plaintext; this.signature = signature; this.hmac = hmac; this.hashAlgorithm = hashAlgorithm; this.signatureAlgorithm = signatureAlgorithm; + this.prehashed = prehashed; } /** @@ -136,6 +140,13 @@ public class VaultSignatureVerificationRequest { return getSignatureAlgorithm(); } + /** + * @return true if the input is already hashed. + */ + public boolean isPrehashed() { + return this.prehashed; + } + /** * Builder to build a {@link VaultSignatureVerificationRequest}. */ @@ -151,6 +162,8 @@ public class VaultSignatureVerificationRequest { private @Nullable String signatureAlgorithm; + private boolean prehashed; + /** * Configure the {@link Plaintext} input to be used to verify the signature. * @param input base input, must not be {@literal null}. @@ -230,6 +243,11 @@ public class VaultSignatureVerificationRequest { return this; } + public VaultSignatureVerificationRequestBuilder prehashed(boolean prehashed) { + this.prehashed = prehashed; + return this; + } + /** * Configure the algorithm to be used for the operation. * @param algorithm Specify the algorithm to be used for the operation. Supported @@ -255,7 +273,7 @@ public class VaultSignatureVerificationRequest { Assert.isTrue(this.hmac != null || this.signature != null, "Either Signature or Hmac must not be null"); return new VaultSignatureVerificationRequest(this.input, this.signature, this.hmac, this.hashAlgorithm, - this.signatureAlgorithm); + this.signatureAlgorithm, this.prehashed); } } 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 index 780a8cdb..7d83032b 100644 --- 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 @@ -15,12 +15,15 @@ */ package org.springframework.vault.core; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.fail; + import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; - import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -29,7 +32,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -807,6 +809,54 @@ class VaultTransitTemplateIntegrationTests extends IntegrationTestSupport { assertThat(signature.getSignature()).isNotEmpty(); } + @Test + @RequiresVaultVersion(SIGN_VERIFY_INTRODUCED_IN_VERSION) + void signAndVerifyWithPrehashedInput() { + String keyName = createEcdsaP256Key(); + Plaintext plaintext = Plaintext.of("P8m2iUWdc4+MiKOkiqnjNUIBa3pAUuABqqU2/KdIE8s="); + VaultSignRequest signRequest = VaultSignRequest.builder() + .plaintext(plaintext) + .signatureAlgorithm("pkcs1v15") + .prehashed(true) + .build(); + + Signature signature = this.transitOperations.sign(keyName, signRequest); + assertThat(signature.getSignature()).isNotEmpty(); + + VaultSignatureVerificationRequest verifyRequest = VaultSignatureVerificationRequest.builder() + .prehashed(true) + .plaintext(plaintext) + .signature(signature) + .signatureAlgorithm("pkcs1v15") + .build(); + SignatureValidation validation = this.transitOperations.verify(keyName, verifyRequest); + assertThat(validation.isValid()).isTrue(); + } + + @Test + @RequiresVaultVersion(SIGN_VERIFY_INTRODUCED_IN_VERSION) + void signWithPrehashedAndVerifyWithoutShouldFail() { + String keyName = createEcdsaP256Key(); + Plaintext plaintext = Plaintext.of("P8m2iUWdc4+MiKOkiqnjNUIBa3pAUuABqqU2/KdIE8s="); + VaultSignRequest signRequest = VaultSignRequest.builder() + .plaintext(plaintext) + .signatureAlgorithm("pkcs1v15") + .prehashed(true) + .build(); + + Signature signature = this.transitOperations.sign(keyName, signRequest); + assertThat(signature.getSignature()).isNotEmpty(); + + VaultSignatureVerificationRequest verifyRequest = VaultSignatureVerificationRequest.builder() + .prehashed(false) + .plaintext(plaintext) + .signature(signature) + .signatureAlgorithm("pkcs1v15") + .build(); + SignatureValidation validation = this.transitOperations.verify(keyName, verifyRequest); + assertThat(validation.isValid()).isFalse(); + } + @Test @RequiresVaultVersion(SIGN_VERIFY_INTRODUCED_IN_VERSION) void shouldVerifyValidSignature() {