Google Cloud KMS Integration

This guide covers the Google Cloud Key Management Service (KMS) integration for TrustWeave. The Google Cloud KMS plugin provides production-ready key management with support for all Google Cloud KMS-compatible algorithms.

Overview

The kms/plugins/google module provides a complete implementation of TrustWeave’s KeyManagementService interface using Google Cloud Key Management Service. This integration enables you to:

  • Use Google Cloud KMS for secure key generation and storage
  • Leverage Google Cloud KMS’s key versioning and rotation capabilities
  • Support all Google Cloud KMS-compatible algorithms (secp256k1, P-256/P-384, RSA)
  • Integrate with existing Google Cloud infrastructure and IAM policies

Installation

Add the Google Cloud KMS module to your dependencies:

1
2
3
4
5
dependencies {
    implementation("com.trustweave.kms:google:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-kms:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-common:1.0.0-SNAPSHOT")
}

Configuration

Basic Configuration

The Google Cloud KMS provider can be configured via options map or environment variables:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.trustweave.kms.*
import com.trustweave.googlekms.*
import java.util.ServiceLoader

// Discover Google Cloud KMS provider
val providers = ServiceLoader.load(KeyManagementServiceProvider::class.java)
val googleProvider = providers.find { it.name == "google-cloud-kms" }

// Create KMS with explicit configuration
val kms = googleProvider?.create(mapOf(
    "projectId" to "my-project",
    "location" to "us-east1",
    "keyRing" to "my-key-ring"
))

Authentication

The plugin supports multiple authentication methods:

When running on Google Cloud infrastructure (Compute Engine, Cloud Run, GKE), use Application Default Credentials:

1
2
3
4
5
6
// No credentials needed - uses ADC automatically
val kms = googleProvider?.create(mapOf(
    "projectId" to "my-project",
    "location" to "us-east1",
    "keyRing" to "my-key-ring"
))

Set up ADC:

1
gcloud auth application-default login

2. Service Account JSON File

For local development or non-Google Cloud environments:

1
2
3
4
5
6
val kms = googleProvider?.create(mapOf(
    "projectId" to "my-project",
    "location" to "us-east1",
    "keyRing" to "my-key-ring",
    "credentialsPath" to "/path/to/service-account.json"
))

3. Service Account JSON String

For programmatic configuration:

1
2
3
4
5
6
val kms = googleProvider?.create(mapOf(
    "projectId" to "my-project",
    "location" to "us-east1",
    "keyRing" to "my-key-ring",
    "credentialsJson" to serviceAccountJsonString
))

4. Environment Variables

The plugin automatically reads from environment variables:

1
2
3
4
export GOOGLE_CLOUD_PROJECT=my-project
export GOOGLE_CLOUD_LOCATION=us-east1
export GOOGLE_CLOUD_KEY_RING=my-key-ring
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
1
2
3
// Configuration loaded from environment automatically
val config = GoogleKmsConfig.fromEnvironment()
val kms = GoogleCloudKeyManagementService(config ?: throw IllegalStateException("Google Cloud config not found"))

Direct Configuration

You can also configure directly using the builder:

1
2
3
4
5
6
7
8
9
10
import com.trustweave.googlekms.*

val config = GoogleKmsConfig.builder()
    .projectId("my-project")
    .location("us-east1")
    .keyRing("my-key-ring")
    .credentialsPath("/path/to/service-account.json")
    .build()

val kms = GoogleCloudKeyManagementService(config)

Algorithm Support

The Google Cloud KMS plugin supports the following algorithms:

Algorithm Google Cloud KMS Algorithm Notes
secp256k1 EC_SIGN_SECP256K1_SHA256 Blockchain-compatible
P-256 EC_SIGN_P256_SHA256 FIPS 140-2 compliant
P-384 EC_SIGN_P384_SHA384 FIPS 140-2 compliant
RSA-2048 RSA_SIGN_PKCS1_2048_SHA256 Legacy support
RSA-3072 RSA_SIGN_PKCS1_3072_SHA256 Higher security
RSA-4096 RSA_SIGN_PKCS1_4096_SHA256 Maximum security

Note: Ed25519 and P-521 support may vary by Google Cloud KMS version. The plugin will throw a clear error message if these algorithms are not available, suggesting alternatives.

Checking Algorithm Support

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val kms = googleProvider?.create(mapOf(
    "projectId" to "my-project",
    "location" to "us-east1",
    "keyRing" to "my-key-ring"
))

// Get all supported algorithms
val supported = kms?.getSupportedAlgorithms()
println("Supported algorithms: ${supported?.joinToString { it.name }}")

// Check specific algorithm
if (kms?.supportsAlgorithm(Algorithm.Secp256k1) == true) {
    println("secp256k1 is supported")
}

Usage Examples

Generating Keys

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import com.trustweave.kms.*

// Generate secp256k1 key
val key = kms.generateKey(Algorithm.Secp256k1)

// Generate key with custom ID and labels
val keyWithLabels = kms.generateKey(
    algorithm = Algorithm.P256,
    options = mapOf(
        "keyId" to "issuer-key-2025",
        "labels" to mapOf(
            "environment" to "production",
            "purpose" to "issuance"
        )
    )
)

// Generate P-256 key for FIPS compliance
val fipsKey = kms.generateKey(
    algorithm = Algorithm.P256,
    options = mapOf(
        "keyRing" to "fips-keys" // Override default key ring
    )
)

Signing Data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Sign with key resource name (KeyId value class)
val signature = kms.sign(KeyId(keyId), data.toByteArray())

// Sign with full resource name
val signature = kms.sign(
    KeyId("projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key"),
    data.toByteArray()
)

