Encrypting & Decrypting Data
Encrypting and Decrpyting data involves using a Key from a Keyring.
The Grizzly system is built around the idea of KeyRings. KeyRings manage Encryption Keys. These Encryption Keys are encrypted with the KeyRing's Public Key. This Public Key can be provided by you, or a Private and Public key pair can be managed by Grizzly. If you provide the Key Pair, it is up to you to securely store this Private Key, and ensure you have access to it when encrypting/decrypting your data with an Encryption Key. This guide separates the two flows: Grizzly managed Key Pair, and customer managed Key Pair.
For this example, we'll be using the REST API, and will need to have the following setup:
- An Account
- A KeyRing
- An API Key with access to the KeyRing
REST Authentication
For each every call made to the REST endpoints, you will need to send your API Key as a Bearer token: Headers{
"Content-Type": "application/json",
"Authentication": "Bearer <API Key>"
}
Create an Account
An Account identifies the user, service, system, or whatever entity is peforming actions taken within the system. Accounts can have API Keys created on their behalf. POST auth/accounts Body{
"uid": "Account-Name",
"notes": "Test Account"
}
{
"id": "account-fb7ea9f2-e000-495d-8d41-15819aaa3dc7",
"uid": "Account-Name",
"notes": "Test Account",
"props": {},
"createdAt": "2025-06-04T21:37:29.635Z",
"updatedAt": "2025-06-04T21:37:29.635Z"
}
Create a KeyRing
This will create a KeyRing where the RSA Key Pair is managed by Grizzly. Having Grizzly manage this Key Pair makes it easier for you to work with the platform. If you have a requirement to provide your own Key Pair, see the Customer Provided Keys solution. POST asym/keyrings Body{
"name": "TestKeyRing"
}
{
"ring": {
"id": "ring-123456",
"name": "testkeyring7",
"displayName": "TestKeyRing7",
"activeKey": "key-123456-00000000",
"activeAlgorithm": "aes256",
"publicKey": "LS0tLS1CRUdJTiBS...",
"createdAt": "2025-06-05T19:01:12.777Z",
"updatedAt": "2025-06-05T19:01:12.777Z"
},
"key": {
"type": "aes256",
"id": "key-123456-00000000",
"props": {
"aks": {
"pairId": "keypair-6de34a7b-dab7-4c3e-819f-62ca3b092023"
}
},
"keyData": {
"key": "708SvJzb..."
}
},
"privateKey": "LS0tLS1CRUdJTiBQUklWQV..."
}
Create a KeyRing: Customer Managed Key Pair
When creating this KeyRing, you'll be managing the the RSA Key Pair. Every Key generated on this KeyRing will be encrypted with the Public Key. When you receive a Key for encrypting, you'll decrypt that Key with the Private Key of the RSA Key Pair. Public Key: RSA 4096-bit PEM encoded POST ks/keyrings Body{
"name": "TestKeyRing",
"publicKey": "LS0tLS1CRUdJTiBQVUJ..."
}
{
"ring": {
"id": "ring-123456",
"name": "testkeyring4",
"displayName": "TestKeyRing4",
"activeKey": "key-123456-00000000",
"activeAlgorithm": "aes256",
"createdAt": "2025-06-05T18:55:52.208Z",
"updatedAt": "2025-06-05T18:55:52.208Z",
"props": {},
"publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZL..."
},
"key": {
"type": "aes256",
"id": "key-123456-00000000",
"props": {
"aks": {
"pairId": "default"
}
},
"keyData": {
"encrypted": {
"key": {
"encoding": "byte",
"value": "GKZCiwllB7cME3R..."
}
}
}
}
}
Create an API Key
An API Key will grant access to a KeyRing. We're going to create an API Key with both encrypt and decrypt privileges, and the ability to view the KeyRing. POST /auth/apikey?uid=true Body{
"accountId": "Account-Name",
"flags": {
"keyring.TestKeyRing.read": true,
"keyring.TestKeyRing.encrypt": true,
"keyring.TestKeyRing.decrypt": true
}
}
You should get a Response like:
{
"key": "apikey-deba8256-d3d1-450f-bef1-3a4e79d44853",
"keyid": "apikeyid-8dc18c83-5e54-4069-81c2-b8eb2c54bca4",
"active": true,
"references": {
"accountId": "account-fb7ea9f2-e000-495d-8d41-15819aaa3dc7"
},
"flags": {
"keyring.testkeyring.read": true,
"keyring.testkeyring.decrypt": true,
"keyring.testkeyring.encrypt": true
},
"props": {
"accountId": "Account-Name"
},
"createdAt": "2025-06-04T22:08:15.641Z",
"updatedAt": "2025-06-04T22:08:15.669Z"
}
Encrypting Data
To encrypt data, you'll need your API Key from the previous section. There are a couple ways to encrypt the data:
- Use our SDKs - this is the easiest way
- Using the REST APIs
Typescript SDK
import { KeyClient } from '@grizzlycbg/nodejs'
let client = await KeyClient.create({
host: `https://<grizzly-endpoint>`,
asymHost: `https://<grizzly-endpoint>/asym`,
apikey: "<API-Key>"
})
// Call to encrypt with the data to encrypt, and the KeyRing name
const encrypted = await client.encrypt("Secret Message", "Finance")
console.log(`Encrypted: ${encrypted})
REST APIs
To encrypt data: - Call the `ks/encrypt` endpoint to receieve an encrypted Key - Decrypt the encrypted Key - Use the decrypted Key to encrypt your data - Prepend the `header` to the encrypted data. This is necessary to match the encrpytion Key that was used to encrypt this data.Calling the `ks/encrypt` endpoint
POST ks/encrypt Headers{
"Content-Type": "application/json",
"Authentication": "Bearer <API Key>"
}
{
"ringname": "TestKeyRing"
}
{
"key": {
"type": "aes256",
"id": "key-000000-00000000",
"props": {
"aks": {
"pairId": "default"
}
},
"keyData": {
"encrypted": {
"key": {
"value": "WA+Tc6MxvZ8H5pva6...", // <-- Base64 encoded Encryption Key
"encoding": "byte"
}
},
"iv": "Q7N8C4hhQiVqAAAD" // <-- base64 encoded IV
}
},
"ringId": "ring-000000",
"hash": "AAAAAAAAAAAAAAAAAAA=", // <-- Unique hash representing the KeyRing and Key
"header": "AQAgAAAAAAAAAAAAAAAAAAABAAxDs3wLiGFCJWoAAAM="
}
Decrypt the encrypted Key
Pass in the KeyRing ID and Key ID in the following URL. It will return the decrypted version of the Key you will use for encrypting. GET asym/keyrings/[keyring-id]/keys/[key-id]?id=true Response{
"type": "aes256",
"id": "key-123456-00000000",
"props": {
"aks": {
"pairId": "default"
}
},
"keyData": {
"key": "sGBt3shukd1OEK/QBWXaD8E5TdN+AiWXS4c9qWV/53E="
}
}
keyData.key contains the decrypted Key, base64 encoded. Convert it to a Byte Array. The previous REST call also provided a unique IV. Use both the encryption Key and the IV to encrypt your data.
Typescript
const encryptionKey = Buffer.from(key.keyData, "base64")
Python
import base64
key_data = base64.b64decode(encoded_key_data)
Use the decrypted Key to encrypt your data
Use both the Encryption key and IV for performing the AES256-GCM encryption.Typescript
import crypto from "node:crypto"
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
let encrypted = cipher.update(
"Some text that needs to be encrypted",
"utf8",
"hex"
);
encrypted += cipher.final();
encrypted += cipher.getAuthTag();
encrypted = Buffer.concat([header, encrypted])
console.log(`Encrypted: ${encrypted})
Python
Using the `pycryptodome` libraryfrom Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_GCM, iv)
encode_me = "I should be encoded!"
ciphertext, auth_tag = cipher.encrypt_and_digest(encode_me.encode())
# Prepend the encrypted data with the Header (base64 decoded header)
# And append the auth_tag for validation
ciphertext = header + ciphertext + auth_tag
Decrypting Data
To decrypt data, you'll also need the API Key from the previous section. Decrypting data involves sending Grizzly either the hash or the header that was receieved when encrypting the data.
We'll walk through two ways to decrypt:
- Use our SDKs - the easiest way
- Using the REST APIs
Typescript SDK
import { KeyClient } from '@grizzlycbg/nodejs'
let client = await KeyClient.create({
rest: {
keystore: {
host: `https://<grizzly-endpoint>`,
port: "443",
auth: {
apikey: "<API-Key>"
},
pathPrefix: "ks"
},
// Only required if Grizzly is managing the KeyPairs for you
asym: {
host: `https://<grizzly-endpoint>/asym`,
port: "443",
auth: {
apikey: "<API-Key>"
}
}
}
})
const ring = await client.ring.get("TestKeyRing")
const decrypted = await ring.decrypt(encrypted)
console.log(`Decrypted: ${decrypted})
REST APIs
To Decrypt data: - Call the `ks/decrypt` endpoint and pass in the header - Decrypt the encrypted Key - Use the decrypted Key to decrypt your dataCalling the `ks/decrypt` endpoint
In the previous section, where we encrypted the data, we prepended the header to it, as well as appended the `authTag` to the encrypted data. Parse those off of the encrypted data.Typescript
// This assumes the encrypted data is still a Buffer. If it has been converted
// to text, convert it to a Buffer first.
// The header length is 2 bytes long starting at index 1
const headerLength = encrypted.readUInt16BE(1)
const header = encrypted.slice(0, headerLength)
// Slice off the auth tag - the last 16 bytes
const authTag = encrypted.slice(encrypted.length - 16)
// Extract the encrypted data
const payload = encrypted.slice(headerLength, encrypted.length - 16)
Python
# This assumes the encrypted data is still a byte array. If it has been converted
# to text, convert it to a byte array.
# The header length is 2 bytes long starting at index 1
# Extract version and header length
version, header_length = struct.unpack(">BH", data[:3])
# Extract the header
header = data[:header_length]
# Extract payload (everything between header and last 16 bytes)
payload = data[header_length:-16]
# Extract the last 16 bytes as the auth tag
auth_tag = data[-16:]
{
"Content-Type": "application/json",
"Authentication": "Bearer <API Key>"
}
{
"header": "AQAgAAAA..."
}
{
"ringId": "ring-123456",
"ringname": "testkeyring",
"key": {
"id": "key-123456-00000000",
"type": "aes256",
"props": {
"aks": {
"pairId": "default"
}
},
"keyData": {
"encrypted": {
"key": {
"value": "bITQanNayAxLb4...",
"encoding": "byte"
}
},
"iv": "M9sw3WsZ9xpSAAAB"
}
}
}
Decrypt the encrypted Key
Pass in the KeyRing ID and Key ID in the following URL. It will return the decrypted version of the Key you will use for decryption. GET asym/keyrings/[keyring-id]/keys/[key-id]?id=true Response{
"type": "aes256",
"id": "key-123456-00000000",
"props": {
"aks": {
"pairId": "default"
}
},
"keyData": {
"key": "sGBt3shukd1OEK/..."
}
}
keyData.key contains the decrypted Key, base64 encoded. Convert it to a Byte Array.
Typescript
const encryptionKey = Buffer.from(key.keyData, "base64")
Python
import base64
key_data = base64.b64decode(encoded_key_data)
Use the decrypted Key to decrypt your data
Use both the Encryption key and IV for performing the AES256-GCM decryption.Typescript
import crypto from "node:crypto"
const cipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
let decrypted = cipher.update(encrypted);
// Call setAuthTag() before calling final()
cipher.setAuthTag(authTag);
decrypted += cipher.final();
console.log(`Decrypted: ${decrypted})
Python
Using the `pycryptodome` libraryfrom Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_GCM, iv)
# Validate authentication tag and decrypt
try:
decrypted = cipher.decrypt_and_verify(ciphertext, auth_tag)
return decrypted.decode()
except ValueError:
return "Decryption failed: Authentication tag mismatch"