Getting Started with the Grapheene APIs

Your walkthrough guide to setup and complete your first encryption and decryption

The Grapheene system is built around the idea of KeyRings. KeyRings store Encryption Keys, and each of these Encryption Keys are themselves encrypted with the KeyRing's Public Key. This Public Key can be provided by you, or a Private and Public key pair can be generated by Grapheene. It is up to you to securely store this Private Key, and ensure you have access to it when excrypting your data with an Encryption Key.

Let's walk through the full process of creating a new KeyRing and then encrypting and decrypting (both generally referred to as encoding) some data using that KeyRing.

API Authentication

For each API call, send an Authorization header with your API Key as the Bearer token.

Authorization: "Bearer apikey-xxxxx-xxxx"

Create a New KeyRing

When a KeyRing is created, it is initialized with a default Encryption Key. We are going to let Grapheene also generate the Public and Private key pairs used to manage the KeyRing itself.

📘

You can also provide your own Private / Public Key Pair. See [Supplying your Own Public and Private Key Pairs]

POST /keyrings

Request Body Example

{  
   "ringname": "comms"  
}

Response Example

{  
    "ring": {  
        "id": "ring-000055",  
        "name": "comms",
        "displayName": "comms",
        "activeKey": "key-000055-00000000",  
        "activeAlgorithm": "aes256",  
        "createdAt": "2023-07-03T18:22:29.596Z",  
        "updatedAt": "2023-07-03T18:22:29.596Z",  
        "publicKey": "PUBLIC KEY DISPLAYED HERE"  
    },  
    "key": {  
        "id": "key-000055-00000000",  
        "type": "aes256",  
        "props": {  
            "aks": {  
                "pairId": "default"  
            }  
        },  
        "keyData": {  
            "encrypted": "G34OldddGwIhhOYqXjd3nEoSBWaIkkRbwoUdOjZk+X/8kMI3PzIK26PcqHxYWC3ihWTLPE+TKDl359j3TnTY9zSvZaxT6LLD35j5pUc8c8PAK2X0SaU5uttZuQzIkmUfPUnS6xZWBtgr6xGrBmHPo6AjZdQ07wA515mUOQlrUtfqRuQpR/4EHkewNnBmfURgpPqAYVJDIMaV5Cb5NhaxjtJpVge9C9i/w6r/+CAk6JbzpkwkqmoqbBAwDyZz8mr16xhDAtx8Y1cs3mc4llkI5V9Yij6tVnJvTgdXKPbyGNP89crx7AVV0HuFFwfLXRL3cyz6bIRH4B/McDO/wcJN1WJUuXMPYemnDBYDKATOqWN6mj/UbSZH/Bo+g/0RCxBoatHVYlaurg/MVPXNQAe217EHzZ4qEj1k0IB92xjZHHJWF4MNwLhJ43UHMDLsHr/cjWh9MTKkmzH+RpZIXb2nga1JahxlecHmhHflcxcH9MAj373QoTfSC+7Aor2C2w64JN6wH1bVHdConqNqJmGzjonwDBlNZiVONPPqsSi/OgbME1U3/YgJ4X/yZppg3r7j1DUX2glDd5qRLnmU9Y/3QlVmq0aWgsgvvO0xUWhRNZItVj4W9UGHKEouy3ZWTAhb00EwARl8blMk1zBsXzwJt3Dq+y8ZcWg5TLFXSeL4spY="  
        }  
    },  
    "privateKey": "PRIVATE KEY DISPLAYED HERE"  
}

❗️

Save your Private Key!

Be sure to save the privateKey. This key is only shown once and is not saved on the KeyRing Server. It will be required anytime you need to encrypt or decrypt using this KeyRing.

Initiate Encryption

Now that your KeyRing is setup, make a POST call to the /encrypt endpoint passing the KeyRing name in the body (in the ringname field). The response contains these important pieces of data:

  • The <<glossary:Encryption Key>>, and any other relevant algorithm specific data, needed to perform an encryption
  • The Hash: A small piece of information to determine the Encryption Key used for this encryption. You will want to save this with the encrypted data.
  • The Header: The hash with additional metadata.

📘

For additional information on the Hash and Header, see Hash and Header

Let's request the Active Encryption Key from the KeyRing:

POST /encrypt

