diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitSignBody.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitSignBody.java
index b2cdc2178a2ce..81e47c465a6d7 100644
--- a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitSignBody.java
+++ b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitSignBody.java
@@ -15,5 +15,7 @@ public class VaultTransitSignBody implements VaultModel {
public Boolean prehashed;
@JsonProperty("signature_algorithm")
public String signatureAlgorithm;
+ @JsonProperty("marshaling_algorithm")
+ public String marshalingAlgorithm;
}
diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitVerifyBody.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitVerifyBody.java
index da6c136176c7d..444ea9e1fd159 100644
--- a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitVerifyBody.java
+++ b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/transit/VaultTransitVerifyBody.java
@@ -13,5 +13,7 @@ public class VaultTransitVerifyBody implements VaultModel {
public Boolean prehashed;
@JsonProperty("signature_algorithm")
public String signatureAlgorithm;
+ @JsonProperty("marshaling_algorithm")
+ public String marshalingAlgorithm;
}
diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/VaultTransitSecretEngine.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/VaultTransitSecretEngine.java
index 9312af1b967e0..1c81fe9cfc85e 100644
--- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/VaultTransitSecretEngine.java
+++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/VaultTransitSecretEngine.java
@@ -9,6 +9,7 @@
import io.quarkus.vault.transit.KeyConfigRequestDetail;
import io.quarkus.vault.transit.KeyCreationRequestDetail;
import io.quarkus.vault.transit.RewrappingRequest;
+import io.quarkus.vault.transit.SignVerifyOptions;
import io.quarkus.vault.transit.SigningInput;
import io.quarkus.vault.transit.SigningRequest;
import io.quarkus.vault.transit.TransitContext;
@@ -165,9 +166,22 @@ public interface VaultTransitSecretEngine {
*/
String sign(String keyName, SigningInput input, TransitContext transitContext);
+ /**
+ * Sign the input with the specified key and an optional explicit sign/verify options and an optional transit
+ * context used for key derivation, if applicable.
+ *
+ * @param keyName the signing key to use
+ * @param input data to sign
+ * @param options optional explicit sign/verify options
+ * @param transitContext optional transit context used for key derivation
+ * @return the signature
+ * @see sign data
+ */
+ String sign(String keyName, SigningInput input, SignVerifyOptions options, TransitContext transitContext);
+
/**
* Sign a list of inputs items. Each item shall specify the input to sign, an optional key version, and
- * an optional transit context used for ky derivation, if applicable.
+ * an optional transit context used for key derivation, if applicable.
* If any error occurs, the service will throw a {@link VaultSigningBatchException}
*
* @param keyName the signing key to use
@@ -177,6 +191,19 @@ public interface VaultTransitSecretEngine {
*/
Map sign(String keyName, List requests);
+ /**
+ * Sign a list of inputs items and an optional explicit sign/verify options. Each item shall specify the input to
+ * sign, an optional key version, and an optional transit context used for key derivation, if applicable.
+ * If any error occurs, the service will throw a {@link VaultSigningBatchException}
+ *
+ * @param keyName the signing key to use
+ * @param requests the list of inputs to sign
+ * @param options optional explicit sign/verify options
+ * @return a map of each request with its corresponding signature item
+ * @see sign data
+ */
+ Map sign(String keyName, List requests, SignVerifyOptions options);
+
/**
* Checks that the signature was obtained from signing the input with the specified key.
* The service will throw a {@link VaultException} if this is not the case.
@@ -200,11 +227,25 @@ public interface VaultTransitSecretEngine {
*/
void verifySignature(String keyName, String signature, SigningInput input, TransitContext transitContext);
+ /**
+ * Checks that the signature was obtained from signing the input with the specified key an an optional explicit
+ * sign/verify options.
+ * The service will throw a {@link VaultException} if this is not the case.
+ *
+ * @param keyName the key that was used to sign the input
+ * @param signature the signature obtained from one of the sign methods
+ * @param input the original input data
+ * @param options optional explicit sign/verify options
+ * @param transitContext optional transit context used for key derivation
+ * @see verify signed data
+ */
+ void verifySignature(String keyName, String signature, SigningInput input, SignVerifyOptions options,
+ TransitContext transitContext);
+
/**
* Checks a list of verification requests. Each request shall specify an input and the signature we want to match
- * against, and an optional transit context used for key derivation, if applicable.
- * If the signature does not match, or if any other error occurs,
- * the service will throw a {@link VaultVerificationBatchException}
+ * against, and an optional transit context used for key derivation, if applicable. If the signature does not
+ * match, or if any other error occurs, the service will throw a {@link VaultVerificationBatchException}
*
* @param keyName the key that was used to sign the input
* @param requests a list of items specifying an input and a signature to match against
@@ -212,6 +253,19 @@ public interface VaultTransitSecretEngine {
*/
void verifySignature(String keyName, List requests);
+ /**
+ * Checks a list of verification requests. Each request shall specify an input and the signature we want to match
+ * against, and an optional explicit sign/verify options and an optionals transit context used for key derivation,
+ * if applicable. If the signature does not match, or if any other error occurs, the service will throw a
+ * {@link VaultVerificationBatchException}
+ *
+ * @param keyName the key that was used to sign the input
+ * @param requests a list of items specifying an input and a signature to match against
+ * @param options optional explicit sign/verify options
+ * @see verify signed data
+ */
+ void verifySignature(String keyName, List requests, SignVerifyOptions options);
+
// --- admin operations
/**
diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultTransitManager.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultTransitManager.java
index 2d1c72054ef73..93a629dc8a061 100644
--- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultTransitManager.java
+++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultTransitManager.java
@@ -59,6 +59,7 @@
import io.quarkus.vault.transit.KeyConfigRequestDetail;
import io.quarkus.vault.transit.KeyCreationRequestDetail;
import io.quarkus.vault.transit.RewrappingRequest;
+import io.quarkus.vault.transit.SignVerifyOptions;
import io.quarkus.vault.transit.SigningInput;
import io.quarkus.vault.transit.SigningRequest;
import io.quarkus.vault.transit.TransitContext;
@@ -228,27 +229,37 @@ public String sign(String keyName, String input) {
@Override
public String sign(String keyName, SigningInput input, TransitContext transitContext) {
+ return sign(keyName, input, null, transitContext);
+ }
+
+ @Override
+ public String sign(String keyName, SigningInput input, SignVerifyOptions options, TransitContext transitContext) {
SigningRequest item = new SigningRequest(input, transitContext);
List pairs = singletonList(new SigningRequestResultPair(item));
- signBatch(keyName, NO_KEY_VERSION, pairs);
+ signBatch(keyName, NO_KEY_VERSION, pairs, options);
return pairs.get(0).getResult().getValueOrElseError();
}
@Override
public Map sign(String keyName, List requests) {
+ return sign(keyName, requests, null);
+ }
+
+ @Override
+ public Map sign(String keyName, List requests, SignVerifyOptions options) {
List pairs = requests.stream().map(SigningRequestResultPair::new).collect(toList());
pairs.stream()
.collect(groupingBy(SigningRequestResultPair::getKeyVersion))
- .forEach((keyVersion, subpairs) -> signBatch(keyName, keyVersion, subpairs));
+ .forEach((keyVersion, subpairs) -> signBatch(keyName, keyVersion, subpairs, options));
List results = pairs.stream().map(SigningRequestResultPair::getResult).collect(toList());
checkBatchErrors(results, errors -> new VaultSigningBatchException(errors + " signing errors", zip(requests, results)));
return zipRequestToValue(requests, results);
}
- private void signBatch(String keyName, int keyVersion, List pairs) {
+ private void signBatch(String keyName, int keyVersion, List pairs, SignVerifyOptions options) {
String hashAlgorithm = null;
@@ -267,6 +278,13 @@ private void signBatch(String keyName, int keyVersion, List requests) {
- List results = verifyBatch(keyName, requests);
+ verifySignature(keyName, requests, null);
+ }
+
+ @Override
+ public void verifySignature(String keyName, List requests, SignVerifyOptions options) {
+ List results = verifyBatch(keyName, requests, options);
Map resultMap = zip(requests, results);
checkBatchErrors(results, errors -> new VaultVerificationBatchException(errors + " verification errors", resultMap));
}
- private List verifyBatch(String keyName, List requests) {
+ private List verifyBatch(String keyName, List requests,
+ SignVerifyOptions options) {
String hashAlgorithm = null;
@@ -309,8 +339,15 @@ private List verifyBatch(String keyName, List Map zip(List keys, List values, Function f)
return map;
}
+ private T defaultIfNull(T value, T defaultValue) {
+ if (value != null)
+ return value;
+ return defaultValue;
+ }
+
}
diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/transit/SignVerifyOptions.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/transit/SignVerifyOptions.java
new file mode 100644
index 0000000000000..423e6fb611c4a
--- /dev/null
+++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/transit/SignVerifyOptions.java
@@ -0,0 +1,45 @@
+package io.quarkus.vault.transit;
+
+public class SignVerifyOptions {
+
+ private String signatureAlgorithm;
+ private String hashAlgorithm;
+ private Boolean prehashed;
+ private String marshalingAlgorithm;
+
+ public String getSignatureAlgorithm() {
+ return signatureAlgorithm;
+ }
+
+ public SignVerifyOptions setSignatureAlgorithm(String signatureAlgorithm) {
+ this.signatureAlgorithm = signatureAlgorithm;
+ return this;
+ }
+
+ public String getHashAlgorithm() {
+ return hashAlgorithm;
+ }
+
+ public SignVerifyOptions setHashAlgorithm(String hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ return this;
+ }
+
+ public Boolean getPrehashed() {
+ return prehashed;
+ }
+
+ public SignVerifyOptions setPrehashed(Boolean prehashed) {
+ this.prehashed = prehashed;
+ return this;
+ }
+
+ public String getMarshalingAlgorithm() {
+ return marshalingAlgorithm;
+ }
+
+ public SignVerifyOptions setMarshalingAlgorithm(String marshalingAlgorithm) {
+ this.marshalingAlgorithm = marshalingAlgorithm;
+ return this;
+ }
+}
diff --git a/integration-tests/vault/src/test/java/io/quarkus/vault/VaultTransitITCase.java b/integration-tests/vault/src/test/java/io/quarkus/vault/VaultTransitITCase.java
index 5816a4c7cf076..9b92b3a7f00d0 100644
--- a/integration-tests/vault/src/test/java/io/quarkus/vault/VaultTransitITCase.java
+++ b/integration-tests/vault/src/test/java/io/quarkus/vault/VaultTransitITCase.java
@@ -13,6 +13,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -38,6 +39,7 @@
import io.quarkus.vault.transit.KeyConfigRequestDetail;
import io.quarkus.vault.transit.KeyCreationRequestDetail;
import io.quarkus.vault.transit.RewrappingRequest;
+import io.quarkus.vault.transit.SignVerifyOptions;
import io.quarkus.vault.transit.SigningInput;
import io.quarkus.vault.transit.SigningRequest;
import io.quarkus.vault.transit.TransitContext;
@@ -128,6 +130,66 @@ public void signString() {
transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input, null);
}
+ @Test
+ public void signStringExplicitHashAlgorithmSha256() {
+ SignVerifyOptions options = new SignVerifyOptions().setHashAlgorithm("sha2-256");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input, options, null);
+ }
+
+ @Test
+ public void signStringExplicitHashAlgorithmSha512() {
+ SignVerifyOptions options = new SignVerifyOptions().setHashAlgorithm("sha2-512");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input, options, null);
+ }
+
+ @Test
+ public void signStringExplicitHashAlgorithmMismatched() {
+ SignVerifyOptions options = new SignVerifyOptions().setHashAlgorithm("sha2-256");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ assertThrows(VaultException.class,
+ () -> transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input,
+ options.setHashAlgorithm("sha1"), null));
+ }
+
+ @Test
+ public void signStringExplicitMarshalingAlgorithmASN1() {
+ SignVerifyOptions options = new SignVerifyOptions().setMarshalingAlgorithm("asn1");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input, options, null);
+ }
+
+ @Test
+ public void signStringExplicitMarshalingAlgorithmJWS() {
+ SignVerifyOptions options = new SignVerifyOptions().setMarshalingAlgorithm("jws");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input, options, null);
+ }
+
+ @Test
+ public void signStringExplicitMarshalingAlgorithmMismatched() {
+ SignVerifyOptions options = new SignVerifyOptions().setMarshalingAlgorithm("jws");
+ String signature = transitSecretEngine.sign(SIGN_KEY_NAME, input, options, null);
+ assertThrows(VaultException.class,
+ () -> transitSecretEngine.verifySignature(SIGN_KEY_NAME, signature, input,
+ options.setMarshalingAlgorithm("asn1"), null));
+ }
+
+ @Test
+ public void signStringExplicitSignatureAlgorithmPKCS1() {
+ SignVerifyOptions options = new SignVerifyOptions().setSignatureAlgorithm("pkcs1v15");
+ String signature = transitSecretEngine.sign(SIGN_KEY2_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY2_NAME, signature, input, options, null);
+ }
+
+ @Test
+ public void signStringExplicitSignatureAlgorithmPSS() {
+ SignVerifyOptions options = new SignVerifyOptions().setSignatureAlgorithm("pss");
+ String signature = transitSecretEngine.sign(SIGN_KEY2_NAME, input, options, null);
+ transitSecretEngine.verifySignature(SIGN_KEY2_NAME, signature, input, options, null);
+ }
+
@Test
public void signJws() {
String signature = transitSecretEngine.sign("jws", input, null);
diff --git a/test-framework/vault/src/main/java/io/quarkus/vault/test/VaultTestExtension.java b/test-framework/vault/src/main/java/io/quarkus/vault/test/VaultTestExtension.java
index 086a314a786f1..a9dfb4832e1e5 100644
--- a/test-framework/vault/src/main/java/io/quarkus/vault/test/VaultTestExtension.java
+++ b/test-framework/vault/src/main/java/io/quarkus/vault/test/VaultTestExtension.java
@@ -371,7 +371,7 @@ private void initVault() throws InterruptedException, IOException {
execVault(format("vault write -f transit/keys/%s", ENCRYPTION_KEY2_NAME));
execVault(format("vault write transit/keys/%s derived=true", ENCRYPTION_DERIVED_KEY_NAME));
execVault(format("vault write transit/keys/%s type=ecdsa-p256", SIGN_KEY_NAME));
- execVault(format("vault write transit/keys/%s type=ecdsa-p256", SIGN_KEY2_NAME));
+ execVault(format("vault write transit/keys/%s type=rsa-2048", SIGN_KEY2_NAME));
execVault(format("vault write transit/keys/%s type=ed25519 derived=true", SIGN_DERIVATION_KEY_NAME));
execVault("vault write transit/keys/jws type=ecdsa-p256");