This tutorial demonstrates how to use a Google Cloud HSM-backed secp256k1 key for signing Hedera transactions. This solution ensures that your private keys are stored securely in Google Cloud’s Hardware Security Module.
Prerequisites
Before you begin, ensure you have:
- A Google Cloud Platform (GCP) account with billing enabled
- Google Cloud SDK (gcloud) installed and configured (Installation Instructions)
- Node.js (version 18.0.0 or higher)
- A Hedera Testnet account. If you don’t have one, you can register at the Hedera Developer Portal
Part 1: Google Cloud HSM Setup
1. Enable Required APIs
Enable the Cloud KMS API for your project.
# Set your project ID
export PROJECT_ID="your-gcp-project-id"
# Enable Cloud KMS API
gcloud services enable cloudkms.googleapis.com --project=$PROJECT_ID
2. Create a Key Ring
Create a key ring to organize your keys. Key rings must be created in a specific location.
# Set the location (e.g., us-east1, us-west1, etc.)
export LOCATION="us-east1"
# Create the key ring
gcloud kms keyrings create hedera-keyring \
--location=$LOCATION \
--project=$PROJECT_ID
3. Create an HSM-Backed Key
Create a secp256k1 key with HSM protection. This requires Cloud HSM, which provides FIPS 140-2 Level 3 certified hardware.
# Create the HSM key
gcloud kms keys create hedera-secp256k1-key \
--keyring=hedera-keyring \
--location=$LOCATION \
--purpose=asymmetric-signing \
--default-algorithm=ec-sign-p256-sha256 \
--protection-level=hsm \
--project=$PROJECT_ID
Note: HSM protection level requires Cloud HSM, which may have additional costs. For development, you can use software protection level, but for production, HSM is recommended.
4. Set Up Service Account
Create a service account for your application to authenticate with GCP.
# Create service account
gcloud iam service-accounts create hedera-kms-signer \
--display-name="Hedera KMS Signer" \
--project=$PROJECT_ID
# Get the service account email
export SERVICE_ACCOUNT_EMAIL="hedera-kms-signer@${PROJECT_ID}.iam.gserviceaccount.com"
5. Grant Permissions
Grant the service account permission to use the key for signing operations.
# Grant Cloud KMS CryptoKey Signer role
gcloud kms keys add-iam-policy-binding hedera-secp256k1-key \
--location=$LOCATION \
--keyring=hedera-keyring \
--member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
--role="roles/cloudkms.signer" \
--project=$PROJECT_ID
6. Create and Download Service Account Key
Create a key file for the service account to authenticate your application.
# Create and download the key file
gcloud iam service-accounts keys create ~/hedera-kms-key.json \
--iam-account=$SERVICE_ACCOUNT_EMAIL \
--project=$PROJECT_ID
Part 2: Application Setup
1. Install Dependencies
Create a new Node.js project and install the required dependencies.
npm init -y
npm install @hashgraph/sdk @google-cloud/kms dotenv
2. Create the .env File
Create a .env file with your GCP credentials and Hedera account details.
# Google Cloud Configuration
GOOGLE_APPLICATION_CREDENTIALS=/path/to/hedera-kms-key.json
GCP_PROJECT_ID=<YOUR_GCP_PROJECT_ID>
GCP_LOCATION=us-east1
GCP_KEY_RING=hedera-keyring
GCP_KEY_NAME=hedera-secp256k1-key
# Hedera Account Details
HEDERA_ACCOUNT_ID=<YOUR_HEDERA_ACCOUNT_ID>
HEDERA_PRIVATE_KEY=<YOUR_HEDERA_PRIVATE_KEY>
HEDERA_NETWORK=testnet
3. Create the Signer Function
Create a custom signer function that uses Google Cloud KMS to sign transaction bytes.
const { KeyManagementServiceClient } = require("@google-cloud/kms");
const { PrivateKey } = require("@hashgraph/sdk");
const client = new KeyManagementServiceClient();
async function createGcpKmsSigner(projectId, location, keyRing, keyName) {
const keyPath = client.cryptoKeyPath(projectId, location, keyRing, keyName);
// Get the public key from KMS
const [publicKeyResponse] = await client.getPublicKey({
name: client.cryptoKeyVersionPath(projectId, location, keyRing, keyName, "1")
});
// Convert the public key to Hedera format
const publicKeyBytes = Buffer.from(publicKeyResponse.pem, "base64");
const publicKey = PrivateKey.fromBytesECDSA(publicKeyBytes);
// Return a signer function
return {
publicKey: publicKey,
sign: async (message) => {
const [signResponse] = await client.asymmetricSign({
name: client.cryptoKeyVersionPath(projectId, location, keyRing, keyName, "1"),
digest: {
sha256: message
}
});
return Buffer.from(signResponse.signature, "base64");
}
};
}
4. Create and Sign a Transaction
Use the custom signer to create and sign a Hedera transaction.
const { Client, AccountCreateTransaction, Hbar } = require("@hashgraph/sdk");
require("dotenv").config();
async function main() {
// Create Hedera client
const client = Client.forTestnet();
client.setOperator(
process.env.HEDERA_ACCOUNT_ID,
process.env.HEDERA_PRIVATE_KEY
);
// Create GCP KMS signer
const kmsSigner = await createGcpKmsSigner(
process.env.GCP_PROJECT_ID,
process.env.GCP_LOCATION,
process.env.GCP_KEY_RING,
process.env.GCP_KEY_NAME
);
// Create a new account with the KMS public key
const transaction = await new AccountCreateTransaction()
.setKey(kmsSigner.publicKey)
.setInitialBalance(Hbar.fromTinybars(100))
.freezeWith(client);
// Sign with KMS
const messageHash = transaction.toBytes();
const signature = await kmsSigner.sign(messageHash);
transaction.addSignature(kmsSigner.publicKey, signature);
// Execute the transaction
const response = await transaction.execute(client);
const receipt = await response.getReceipt(client);
console.log("Account created:", receipt.accountId.toString());
}
main().catch(console.error);
Part 3: Verification
1. Verify Key Configuration
Verify that your KMS key is configured correctly:
gcloud kms keys describe hedera-secp256k1-key \
--keyring=hedera-keyring \
--location=$LOCATION \
--project=$PROJECT_ID
The output should show:
purpose: ASYMMETRIC_SIGN
algorithm: EC_SIGN_P256_SHA256
protectionLevel: HSM
2. Test Transaction Signing
Run your application and verify that transactions are being signed successfully with the GCP HSM key.
Cleanup
To avoid ongoing charges, delete the KMS key and related resources when you’re done:
# Delete the key (this schedules deletion, keys are retained for 24 hours)
gcloud kms keys destroy hedera-secp256k1-key \
--keyring=hedera-keyring \
--location=$LOCATION \
--project=$PROJECT_ID
# Delete the key ring (only after all keys are deleted)
gcloud kms keyrings delete hedera-keyring \
--location=$LOCATION \
--project=$PROJECT_ID
# Delete the service account
gcloud iam service-accounts delete $SERVICE_ACCOUNT_EMAIL \
--project=$PROJECT_ID
Important: Deleting a KMS key is irreversible after the retention period. Make sure you have backups of any data before deletion.
Resources