diff options
Diffstat (limited to 'hosted-api/src')
4 files changed, 27 insertions, 61 deletions
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/RequestSigner.java b/hosted-api/src/main/java/ai/vespa/hosted/api/RequestSigner.java index 4dc2823ed3b..fb8eb1421b4 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/RequestSigner.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/RequestSigner.java @@ -8,6 +8,7 @@ import java.net.http.HttpRequest; import java.security.Key; import java.security.PrivateKey; import java.security.Signature; +import java.security.SignatureException; import java.time.Clock; import java.util.Base64; import java.util.function.Supplier; @@ -21,7 +22,7 @@ import static ai.vespa.hosted.api.Signatures.sha256Digest; */ public class RequestSigner { - private final PrivateKey privateKey; + private final Signature signer; private final String keyId; private final Clock clock; @@ -32,7 +33,7 @@ public class RequestSigner { /** Creates a new request signer with a custom clock. */ RequestSigner(String pemPrivateKey, String keyId, Clock clock) { - this.privateKey = KeyUtils.fromPemEncodedPrivateKey(pemPrivateKey); + this.signer = KeyUtils.createSigner(KeyUtils.fromPemEncodedPrivateKey(pemPrivateKey)); this.keyId = keyId; this.clock = clock; } @@ -48,18 +49,24 @@ public class RequestSigner { * added to the request as another header. */ public HttpRequest signed(HttpRequest.Builder request, Method method, Supplier<InputStream> data) { - String timestamp = clock.instant().toString(); - String contentHash = Base64.getEncoder().encodeToString(sha256Digest(data::get)); - byte[] canonicalMessage = Signatures.canonicalMessageOf(method.name(), request.copy().build().uri(), timestamp, contentHash); - String signature = Base64.getEncoder().encodeToString(Signatures.signed(canonicalMessage, privateKey)); + try { + String timestamp = clock.instant().toString(); + String contentHash = Base64.getEncoder().encodeToString(sha256Digest(data::get)); + byte[] canonicalMessage = Signatures.canonicalMessageOf(method.name(), request.copy().build().uri(), timestamp, contentHash); + signer.update(canonicalMessage); + String signature = Base64.getEncoder().encodeToString(signer.sign()); - request.setHeader("X-Timestamp", timestamp); - request.setHeader("X-Content-Hash", contentHash); - request.setHeader("X-Key-Id", keyId); - request.setHeader("X-Authorization", signature); + request.setHeader("X-Timestamp", timestamp); + request.setHeader("X-Content-Hash", contentHash); + request.setHeader("X-Key-Id", keyId); + request.setHeader("X-Authorization", signature); - request.method(method.name(), HttpRequest.BodyPublishers.ofInputStream(data)); - return request.build(); + request.method(method.name(), HttpRequest.BodyPublishers.ofInputStream(data)); + return request.build(); + } + catch (SignatureException e) { + throw new IllegalArgumentException(e); + } } /** diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/RequestVerifier.java b/hosted-api/src/main/java/ai/vespa/hosted/api/RequestVerifier.java index 3e1224e29d0..a46a93f624e 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/RequestVerifier.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/RequestVerifier.java @@ -5,6 +5,8 @@ import com.yahoo.security.KeyUtils; import java.net.URI; import java.security.Key; import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; import java.time.Clock; import java.time.Duration; import java.time.Instant; @@ -18,7 +20,7 @@ import java.util.Base64; */ public class RequestVerifier { - private final PublicKey publicKey; + private final Signature verifier; private final Clock clock; public RequestVerifier(String pemPublicKey) { @@ -26,7 +28,7 @@ public class RequestVerifier { } RequestVerifier(String pemPublicKey, Clock clock) { - this.publicKey = KeyUtils.fromPemEncodedPublicKey(pemPublicKey); + this.verifier = KeyUtils.createVerifier(KeyUtils.fromPemEncodedPublicKey(pemPublicKey)); this.clock = clock; } @@ -38,9 +40,10 @@ public class RequestVerifier { return false; // Timestamp mismatch between sender and receiver of more than 5 minutes is not acceptable. byte[] canonicalMessage = Signatures.canonicalMessageOf(method.name(), requestUri, timestamp, contentHash); - return Signatures.verify(canonicalMessage, Base64.getDecoder().decode(signature), publicKey); + verifier.update(canonicalMessage); + return verifier.verify(Base64.getDecoder().decode(signature)); } - catch (RuntimeException e) { + catch (RuntimeException | SignatureException e) { return false; } } diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java index 6872fc264a1..c93d5fc9168 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java @@ -1,6 +1,6 @@ package ai.vespa.hosted.api; -import com.yahoo.security.SignatureAlgorithm; +import com.yahoo.security.KeyUtils; import java.io.InputStream; import java.net.URI; @@ -49,40 +49,4 @@ public class Signatures { return (method + "\n" + requestUri.normalize() + "\n" + timestamp + "\n" + hash).getBytes(UTF_8); } - /** Returns the signature of the given content, with the given private key. */ - public static byte[] signed(byte[] content, PrivateKey key) { - Signature signer = createSigner(); - try { - signer.initSign(key); - signer.update(content); - return signer.sign(); - } - catch (InvalidKeyException | SignatureException e) { - throw new IllegalArgumentException(e); - } - } - - /** Returns whether the given public key verifies the given signature for the given content. */ - public static boolean verify(byte[] content, byte[] signature, PublicKey key) { - Signature signer = createSigner(); - try { - signer.initVerify(key); - signer.update(content); - return signer.verify(signature); - } - catch (InvalidKeyException | SignatureException e) { - throw new IllegalArgumentException(e); - } - } - - /** Returns a signature instance which computes a SHA-256 hash of its content, before signing / verifying. */ - private static Signature createSigner() { - try { - return Signature.getInstance(SignatureAlgorithm.SHA256_WITH_ECDSA.getAlgorithmName()); - } - catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(e); - } - } - } diff --git a/hosted-api/src/test/java/ai/vespa/hosted/api/SignaturesTest.java b/hosted-api/src/test/java/ai/vespa/hosted/api/SignaturesTest.java index 212b2bdebac..23d29dbeb55 100644 --- a/hosted-api/src/test/java/ai/vespa/hosted/api/SignaturesTest.java +++ b/hosted-api/src/test/java/ai/vespa/hosted/api/SignaturesTest.java @@ -1,7 +1,5 @@ package ai.vespa.hosted.api; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.BeforeClass; import org.junit.Test; import java.io.ByteArrayInputStream; @@ -9,7 +7,6 @@ import java.net.URI; import java.net.http.HttpRequest; import java.security.DigestInputStream; import java.security.MessageDigest; -import java.security.Security; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; @@ -58,11 +55,6 @@ public class SignaturesTest { "Yours truly,\n" + "∠( ᐛ 」∠)_").getBytes(UTF_8); - @BeforeClass - public static void register() { - Security.addProvider(new BouncyCastleProvider()); - } - @Test public void testHashing() throws Exception { byte[] hash1 = MessageDigest.getInstance("SHA-256").digest(message); |