Request Body Example

📘

Be sure to include your authorization header

{
    "ringname": "comms"
}

Response Example

{
	"key": {
		"type": "aes256",
		"id": "key-000055-00000000",
		"props": {
			"aks": {
				"pairId": "default"
			}
		},
		"keyData": {
			"encrypted": "SGmVJNWUZoSYZWA8i88WF1vAXXanuyn1gaR6/VfH1bt1iYwfbHybmY4uxsufP5O+sclW55QFSDGqH4zc8V4d4YmFDBFye4uTWzM8/l8hLMbt2r79dhAIiwq9enct9WdtqfJr6AIIGRSpSuN50uix3qHnjcDYtTz6wM2Y/bud/CYg/OoSMW6nskyoz1lXV1cSpBa/NpMWRYRTPtmXm9IUAM+Opv8sTGXzh7M9tj1/B7SBrbz4cTXcSPPjO+IJmE+sCk1Z9j2DsH2jowwBg+KK0edo81dmJlpnHpqK5ZLi4ziYOnQsr7/J92pPeX71GdXCfuH2ebdsrPThAEg165P/glrqxGbDSd8LyMPSb9HSUiGhUQD5C/pYxYJN+c5lLi0pWlA5x7YVw6PWLnHF653PDgSe6zA8fOa2kyFh+p6JmpxtT9OtreENG5iJOdVRHR3McneWQaCHH3JKCqi2yQRk0TQDEdFtbLGwny/omag4wKBvEvdxDwRbGEUFikaY62INP/4syLKOwt3a6568vM7mzpQ2DnWj3MGpoaDvCCCnLujhfvKuNL5sKwtL+nqzcSk6hsY+pOsLvWQIEDbds8VVxlD1HaxgaH2ha/8n3WE91YAEJoM4blzeSRGSqPE0cCI5fqqEfIY0MUfQAusCYGod1Gx+1mArZuF9T1i/Pdkypb8=",
			"iv": "YaXD63iMKiCjAAAC"
		}
	},
	"ringId": "ring-000055",
  "ringname": "comms",
  "hash": "AAAAAAA3AAAAAAAAAAA=",
	"header": "AQAgAAAAAAA3AAAAAAAAAAABAAxhpcPreIwqIKMAAAI="
}
  • key: The Active Encryption Key for this KeyRing.
  • hash: A hash that represents the KeyRing and Encryption Key pair.
  • header: A hash that represents the hashed value from hash, along with additional information.
  • ringId/ringname: The ID and name of the KeyRing.

📘

Save your Encryption Key

We recommend saving the Encryption Key (key) locally once you fetch it so you don’t have to request it each time you encode.

Finally, we decrypt the Active Encryption Key keyData field to access the key details necessary to perform an encryption:

import { privateDecrypt } from "node:crypto" 

const buffer = Buffer.from(keyData.encrypted, "base64")
const decrypted = privateDecrypt(privateKey, buffer).toString('utf8')
# Using pycryptodome: https://www.pycryptodome.org/

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

# The encrypted Key data will be base64 encoded and must be decoded first
enc_key_data = /* the encrypted text */
key_data = base64.b64decode(enc_key_data)

# Use the Key Ring's Private Key to decrypt the encryption Key
private_key = RSA.import_key(open("private.pem").read())
cipher_rsa = PKCS1_OAEP.new(private_key)
decrypted_key = cipher_rsa.decrypt(key_data)

The decrypted Encryption Key will look something like:

{  
  "key":"KBgbLw_Kt_IljnqxdudkBzN$kJ^y\*x&i",  
  "iv":"KfvzIbp^y^1%rmTl"  
}

Perform the Encrypt

Now that we have the Encryption Key, we can use it to encrypt some data. We're also going to prepend the header info we received previously and prepend it to the encrypted data before saving it.

import { createCipheriv, createHash } from 'node:crypto'

const key = /* import key */
const iv = /* assign the IV */
const textToBeEncrypted = "...."

// Best practice is to SHA256 the Key before encrypting to align on 32-byte padding
let sha256 = createHash('sha256')
sha256.update(key)
let hashedKey = sha256.digest()

// Perform the encryption, you must use aes-256-cbc
let cipher = createCipheriv("aes-256-cbc", hashedKey, iv)
let cipherText = cipher.update(textToBeEncrypted, 'utf8', 'hex')
cipherText += cipher.final('hex')

