Encrypting Data With The Notecard
The Notecard was built with security in mind. With a factory-installed ECC P-384 certificate provisioned at chip manufacture, and "off-the-internet" communication between the Notecard and Notehub.io cloud service, you can be sure that your data is safe. For certain kinds of applications, however, you may wish to have an additional layer of security between your host application and your cloud app. To enable this extra layer of security, the Notecard supports end-to-end encryption of your data. Using an AES-256 symmetric algorithm based on an RSA public key you specify, the Notecard can encrypt every Note added by your host. Encrypted Notes are then synched to Notehub, where they can be routed to your cloud application for decryption using your RSA private key.
Notecard Encryption Overview
Notefile encryption (as is demonstrated in this guide) is not supported on the Notecard LoRa.
At a high-level, you'll need to take the following steps to leverage end-to-end encryption with the Notecard and Notehub.io:
- Generate an RSA key pair.
- Add the contents of your RSA public key to an environment variable in your Notehub project. We recommend adding your key to a project-level variable to ensure it can be used by all of the devices in your solution.
- When adding Notes to the Notecard, use the
key
argument with eachnote.add
request and provide the name of the environment variable added to Notehub in the previous step. - Create a Notehub Route to send your encrypted data to your cloud application.
- Upon receipt of your encrypted data, use your RSA private key to decrypt it.
The remainder of this guide will provide step-by-step instructions for using the encryption capabilities of the Notecard, along with examples of decrypting the data using OpenSSL and Node.js.
Notecard Encryption Walkthrough
Create an RSA Key Pair
First, create an RSA public and private key pair to use for your application.
For example, to generate a 4096-bit RSA key pair using openssl
:
openssl genrsa -out keys/private.pem 4096
openssl rsa -in keys/private.pem -pubout -out keys/public.pem
The commands above will output two files with .pem
extensions, one with your
private key, and one with the public key.
Add Your Public Key To Notehub as an Environment Variable
Once you have a key pair, you'll need to add the public key to your Notehub project so that the Notecard can encrypt Note bodies on your behalf.
Navigate to your Notehub project and click the "Environment" menu item in the left navigation.
Then, create a new environment variable with any name you wish. You'll use this name below to tell the Notecard which key to use to encrypt your data. Copy the complete contents of your public key file into the value field for this variable.
Add Notes with the key
argument
After the public key environment variable is set, all that's left is to include
the key
argument when sending note.add
requests. Use the name you specified
for the variable in Notehub and the Notecard will encrypt the Note body
before
it saves it to a Notefile.
{
"req": "note.add",
"file": "sensors.qo",
"key": "encryption_key",
"body":
{
"temp": 72.22,
"humidity": 21.3,
"pressure": 1.002,
"sound_level": 68.9,
"heart_rate": 65,
}
}
J *req = notecard.newRequest("note.add");
JAddStringToObject(req, "file", "sensors.qo");
JAddStringToObject(req, "key", "encryption_key");
J *body = JCreateObject();
JAddNumberToObject(body, "temp", bmp280.temperature);
JAddNumberToObject(body, "humidity", sht31d.relative_humidity);
JAddNumberToObject(body, "pressure", bmp280.pressure);
JAddNumberToObject(body, "sound_level", sound_level);
JAddNumberToObject(body, "heart_rate", heart_rate);
JAddItemToObject(req, "body", body);
notecard.sendRequest(req);
req = {"req": "note.add"}
req["file"] = "sensors.qo"
req["key"] = "encryption_key"
req["body"] = {
"temp": bmp280.temperature,
"humidity": sht31d.relative_humidity,
"pressure": bmp280.pressure,
"sound_level": sound_level,
"heart_rate": heart_rate,
}
card.Transaction(req)
How exactly does the Notecard encrypt my data?
It's important to understand the exact process the Notecard uses to encrypt your Note body because you'll need to do the reverse to decrypt it once received from Notehub.
- The Notecard generates a random 64 bit AES encryption key.
- The clear text Note body you specified in the
note.add
request is encrypted with the AES key and base64-encoded for transport. - The random AES key is then encrypted with your RSA public key and base64-encoded for transport.
Once encryption is complete, the original Note body is replaced with a new body that contains the original encrypted body and the encrypted AES key.
Route Your Data
After the next Notecard sync with Notehub, your Notes will appear with encrypted data.
{
"alg": "rsa+aes-256-cbc",
"data": "FIo2VF1lj052pjKQ6L0fRrlLWAdi1jdN12ARXghDtbAscsgbgXFiYVdXnECqe0zI6+c7fIwh7B3N52BmLyo4LQv+pBDnUV9sPoPgvj81jdm9j0Ac+AlCibNn3wooRjIIEJIePSJbUYUDkHq8cvNoHuxcgSJzTZHhnJ2zz4nmNYbOeIG4w+sCFcrx3HTrHc0JIEd8ySFZWCdJV536Ej3KTavYjIeZWByknx9ultZ42vR5fvJQyQscG30NipHvmY+i",
"env": "encryption_key",
"key": "HYmDRAQ3hZBpmSnwimASF1sPlXsJy0tYiam9pc5aaZRglVWuQnpDJeNpHerQuKWtKsRyzaFSGpeRyU8U0EIffah1azcA1ZxmVC+1YJOaBUhwKczdNI0jWLARZu767AB27F6wD0NLXk9UY6wtHRf7+Yt5T+jUrrEKW7abogcV9V7GUuds6wSHiIGob7Md2zIv2hxLLLErSmwYa4rLU69BkkFH9ffO0LqkjxYOhp5KQ5yDuU8QfvRZ7Lp/iTxoz7CkQoeCnLEfCEZnNUSZaLlhmoCjClvc+kcVd6M3S682j6LG8PDYVmJUVE7u1qd0khmPHLRoGmr9HpNL6N3KwcjpLQ=="
}
From Notehub, you can route your encrypted data
to your cloud app of choice. Don't forget to include both the data
and key
fields in any transformations you might make prior to routing. You'll need both
to properly decrypt the data.
Decrypt Your data
After you've safely received the encrypted data and key from Notehub you can decrypt it with the following steps:
- Decode the base64 encrypted AES key and decrypt it using your RSA Private key.
- Decode the base64 encrypted data and decrypt it with the decrypted AES key from the last step.
### Decrypt with OpenSSL ###
# Decode the base64-encoded AES key
openssl base64 -A -d -in transport/ciphertext_key_out.b64 -out temp/ciphertext_dec_key.bin
# Decrypt the AES key using the RSA private key
openssl rsautl -decrypt -inkey keys/private.pem -in temp/ciphertext_dec_key.bin -out temp/dec_key.bin
# Decode the base64-encoded encrypted data
openssl base64 -A -d -in transport/ciphertext_data_out.b64 -out temp/ciphertext_dec_data.bin
# Decrypt the encrypted data using the decrypted AES key
KEYHEX=$(xxd -c 256 -p temp/dec_key.bin) && openssl enc -d -aes-256-cbc -iv 0 -K $KEYHEX -in temp/ciphertext_dec_data.bin -out output/cleartext_data.bin
/* Decrypt with Nodejs and the built-in Crypto library */
const crypto = require('crypto');
const fs = require('fs');
const cipher_text = '<encrypted data from Notehub>';
const encrypted_aes = '<encrypted AES key from Notehub>';
const Algorithm = 'AES-256-CBC';
const IV_LENGTH = 16;
// Load RSA Private Key PEM file
const privateKeyFile = fs.readFileSync('./keys/private.pem');
const aes_decrypt = function (encrypted_aes, cipherText) {
// Decrypt the random AES with RSA private key
const aes = crypto.privateDecrypt({
key: privateKeyFile,
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(encrypted_aes, "base64"));
// Load the encrypted data into a Buffer
const text = Buffer.from(cipherText, 'base64');
const iv = Buffer.alloc(IV_LENGTH, 0);
// Create a Decipher object using the decrypted AES key
var decipher = crypto.createDecipheriv(Algorithm, aes, iv);
decipher.setAutoPadding(false);
// Decrypt the cipher text using the AES key
var dec = decipher.update(text, 'base64', 'utf8');
dec += decipher.final('utf8');
// Strip Unicode Characters from the resulting object
// for easier JSON parsing.
return dec.replace(/[\u0000-\u001F+\f]/gu,"");
}
const decryptedMessage = aes_decrypt(encrypted_aes, cipher_text);
console.log("Plaintext Note: ", decryptedMessage);