summaryrefslogtreecommitdiffstats
path: root/hosted-api
diff options
context:
space:
mode:
authorJon Marius Venstad <jvenstad@yahoo-inc.com>2019-04-30 17:24:52 +0200
committerJon Marius Venstad <jvenstad@yahoo-inc.com>2019-04-30 17:24:52 +0200
commit2756848efb8588f7fdba30d6622d8bf9dbcbd6b3 (patch)
tree5591b40df4d7534f11f6421272091a5b4366449e /hosted-api
parentccd7db4d6888b4abff9a9e8c526f0c819ad8c01d (diff)
BCP for Signature, to handle ECDSA keys
Diffstat (limited to 'hosted-api')
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/RequestSigner.java31
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/RequestVerifier.java11
-rw-r--r--hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java38
-rw-r--r--hosted-api/src/test/java/ai/vespa/hosted/api/SignaturesTest.java8
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);