11package org .biscuitsec .biscuit .token ;
22
33import biscuit .format .schema .Schema .PublicKey .Algorithm ;
4- import net .i2p .crypto .eddsa .Utils ;
54import org .biscuitsec .biscuit .crypto .KeyPair ;
65import org .biscuitsec .biscuit .crypto .PublicKey ;
76import org .biscuitsec .biscuit .crypto .Signer ;
87import org .biscuitsec .biscuit .error .Error ;
98import org .bouncycastle .asn1 .ASN1InputStream ;
109import org .bouncycastle .asn1 .x509 .SubjectPublicKeyInfo ;
1110import org .bouncycastle .asn1 .x9 .ECNamedCurveTable ;
12- import org .junit .jupiter .api .AfterAll ;
13- import org .junit .jupiter .api .BeforeAll ;
11+ import org .junit .jupiter .api .BeforeEach ;
1412import org .junit .jupiter .api .Test ;
13+ import org .testcontainers .containers .localstack .LocalStackContainer ;
14+ import org .testcontainers .junit .jupiter .Container ;
15+ import org .testcontainers .junit .jupiter .Testcontainers ;
16+ import org .testcontainers .utility .DockerImageName ;
17+ import software .amazon .awssdk .auth .credentials .AwsBasicCredentials ;
18+ import software .amazon .awssdk .auth .credentials .StaticCredentialsProvider ;
1519import software .amazon .awssdk .core .SdkBytes ;
16- import software .amazon .awssdk .core . exception . SdkClientException ;
20+ import software .amazon .awssdk .regions . Region ;
1721import software .amazon .awssdk .services .kms .KmsClient ;
22+ import software .amazon .awssdk .services .kms .model .KeySpec ;
23+ import software .amazon .awssdk .services .kms .model .KeyUsageType ;
1824import software .amazon .awssdk .services .kms .model .SigningAlgorithmSpec ;
1925
2026import java .io .ByteArrayInputStream ;
2127import java .io .IOException ;
2228import java .security .InvalidKeyException ;
2329import java .security .NoSuchAlgorithmException ;
2430import java .security .SignatureException ;
25- import java .util .function .Function ;
2631
27- import static java .lang .ProcessBuilder .Redirect ;
2832import static org .junit .jupiter .api .Assertions .assertDoesNotThrow ;
29- import static org .junit .jupiter .api .Assertions .fail ;
30-
3133
34+ @ Testcontainers
3235public class KmsSignerExampleTest {
3336
34- private static final String AWS_PROFILE = null ;
35- private static final String AWS_REGION = null ;
36- private static final String KMS_KEY_ARN = null ;
37+ private static final DockerImageName LOCALSTACK_IMAGE = DockerImageName .parse ("localstack/localstack:3.0.1" );
3738
38- @ BeforeAll
39- public static void setup () {
40- System .setProperty ("aws.region" , AWS_REGION );
41- System .setProperty ("aws.profile" , AWS_PROFILE );
42- }
39+ @ Container
40+ public static LocalStackContainer LOCALSTACK = new LocalStackContainer (LOCALSTACK_IMAGE )
41+ .withServices (LocalStackContainer .Service .KMS );
4342
44- @ AfterAll
45- public static void teardown () {
46- System .clearProperty ("aws.region" );
47- System .clearProperty ("aws.profile" );
48- }
43+ private KmsClient kmsClient ;
44+ private String kmsKeyId ;
4945
50- // @Test
51- public void createWithRemoteSigner () throws Error {
52- Function < String , byte []> getPublicKeyBytes = ( keyId ) -> {
53- try ( var kmsClient = KmsClient .create ()) {
54- var publicKeyResponse = kmsClient . getPublicKey ( b -> b . keyId ( keyId ). build ());
55- return convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey ( publicKeyResponse . publicKey (). asByteArray ());
56- }
57- } ;
46+ @ BeforeEach
47+ public void setup () {
48+ var credentials = AwsBasicCredentials . create ( LOCALSTACK . getAccessKey (), LOCALSTACK . getSecretKey ());
49+ kmsClient = KmsClient .builder ()
50+ . endpointOverride ( LOCALSTACK . getEndpointOverride ( LocalStackContainer . Service . KMS ))
51+ . credentialsProvider ( StaticCredentialsProvider . create ( credentials ))
52+ . region ( Region . of ( LOCALSTACK . getRegion ()))
53+ . build () ;
5854
59- byte [] publicKeyBytes ;
60- try {
61- publicKeyBytes = getPublicKeyBytes . apply ( KMS_KEY_ARN );
62- } catch ( SdkClientException e ) {
63- sso ();
64- publicKeyBytes = getPublicKeyBytes . apply ( KMS_KEY_ARN );
65- }
55+ // ECC_NIST_P256 == SECP256R1
56+ kmsKeyId = kmsClient . createKey ( b -> b
57+ . keySpec ( KeySpec . ECC_NIST_P256 )
58+ . keyUsage ( KeyUsageType . SIGN_VERIFY )
59+ . build ()
60+ ). keyMetadata (). keyId ( );
61+ }
6662
67- var publicKey = new PublicKey (Algorithm .SECP256R1 , publicKeyBytes );
63+ @ Test
64+ public void testCreateBiscuitWithRemoteSigner () throws Error , NoSuchAlgorithmException , SignatureException , InvalidKeyException {
65+ var getPublicKeyResponse = kmsClient .getPublicKey (b -> b .keyId (kmsKeyId ).build ());
66+ var x509EncodedPublicKey = getPublicKeyResponse .publicKey ().asByteArray ();
67+ var sec1CompressedEncodedPublicKey = convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey (x509EncodedPublicKey );
68+ var publicKey = new PublicKey (Algorithm .SECP256R1 , sec1CompressedEncodedPublicKey );
6869 var keyPair = KeyPair .generate (publicKey , new Signer () {
69-
7070 @ Override
7171 public byte [] sign (byte [] bytes ) {
72- try (var kmsClient = KmsClient .create ()) {
73- var response = kmsClient .sign (builder -> builder
74- .keyId (KMS_KEY_ARN )
75- .signingAlgorithm (SigningAlgorithmSpec .ECDSA_SHA_256 )
76- .message (SdkBytes .fromByteArray (bytes ))
77- );
78- var signature = response .signature ().asByteArray ();
79- var hex = Utils .bytesToHex (signature );
80- System .out .println ("Signature: " + hex );
81-
82- var sgr = KeyPair .generateSignature (Algorithm .SECP256R1 );
83- sgr .initVerify (publicKey .key );
84- sgr .update (bytes );
85- var verified = sgr .verify (signature );
86- System .out .println ("Verified: " + verified );
87-
88- if (!verified ) {
89- fail ("Signature verification failed" );
90- }
91-
92- return signature ;
93- } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e ) {
94- throw new RuntimeException (e );
95- }
72+ var signResponse = kmsClient .sign (b -> b
73+ .keyId (kmsKeyId )
74+ .signingAlgorithm (SigningAlgorithmSpec .ECDSA_SHA_256 )
75+ .message (SdkBytes .fromByteArray (bytes ))
76+ );
77+ return signResponse .signature ().asByteArray ();
9678 }
9779 });
98-
9980 var biscuit = Biscuit .builder (keyPair )
10081 .add_authority_fact ("user(\" 1234\" )" )
10182 .add_authority_check ("check if operation(\" read\" )" )
10283 .build ();
103- assertDoesNotThrow (biscuit ::serialize );
84+ var serializedBiscuit = assertDoesNotThrow (biscuit ::serialize );
85+ var unverifiedBiscuit = Biscuit .from_bytes (serializedBiscuit );
86+ var deserializedBiscuit = unverifiedBiscuit .verify (publicKey );
87+
88+ System .out .println (deserializedBiscuit .print ());
10489 }
10590
106- static byte [] convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey (byte [] publicKeyBytes ) {
91+ private static byte [] convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey (byte [] publicKeyBytes ) {
10792 try (ASN1InputStream asn1InputStream = new ASN1InputStream (new ByteArrayInputStream (publicKeyBytes ))) {
10893
10994 // Parse the ASN.1 encoded public key bytes
@@ -122,20 +107,4 @@ static byte[] convertDEREncodedX509PublicKeyToSEC1CompressedEncodedPublicKey(byt
122107 throw new RuntimeException ("Error converting DER-encoded X.509 to SEC1 compressed format" , e );
123108 }
124109 }
125-
126- private void sso () {
127- try {
128- var code = new ProcessBuilder ()
129- .command ("aws" , "sso" , "login" , "--profile" , AWS_PROFILE )
130- .redirectOutput (Redirect .INHERIT )
131- .redirectError (Redirect .INHERIT )
132- .start ()
133- .waitFor ();
134- if (code != 0 ) {
135- throw new RuntimeException ("SSO login failed" );
136- }
137- } catch (InterruptedException | IOException e ) {
138- throw new RuntimeException (e );
139- }
140- }
141110}
0 commit comments