diff --git a/pom.xml b/pom.xml
index 1ac40a5..d4cfe1b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -266,6 +266,24 @@
junit-jupiter
test
+
+ software.amazon.awssdk
+ kms
+ 2.25.65
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ 1.19.8
+ test
+
+
+ org.testcontainers
+ localstack
+ 1.19.8
+ test
+
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
index 22857f7..8ef0d98 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Ed25519KeyPair.java
@@ -1,6 +1,6 @@
package org.biscuitsec.biscuit.crypto;
-import biscuit.format.schema.Schema;
+import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
@@ -10,42 +10,47 @@
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.biscuitsec.biscuit.token.builder.Utils;
+import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Optional;
class Ed25519KeyPair extends KeyPair {
private final EdDSAPrivateKey privateKey;
private final EdDSAPublicKey publicKey;
+ private final Signer signer;
- private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
+ private static final EdDSANamedCurveSpec CURVE_SPEC = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
public Ed25519KeyPair(byte[] bytes) {
- EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, ed25519);
+ EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(bytes, CURVE_SPEC);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
- EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
+ EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), CURVE_SPEC);
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
this.privateKey = privKey;
this.publicKey = pubKey;
+ this.signer = new PrivateKeySigner(Algorithm.Ed25519, privKey);
}
public Ed25519KeyPair(SecureRandom rng) {
byte[] b = new byte[32];
rng.nextBytes(b);
- EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, ed25519);
+ EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(b, CURVE_SPEC);
EdDSAPrivateKey privKey = new EdDSAPrivateKey(privKeySpec);
- EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), ed25519);
+ EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKey.getA(), CURVE_SPEC);
EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec);
this.privateKey = privKey;
this.publicKey = pubKey;
+ this.signer = new PrivateKeySigner(Algorithm.Ed25519, privKey);
}
public Ed25519KeyPair(String hex) {
@@ -53,11 +58,11 @@ public Ed25519KeyPair(String hex) {
}
public static java.security.PublicKey decode(byte[] data) {
- return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, ed25519));
+ return new EdDSAPublicKey(new EdDSAPublicKeySpec(data, CURVE_SPEC));
}
public static Signature getSignature() throws NoSuchAlgorithmException {
- return new EdDSAEngine(MessageDigest.getInstance(ed25519.getHashAlgorithm()));
+ return new EdDSAEngine(MessageDigest.getInstance(CURVE_SPEC.getHashAlgorithm()));
}
@Override
@@ -76,12 +81,22 @@ public java.security.PublicKey publicKey() {
}
@Override
- public PrivateKey private_key() {
- return privateKey;
+ public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signStandard(block, Algorithm.Ed25519, publicKey);
+ }
+
+ @Override
+ public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signExternal(block, Algorithm.Ed25519, publicKey, external);
+ }
+
+ @Override
+ public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signSealed(block, Algorithm.Ed25519, publicKey, seal);
}
@Override
public PublicKey public_key() {
- return new PublicKey(Schema.PublicKey.Algorithm.Ed25519, this.publicKey);
+ return new PublicKey(Algorithm.Ed25519, this.publicKey);
}
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
index 9014271..b2f812a 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/KeyPair.java
@@ -4,9 +4,14 @@
import biscuit.format.schema.Schema.PublicKey.Algorithm;
import net.i2p.crypto.eddsa.Utils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Optional;
/**
* Private and public key.
@@ -41,6 +46,10 @@ public static KeyPair generate(Algorithm algorithm, SecureRandom rng) {
}
}
+ public static KeyPair generate(PublicKey publicKey, Signer signer) {
+ return new RemoteKeyPair(publicKey, signer);
+ }
+
public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgorithmException {
if (algorithm == Algorithm.Ed25519) {
return Ed25519KeyPair.getSignature();
@@ -57,7 +66,11 @@ public static Signature generateSignature(Algorithm algorithm) throws NoSuchAlgo
public abstract java.security.PublicKey publicKey();
- public abstract java.security.PrivateKey private_key();
-
public abstract PublicKey public_key();
+
+ public abstract byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;
+
+ public abstract byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;
+
+ public abstract byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException;
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/PrivateKeySigner.java b/src/main/java/org/biscuitsec/biscuit/crypto/PrivateKeySigner.java
new file mode 100644
index 0000000..3278ab1
--- /dev/null
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/PrivateKeySigner.java
@@ -0,0 +1,27 @@
+package org.biscuitsec.biscuit.crypto;
+
+import biscuit.format.schema.Schema.PublicKey.Algorithm;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.SignatureException;
+
+public class PrivateKeySigner implements Signer {
+
+ private final Algorithm algorithm;
+ private final PrivateKey privateKey;
+
+ public PrivateKeySigner(Algorithm algorithm, PrivateKey privateKey) {
+ this.algorithm = algorithm;
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public byte[] sign(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ var sgr = KeyPair.generateSignature(algorithm);
+ sgr.initSign(privateKey);
+ sgr.update(bytes);
+ return sgr.sign();
+ }
+}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/RemoteKeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/RemoteKeyPair.java
new file mode 100644
index 0000000..19438f7
--- /dev/null
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/RemoteKeyPair.java
@@ -0,0 +1,53 @@
+package org.biscuitsec.biscuit.crypto;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.util.Optional;
+
+public class RemoteKeyPair extends KeyPair {
+
+ private final org.biscuitsec.biscuit.crypto.PublicKey publicKey;
+ private final Signer signer;
+
+ public RemoteKeyPair(org.biscuitsec.biscuit.crypto.PublicKey publicKey, Signer signer) {
+ this.publicKey = publicKey;
+ this.signer = signer;
+ }
+
+ @Override
+ public byte[] toBytes() {
+ throw new RuntimeException("Illegal operation; remote private private key cannot be accessed.");
+ }
+
+ @Override
+ public String toHex() {
+ throw new RuntimeException("Illegal operation; remote private private key cannot be accessed.");
+ }
+
+ @Override
+ public PublicKey publicKey() {
+ return publicKey.key;
+ }
+
+ @Override
+ public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signStandard(block, this.publicKey.algorithm, publicKey);
+ }
+
+ @Override
+ public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signExternal(block, this.publicKey.algorithm, publicKey, external);
+ }
+
+ @Override
+ public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signSealed(block, this.publicKey.algorithm, publicKey, seal);
+ }
+
+ @Override
+ public org.biscuitsec.biscuit.crypto.PublicKey public_key() {
+ return publicKey;
+ }
+}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
index 8bbeb1c..34591df 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/SECP256R1KeyPair.java
@@ -1,6 +1,6 @@
package org.biscuitsec.biscuit.crypto;
-import biscuit.format.schema.Schema;
+import biscuit.format.schema.Schema.PublicKey.Algorithm;
import org.biscuitsec.biscuit.token.builder.Utils;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
@@ -11,48 +11,48 @@
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.BigIntegers;
+import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
import java.security.SecureRandom;
-import java.security.Security;
import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Optional;
class SECP256R1KeyPair extends KeyPair {
private final BCECPrivateKey privateKey;
private final BCECPublicKey publicKey;
+ private final Signer signer;
private static final String ALGORITHM = "ECDSA";
private static final String CURVE = "secp256r1";
- private static final ECNamedCurveParameterSpec SECP256R1 = ECNamedCurveTable.getParameterSpec(CURVE);
-
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
+ private static final ECNamedCurveParameterSpec CURVE_SPEC = ECNamedCurveTable.getParameterSpec(CURVE);
public SECP256R1KeyPair(byte[] bytes) {
- var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1);
+ var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), CURVE_SPEC);
var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);
- var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1);
+ var publicKeySpec = new ECPublicKeySpec(CURVE_SPEC.getG().multiply(privateKeySpec.getD()), CURVE_SPEC);
var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION);
this.privateKey = privateKey;
this.publicKey = publicKey;
+ this.signer = new PrivateKeySigner(Algorithm.SECP256R1, privateKey);
}
public SECP256R1KeyPair(SecureRandom rng) {
byte[] bytes = new byte[32];
rng.nextBytes(bytes);
- var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), SECP256R1);
+ var privateKeySpec = new ECPrivateKeySpec(BigIntegers.fromUnsignedByteArray(bytes), CURVE_SPEC);
var privateKey = new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);
- var publicKeySpec = new ECPublicKeySpec(SECP256R1.getG().multiply(privateKeySpec.getD()), SECP256R1);
+ var publicKeySpec = new ECPublicKeySpec(CURVE_SPEC.getG().multiply(privateKeySpec.getD()), CURVE_SPEC);
var publicKey = new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION);
this.privateKey = privateKey;
this.publicKey = publicKey;
+ this.signer = new PrivateKeySigner(Algorithm.SECP256R1, privateKey);
}
public SECP256R1KeyPair(String hex) {
@@ -85,12 +85,22 @@ public java.security.PublicKey publicKey() {
}
@Override
- public PrivateKey private_key() {
- return privateKey;
+ public byte[] sign(byte[] block, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signStandard(block, Algorithm.SECP256R1, publicKey);
+ }
+
+ @Override
+ public byte[] signExternal(byte[] block, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signExternal(block, Algorithm.SECP256R1, publicKey, external);
+ }
+
+ @Override
+ public byte[] signSealed(byte[] block, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ return signer.signSealed(block, Algorithm.SECP256R1, publicKey, seal);
}
@Override
public PublicKey public_key() {
- return new PublicKey(Schema.PublicKey.Algorithm.SECP256R1, publicKey);
+ return new PublicKey(Algorithm.SECP256R1, publicKey);
}
}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java b/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java
new file mode 100644
index 0000000..7c6cd55
--- /dev/null
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Signer.java
@@ -0,0 +1,53 @@
+package org.biscuitsec.biscuit.crypto;
+
+import biscuit.format.schema.Schema;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+import java.util.Arrays;
+
+public interface Signer {
+ byte[] sign(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException;
+
+ // format: block, algo, publicKey
+ default byte[] signStandard(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ var algorithmBytes = getBufferForAlgorithm(algorithm);
+ var payload = concatenateArrays(block, algorithmBytes, publicKey);
+ return sign(payload);
+ }
+
+ // format: block, algo, publicKey, seal
+ default byte[] signSealed(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey, byte[] seal) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ var algorithmBytes = getBufferForAlgorithm(algorithm);
+ var payload = concatenateArrays(block, algorithmBytes, publicKey, seal);
+ return sign(payload);
+ }
+
+ // format: block, external, algo, publicKey
+ default byte[] signExternal(byte[] block, Schema.PublicKey.Algorithm algorithm, byte[] publicKey, byte[] external) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
+ var algorithmBytes = getBufferForAlgorithm(algorithm);
+ var payload = concatenateArrays(block, external, algorithmBytes, publicKey);
+ return sign(payload);
+ }
+
+ private static byte[] concatenateArrays(byte[]... arrays) {
+ int totalLength = Arrays.stream(arrays).mapToInt(arr -> arr.length).sum();
+ byte[] result = new byte[totalLength];
+ int currentPos = 0;
+ for (byte[] array : arrays) {
+ System.arraycopy(array, 0, result, currentPos, array.length);
+ currentPos += array.length;
+ }
+ return result;
+ }
+
+ private static byte[] getBufferForAlgorithm(Schema.PublicKey.Algorithm algorithm) {
+ var algorithmBuffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
+ algorithmBuffer.putInt(algorithm.getNumber());
+ algorithmBuffer.flip();
+ return algorithmBuffer.array();
+ }
+}
diff --git a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
index 18ed8ca..0513a3a 100644
--- a/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
+++ b/src/main/java/org/biscuitsec/biscuit/crypto/Token.java
@@ -18,17 +18,7 @@ class Token {
public final KeyPair next;
public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
- Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
- algo_buf.flip();
- sgr.initSign(rootKeyPair.private_key());
- sgr.update(message);
- sgr.update(algo_buf);
- sgr.update(next.public_key().toBytes());
-
- byte[] signature = sgr.sign();
-
+ byte[] signature = rootKeyPair.sign(message, next.public_key().toBytes());
this.blocks = new ArrayList<>();
this.blocks.add(message);
this.keys = new ArrayList<>();
@@ -38,8 +28,7 @@ public Token(KeyPair rootKeyPair, byte[] message, KeyPair next) throws NoSuchAlg
this.next = next;
}
- public Token(final ArrayList blocks, final ArrayList keys, final ArrayList signatures,
- final KeyPair next) {
+ public Token(final ArrayList blocks, final ArrayList keys, final ArrayList signatures, final KeyPair next) {
this.signatures = signatures;
this.blocks = blocks;
this.keys = keys;
@@ -47,17 +36,7 @@ public Token(final ArrayList blocks, final ArrayList keys, fi
}
public Token append(KeyPair keyPair, byte[] message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
- Signature sgr = KeyPair.generateSignature(next.public_key().algorithm);
- sgr.initSign(this.next.private_key());
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next.public_key().algorithm.getNumber()));
- algo_buf.flip();
- sgr.update(message);
- sgr.update(algo_buf);
- sgr.update(keyPair.public_key().toBytes());
-
- byte[] signature = sgr.sign();
-
+ byte[] signature = next.sign(message, keyPair.public_key().toBytes());
Token token = new Token(this.blocks, this.keys, this.signatures, keyPair);
token.blocks.add(message);
token.signatures.add(signature);
diff --git a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
index 510f62c..00b1831 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/ThirdPartyBlockRequest.java
@@ -45,16 +45,7 @@ public Either createBlock(KeyPair ke
byte[] serializedBlock = res.get();
- Signature sgr = KeyPair.generateSignature(keyPair.public_key().algorithm);
- sgr.initSign(keyPair.private_key());
- sgr.update(serializedBlock);
-
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(Schema.PublicKey.Algorithm.Ed25519.getNumber()));
- algo_buf.flip();
- sgr.update(algo_buf);
- sgr.update(previousKey.toBytes());
- byte[] signature = sgr.sign();
+ byte[] signature = keyPair.sign(serializedBlock, previousKey.toBytes());
PublicKey publicKey = keyPair.public_key();
diff --git a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
index e1b8d3c..9317b9b 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/UnverifiedBiscuit.java
@@ -256,14 +256,14 @@ public ThirdPartyBlockRequest thirdPartyRequest() {
*/
public UnverifiedBiscuit appendThirdPartyBlock(PublicKey externalKey, ThirdPartyBlockContents blockResponse)
throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Error {
- KeyPair nextKeyPair = KeyPair.generate(Schema.PublicKey.Algorithm.Ed25519);
+ KeyPair nextKeyPair = KeyPair.generate(externalKey.algorithm);
Signature sgr = KeyPair.generateSignature(externalKey.algorithm);
sgr.initVerify(externalKey.key);
sgr.update(blockResponse.payload);
ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(Schema.PublicKey.Algorithm.Ed25519.getNumber()));
+ algo_buf.putInt(Integer.valueOf(externalKey.algorithm.getNumber()));
algo_buf.flip();
sgr.update(algo_buf);
diff --git a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
index d2df062..1dc6f7b 100644
--- a/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
+++ b/src/main/java/org/biscuitsec/biscuit/token/format/SerializedBiscuit.java
@@ -237,17 +237,8 @@ static public Either make(final org.biscui
b.writeTo(stream);
byte[] block = stream.toByteArray();
org.biscuitsec.biscuit.crypto.PublicKey next_key = next.public_key();
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber()));
- algo_buf.flip();
-
- Signature sgr = KeyPair.generateSignature(root.public_key().algorithm);
- sgr.initSign(root.private_key());
- sgr.update(block);
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
- byte[] signature = sgr.sign();
+ byte[] signature = root.sign(block, next_key.toBytes());
SignedBlock signedBlock = new SignedBlock(block, next_key, signature, Option.none());
Proof proof = new Proof(next);
@@ -270,20 +261,14 @@ public Either append(final org.biscuitsec.
byte[] block = stream.toByteArray();
org.biscuitsec.biscuit.crypto.PublicKey next_key = next.public_key();
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(next_key.algorithm.getNumber()));
- algo_buf.flip();
- Signature sgr = KeyPair.generateSignature(next_key.algorithm);
- sgr.initSign(this.proof.secretKey.get().private_key());
- sgr.update(block);
- if(externalSignature.isDefined()) {
- sgr.update(externalSignature.get().signature);
+ byte[] signature;
+ if (externalSignature.isDefined()) {
+ signature = proof.secretKey.get().signExternal(block, next_key.toBytes(), externalSignature.get().signature);
+ } else {
+ signature = proof.secretKey.get().sign(block, next_key.toBytes());
}
- sgr.update(algo_buf);
- sgr.update(next_key.toBytes());
- byte[] signature = sgr.sign();
-
+
SignedBlock signedBlock = new SignedBlock(block, next_key, signature, externalSignature);
ArrayList blocks = new ArrayList<>();
@@ -496,19 +481,7 @@ public Either seal() throws InvalidKeyException, NoSuchAlgorithmExc
block = this.blocks.get(this.blocks.size() - 1);
}
- Signature sgr = KeyPair.generateSignature(block.key.algorithm);
- ByteBuffer algo_buf = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
- algo_buf.putInt(Integer.valueOf(block.key.algorithm.getNumber()));
- algo_buf.flip();
-
-
- sgr.initSign(this.proof.secretKey.get().private_key());
- sgr.update(block.block);
- sgr.update(algo_buf);
- sgr.update(block.key.toBytes());
- sgr.update(block.signature);
-
- byte[] signature = sgr.sign();
+ byte[] signature = proof.secretKey.get().signSealed(block.block, block.key.toBytes(), block.signature);
this.proof.secretKey = Option.none();
this.proof.signature = Option.some(signature);
diff --git a/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java b/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java
new file mode 100644
index 0000000..f2d0f5b
--- /dev/null
+++ b/src/test/java/org/biscuitsec/biscuit/token/KmsSignerExampleTest.java
@@ -0,0 +1,110 @@
+package org.biscuitsec.biscuit.token;
+
+import biscuit.format.schema.Schema.PublicKey.Algorithm;
+import org.biscuitsec.biscuit.crypto.KeyPair;
+import org.biscuitsec.biscuit.crypto.PublicKey;
+import org.biscuitsec.biscuit.crypto.Signer;
+import org.biscuitsec.biscuit.error.Error;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.containers.localstack.LocalStackContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.SdkBytes;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.kms.KmsClient;
+import software.amazon.awssdk.services.kms.model.KeySpec;
+import software.amazon.awssdk.services.kms.model.KeyUsageType;
+import software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+@Testcontainers
+public class KmsSignerExampleTest {
+
+ private static final DockerImageName LOCALSTACK_IMAGE = DockerImageName.parse("localstack/localstack:3.0.1");
+
+ @Container
+ public static LocalStackContainer LOCALSTACK = new LocalStackContainer(LOCALSTACK_IMAGE)
+ .withServices(LocalStackContainer.Service.KMS);
+
+ private KmsClient kmsClient;
+ private String kmsKeyId;
+
+ @BeforeEach
+ public void setup() {
+ var credentials = AwsBasicCredentials.create(LOCALSTACK.getAccessKey(), LOCALSTACK.getSecretKey());
+ kmsClient = KmsClient.builder()
+ .endpointOverride(LOCALSTACK.getEndpointOverride(LocalStackContainer.Service.KMS))
+ .credentialsProvider(StaticCredentialsProvider.create(credentials))
+ .region(Region.of(LOCALSTACK.getRegion()))
+ .build();
+
+ // ECC_NIST_P256 == SECP256R1
+ kmsKeyId = kmsClient.createKey(b -> b
+ .keySpec(KeySpec.ECC_NIST_P256)
+ .keyUsage(KeyUsageType.SIGN_VERIFY)
+ .build()
+ ).keyMetadata().keyId();
+ }
+
+ @Test
+ public void testCreateBiscuitWithRemoteSigner() throws Error {
+ var getPublicKeyResponse = kmsClient.getPublicKey(b -> b.keyId(kmsKeyId).build());
+ var x509EncodedPublicKey = getPublicKeyResponse.publicKey().asByteArray();
+ var sec1CompressedEncodedPublicKey = convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey(x509EncodedPublicKey);
+ var publicKey = new PublicKey(Algorithm.SECP256R1, sec1CompressedEncodedPublicKey);
+ var keyPair = KeyPair.generate(publicKey, new Signer() {
+ @Override
+ public byte[] sign(byte[] bytes) {
+ var signResponse = kmsClient.sign(b -> b
+ .keyId(kmsKeyId)
+ .signingAlgorithm(SigningAlgorithmSpec.ECDSA_SHA_256)
+ .message(SdkBytes.fromByteArray(bytes))
+ );
+ return signResponse.signature().asByteArray();
+ }
+ });
+ var biscuit = Biscuit.builder(keyPair)
+ .add_authority_fact("user(\"1234\")")
+ .add_authority_check("check if operation(\"read\")")
+ .build();
+ var serializedBiscuit = biscuit.serialize();
+ var deserializedUnverifiedBiscuit = Biscuit.from_bytes(serializedBiscuit);
+ var verifiedBiscuit = assertDoesNotThrow(() -> deserializedUnverifiedBiscuit.verify(publicKey));
+
+ System.out.println(verifiedBiscuit.print());
+ }
+
+ private static byte[] convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey(byte[] publicKeyBytes) {
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(publicKeyBytes))) {
+
+ // Parse the ASN.1 encoded public key bytes
+ var asn1Primitive = asn1InputStream.readObject();
+ var subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(asn1Primitive);
+
+ // Extract the public key data
+ var publicKeyDataBitString = subjectPublicKeyInfo.getPublicKeyData();
+ byte[] publicKeyData = publicKeyDataBitString.getBytes();
+
+ // Parse the public key data to get the elliptic curve point
+ var ecParameters = ECNamedCurveTable.getByName("secp256r1");
+ var ecPoint = ecParameters.getCurve().decodePoint(publicKeyData);
+ return ecPoint.getEncoded(true);
+ } catch (IOException e) {
+ throw new RuntimeException("Error converting DER-encoded X.509 to SEC1 compressed format", e);
+ }
+ }
+}