Google Cloud KMS provider for Nimbus JOSE
This library provides JWS utilities for Nimbus JOSE on top of Google Cloud KMS: You can sign and verify JWS objects backed by keys in GCP KMS.
- The current version is: 1.0.1
 
Maven
<dependency>
  <groupId>io.github.fungrim.nimbus</groupId>
  <artifactId>gcp-kms-nimbus-provider</artifactId>
  <version>1.0.1</version>
</dependency>
Gradle
implementation 'io.github.fungrim.nimbus:gcp-kms-nimbus-provider:1.0.1'
Prerequisites
You need to configure the GCP application credentials, e.g.:
export GOOGLE_APPLICATION_CREDENTIALS=my-sa.json
Create a key ring if you don’t already have one, e.g.:
gcloud kms keyrings create jws-keys --location=us-east1
Create at least one key to use, e.g.:
gcloud kms keys create jwd-ec-1 \
   --location=us-east1 \
   --keyring=jws-keys \
   --purpose=asymmetric-signing \
   --default-algorithm=ec-sign-p256-sha256
Make sure you, or the SA you’re using, have rights to use the keys, for example via the role roles/cloudkms.signerVerifier
TL;DR
try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // you need the resource ID of the key ring to use
    String keyRingResourceName = "projects/you-project/locations/europe/keyRings/your-keyring";
    
    // the key handle factory is your key access point, and caches keys in memory for you
    KmsKeyHandleFactory factory = KmsKeyHandleFactory.builder(client, KeyRingName.parse(keyRingResourceName))
                    .withKeyCacheDuration(Duration.ofSeconds(60))
                    .build();
    // there's several ways of getting hold of a key, for example
    // by a key version resource name, but below we don't care and ask
    // for a key by algorithm - the provider will pick the a matching key and
    // the latest version
    KmsKeyHandle handle = factory.find(JWSAlgorithm.ES256);
    // create claims
    JWTClaimsSet claims = new JWTClaimsSet.Builder()
            .subject("bob")
            .issuer("https://www.google.com")
            .expirationTime(new Date(LocalDateTime.now().plusHours(24).toInstant(ZoneOffset.UTC).toEpochMilli()))
            .build();
    // let the handle create the header, this will set the algorithm and key ID automagically
    JWSHeader header = handle.createHeaderBuilder().build();
    // create and sign 
    SignedJWT jwt = new SignedJWT(header, claims);
    jwt.sign(handler.getSigner());
    // verify
    String token = jwt.serialize();
    SignedJWT parsed = SignedJWT.parse(token);
    if(!partsed.verify(handler.getVerifier())) {
        System.out.println("Help! Help! I'm being repressed!");
    }
}
Algorithm support
All keys for asymmetric signing in GCP KMS are supported - with the exception of secp256k1, see below - as well as HMAC signing.
- EC P256 / SHA256
 - EC P384 / SHA384
 - EC secp256k1 / SHA256 (see below)
 - RSA 2048 PKCS#1 / SHA256
 - RSA 3072 PKCS#1 / SHA256
 - RSA 4096 PKCS#1 / SHA256
 - RSA 4096 PKCS#1 / SHA512
 - RSA 2048 PSS / SHA256
 - RSA 3072 PSS / SHA256
 - RSA 4096 PSS / SHA256
 - RSA 4096 PSS / SHA512
 - HMAC / SHA256
 
secp256k1
Note that the EC curve secp256k1 was removed from Java 15 by Oracle, and Java 16 by OpenJDK. This library retains
the necesarry code, but it is not well tested.
- Ref: https://bugs.openjdk.java.net/browse/JDK-8251547
 - Ref: https://www.oracle.com/java/technologies/javase/15-relnote-issues.html#JDK-8237219
 
How-to: List all keys for a specific JWS algorithm
try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // you need the resource ID of the key ring to use
    String keyRingResourceName = "projects/you-project/locations/europe/keyRings/your-keyring";
    
    // the key handle factory is your key access point, and caches keys in memory for you
    KmsKeyHandleFactory factory = KmsKeyHandleFactory.builder(client, KeyRingName.parse(keyRingResourceName))
                    .withKeyCacheDuration(Duration.ofSeconds(60))
                    .build();
    // list by a specific algorithm
    for (KmsKeyHandle h : factory.listByAlgorithm(a -> a.equals(JWSAlgorithm.ES256))) {
        // TODO: do something smart here
    }
}
How-to: Create a JWK set for a set of public keys
try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // you need the resource ID of the key ring to use
    String keyRingResourceName = "projects/you-project/locations/europe/keyRings/your-keyring";
    
    // the key handle factory is your key access point, and caches keys in memory for you
    KmsKeyHandleFactory factory = KmsKeyHandleFactory.builder(client, KeyRingName.parse(keyRingResourceName))
                    .withKeyCacheDuration(Duration.ofSeconds(60))
                    .build();
    // create a JWK set, only public keys will be included, and HMAC keys will be filtered out
    JWKSet jwks = PublicJwkSetCreator.of(factory.list());
}