console.log(Buffer.from(cipherText).toString('utf8'))
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Util.Padding import pad

key = b"imported key"
iv = b"imported iv"

# Best practice is to SHA256 the Key before encrypting to align on 32-byte padding
hash_object = SHA256.new(data=key)
hashed_key = hash_object.digest()

data = b"Encrypt Me!"

# Encrypt
cipher = Crypto.Cipher.AES.new(hashed_key, AES.MODE_CBC, iv=iv)
ciphertext = cipher.encrypt(pad(data, AES.block_size))

print(ciphertext)

Initiate Decryption

Now let’s initiate a new Decryption process. We’re going to use the Encryption Header info that we saved and send it to the /decrypt API endpoint. This will provide us the matching Encryption Key info that we need to perform the decryption.

POST /decrypt

Request Body Example

📘

Be sure to include your authorization header

{  
   "hash": "AAAAAAA3AAAAAAAAAAA="  
}

Response Example

{  
  "ringname": "comms",
  "key": {
		"type": "aes256",
		"id": "key-000055-00000000",
		"props": {
			"aks": {
				"pairId": "default"
			}
		},
		"keyData": {
			"encrypted": "SGmVJNWUZoSYZWA8i88WF1vAXXanuyn1gaR6/VfH1bt1iYwfbHybmY4uxsufP5O+sclW55QFSDGqH4zc8V4d4YmFDBFye4uTWzM8/l8hLMbt2r79dhAIiwq9enct9WdtqfJr6AIIGRSpSuN50uix3qHnjcDYtTz6wM2Y/bud/CYg/OoSMW6nskyoz1lXV1cSpBa/NpMWRYRTPtmXm9IUAM+Opv8sTGXzh7M9tj1/B7SBrbz4cTXcSPPjO+IJmE+sCk1Z9j2DsH2jowwBg+KK0edo81dmJlpnHpqK5ZLi4ziYOnQsr7/J92pPeX71GdXCfuH2ebdsrPThAEg165P/glrqxGbDSd8LyMPSb9HSUiGhUQD5C/pYxYJN+c5lLi0pWlA5x7YVw6PWLnHF653PDgSe6zA8fOa2kyFh+p6JmpxtT9OtreENG5iJOdVRHR3McneWQaCHH3JKCqi2yQRk0TQDEdFtbLGwny/omag4wKBvEvdxDwRbGEUFikaY62INP/4syLKOwt3a6568vM7mzpQ2DnWj3MGpoaDvCCCnLujhfvKuNL5sKwtL+nqzcSk6hsY+pOsLvWQIEDbds8VVxlD1HaxgaH2ha/8n3WE91YAEJoM4blzeSRGSqPE0cCI5fqqEfIY0MUfQAusCYGod1Gx+1mArZuF9T1i/Pdkypb8=",				"iv": "YaXD63iMKiCjAAAC"
		}
	},
}

We’ve now verified that the Hash we have refers to the Encryption Key with ID key-000055-00000000, which we currently have locally from the Initiate Encryption section above.

Perform the Decrypt

Finally, we'll use the Encryption Key to decrypt the data.

import { createDecipheriv, createHash } from 'node:crypto'

const key = /* import key */
const iv = /* assign the IV */
const textToBeEncrypted = "...."

// Best practice is to SHA256 the Key before encrypting to align on 32-byte padding
let sha256 = createHash('sha256')
sha256.update(key)
let hashedKey = sha256.digest()

// Perform the encryption, you must use aes-256-cbc
let decipher = createDecipheriv("aes-256-cbc", hashedKey, iv)
let decipherText = decipher.update(cipherText, 'hex', 'utf8')
decipherText += decipher.final('utf8')

console.log(Buffer.from(decipherText).toString('utf8'))
# Using pycryptodome: https://www.pycryptodome.org/
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Util.Padding import unpad

key = b'import key as byte array'
iv =  b'import iv as byte array'
cyphertext = b"import your cipher text"

hash_object = SHA256.new(data=key)
hashed_key = hash_object.digest()

# Best practice is to SHA256 the Key before encrypting to align on 32-byte padding
cipher = AES.new(hashed_key, AES.AES.MODE_CBC, iv=iv)
plaintext = cipher.decrypt(data)