// Sign with algorithm override
val signature = kms.sign(
    keyId = KeyId(keyId),
    data = data.toByteArray(),
    algorithm = Algorithm.Secp256k1
)

Retrieving Public Keys

1
2
3
4
5
6
7
8
9
10
11
// Get public key by key ID (uses config defaults, KeyId value class)
val publicKey = kms.getPublicKey(KeyId("my-key"))

// Get public key by full resource name
val publicKey = kms.getPublicKey(
    KeyId("projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key")
)

// Access JWK format
val jwk = publicKey.publicKeyJwk
println("Public key JWK: $jwk")

Key Deletion

1
2
3
4
5
// Destroy key version (makes key unusable)
val deleted = kms.deleteKey(KeyId(keyId))

// Note: Google Cloud KMS schedules key version destruction
// The key version enters a "DESTROYED" state and cannot be used

Key Resource Names

Google Cloud KMS uses full resource names for keys:

1
projects/{project}/locations/{location}/keyRings/{keyRing}/cryptoKeys/{key}

The plugin supports both:

  • Full resource names: projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key
  • Short names: my-key (uses defaults from config)

Key Versioning

Google Cloud KMS uses key versions. When you create a key, a primary version is automatically created. The plugin:

  • Uses the primary version for all operations
  • Returns the full resource name as the key ID
  • Handles version management automatically

Error Handling

The plugin maps Google Cloud exceptions to TrustWeave exceptions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
try {
    val key = kms.generateKey(Algorithm.Secp256k1)
} catch (e: UnsupportedAlgorithmException) {
    println("Algorithm not supported: ${e.message}")
} catch (e: KeyNotFoundException) {
    println("Key not found: ${e.message}")
} catch (e: TrustWeaveException) {
    when {
        e.message?.contains("Permission denied") == true -> {
            println("Check IAM permissions")
        }
        e.message?.contains("Key not found") == true -> {
            println("Key does not exist")
        }
        else -> {
            println("Error: ${e.message}")
        }
    }
}

Common Errors

Error Cause Solution
PermissionDeniedException Insufficient IAM permissions Grant cloudkms.cryptoKeys.create, cloudkms.cryptoKeys.useToSign, cloudkms.cryptoKeyVersions.useToSign permissions
NotFoundException Key doesn’t exist Verify key resource name is correct
UnsupportedAlgorithmException Algorithm not supported Check algorithm compatibility table
IllegalArgumentException Missing required config Ensure projectId, location, and keyRing are provided

IAM Permissions

Your Google Cloud service account needs the following IAM roles:

Minimum Required Permissions

1
2
3
4
5
6
7
8
9
10
{
  "bindings": [
    {
      "role": "roles/cloudkms.cryptoKeyEncrypterDecrypter",
      "members": [
        "serviceAccount:my-service-account@my-project.iam.gserviceaccount.com"
      ]
    }
  ]
}

For full key management capabilities:

  • roles/cloudkms.admin - Full key management access
  • roles/cloudkms.cryptoKeyEncrypterDecrypter - Signing and encryption
  • roles/cloudkms.viewer - Read-only access to keys

Key Ring Permissions

Ensure the service account has access to the key ring:

1
2
3
4
gcloud kms keyrings add-iam-policy-binding my-key-ring \
    --location=us-east1 \
    --member="serviceAccount:my-service-account@my-project.iam.gserviceaccount.com" \
    --role="roles/cloudkms.cryptoKeyEncrypterDecrypter"

Algorithm Compatibility

See the Algorithm Compatibility Table for detailed comparison of algorithm support across DIDs, VCs, AWS KMS, Azure Key Vault, and Google Cloud KMS.

Key Points:

  • ✅ secp256k1 supported for blockchain integration
  • ✅ All NIST curves (P-256/P-384) supported
  • ✅ RSA keys supported for legacy compatibility
  • ⚠️ Ed25519 support varies by Google Cloud KMS version
  • ⚠️ P-521 support varies by Google Cloud KMS version
  • ❌ BLS12-381 not supported (requires specialized KMS)

Best Practices

  1. Use Application Default Credentials: Prefer ADC over service account files for production
  2. Key Rings: Organize keys by environment or purpose using key rings
  3. Key Labels: Use labels to tag keys with metadata (environment, purpose, etc.)
  4. Resource Names: Use full resource names for clarity in production
  5. Error Handling: Implement proper error handling for Google Cloud exceptions
  6. Key Lifecycle: Plan key rotation and destruction schedules
  7. Access Control: Use IAM policies to restrict key access
  8. Key Versioning: Understand that Google Cloud KMS uses key versions internally

Testing

Unit Tests

The module includes unit tests that can be run without Google Cloud credentials:

1
./gradlew :kms/plugins/google:test

Integration Tests

For integration testing, use a test Google Cloud project:

1
2
3
4
5
6
7
// Configure for test project
val kms = googleProvider?.create(mapOf(
    "projectId" to "test-project",
    "location" to "us-east1",
    "keyRing" to "test-key-ring",
    "credentialsPath" to "/path/to/test-service-account.json"
))

Using with TrustWeave

Basic Setup

1
2
3
4
5
6
7
8
9
10
11
12
import com.trustweave.*
import com.trustweave.googlekms.*

val TrustWeave = TrustWeave.create {
    kms = GoogleCloudKeyManagementService(
        GoogleKmsConfig.builder()
            .projectId("my-project")
            .location("us-east1")
            .keyRing("TrustWeave-keys")
            .build()
    )
}

With SPI Auto-Discovery

1
2
3
4
val TrustWeave = TrustWeave.create {
    // Google Cloud KMS will be discovered automatically if on classpath
    // Configure via environment variables or system properties
}

See Also