# Decrypt
cipher = AES.new(hashed_key, AES.MODE_CBC, iv=iv)
dec_text = cipher.decrypt(ciphertext)
dec_text = unpad(dec_text, AES.block_size)

Conclusion

We just walked through the process of creating a new KeyRing, initiating an Encrypt to get the Encryption Key info, performing the actual Encryption, initiating a Decrypt, and finally Decrypting the data. For more info, take a look at our documentation for API Keys, Hash and Header, KeyRings

Supplying your own Public and Private Key Pairs when Creating a KeyRing

Grapheene will automatically generate a Public and Private key pair for you each time you create a KeyRing. However, you may prefer to provide your own Key Pair to prevent the Private Key from ever been passed over the network. There are two ways to accomplish this:

  1. Generate a local Public and Private Key Pair and send the Public Key with your Key Ring Create Request.
  2. Utilize the Grapheene Asymmetric Key Service (AKS) to manage your Public and Private Keys for you.

Generate a local Public and Private Key Pair

Here’s an example of how to create an RSA Key Pair locally and then providing the Public Key to create a new KeyRing:

Creating an RSA Key Pair locally

Use the openssl package to easily generate the necessary files.

openssl genrsa -out pem 4096  
openssl rsa -in pem -pubout -out pem.pub

Create a new KeyRing with the generated Public Key above

POST /keyring

Request Body Example

📘

Be sure to include your authorization header

{  
   “name”: “comms-two”,  
   “publicKey”: “{Base64-encoded text of the public key file contents generated above}”  
}  

Response Example

Note that there is no privateKey field returned here since you are providing the Key Pair.

{  
    "ring": {  
        "id": "keyring-78f76248-XXXXXXXXX",  
        "name": "comms-two",  
        "activeKey": "key-3e01fa33-XXXXXXXXX",  
        "activeAlgorithm": "aes256",  
        "createdAt": "2023-07-03T18:22:29.596Z",  
        "updatedAt": "2023-07-03T18:22:29.596Z",  
        "publicKey": "KEY DISPLAYED HERE"  
    },  
    "key": {  
        "id": "key-3e01fa33-XXXXXXXXX",  
        "type": "aes256",  
        "props": {  
            "aks": {  
                "pairId": "default"  
            }  
        },  
        "keyData": {  
            "encrypted": "eRt894DniTIhhOYqXjd3nEoSBWaIkkRbwoUdOjZk+X/8kMI3PzIK26PcqHxYWC3ihWTLPE+TKDl359j3TnTY9zSvZaxT6LLD35j5pUc8c8PAK2X0SaU5uttZuQzIkmUfPUnS6xZWBtgr6xGrBmHPo6AjZdQ07wA515mUOQlrUtfqRuQpR/4EHkewNnBmfURgpPqAYVJDIMaV5Cb5NhaxjtJpVge9C9i/w6r/+CAk6JbzpkwkqmoqbBAwDyZz8mr16xhDAtx8Y1cs3mc4llkI5V9Yij6tVnJvTgdXKPbyGNP89crx7AVV0HuFFwfLXRL3cyz6bIRH4B/McDO/wcJN1WJUuXMPYemnDBYDKATOqWN6mj/UbSZH/Bo+g/0RCxBoatHVYlaurg/MVPXNQAe217EHzZ4qEj1k0IB92xjZHHJWF4MNwLhJ43UHMDLsHr/cjWh9MTKkmzH+RpZIXb2nga1JahxlecHmhHflcxcH9MAj373QoTfSC+7Aor2C2w64JN6wH1bVHdConqNqJmGzjonwDBlNZiVONPPqsSi/OgbME1U3/YgJ4X/yZppg3r7j1DUX2glDd5qRLnmU9Y/3QlVmq0aWgsgvvO0xUWhRNZItVj4W9UGHKEouy3ZWTAhb00EwARl8blMk1zBsXzwJt3Dq+y8ZcWg5TLFXSeL4spY="  
        }  
    }  
}

Utilize the Grapheene Asymmetric Key Service (AKS)

(We're currently writing this up and will have it to you once we get you setup with an AKS).


What’s Next

Now that you are familiar with Grapheene's API, see what our Sandbox and Dashboard are all about!