Encrypting and Decrypting 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 Cellular and our Notehub cloud service, you can be sure that your data is safe during transport. On Notecard LoRa the transport is encrypted using standard LoRa transport encryption. Likewise, data sent and received with Notecard WiFi is is always transferred over TLS.
If you want to ensure that all data between the Notecard and Notehub is
always transferred over a TLS connection, use a Notefile name that ends in
"s", such as .qos
, .qis
, or .dbs
.
For certain types of applications, however, you may wish to add an additional layer of security between your host application and your cloud app by encrypting data at rest. 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 a public key you specify, the Notecard can both encrypt outbound Notes added by your host and decrypt inbound Notes encrypted by your cloud application.
Encrypting and decrypting Notefiles (as demonstrated in this guide) is not supported on the Notecard LoRa.
Table of Contents
- Device to Cloud Encryption/Decryption Overview
- Device to Cloud Walkthrough
- Cloud to Device Encryption/Decryption Overview
- Cloud to Device Walkthrough
Device to Cloud Overview
At a high-level, you'll need to take the following steps when sending encrypted data from the Notecard to your cloud application:
- Generate an ECC key pair using the
secp384r1
curve. - Add the contents of your 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 project.
- 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 on your cloud application, use your private key to decrypt it.
The next section provides step-by-step instructions for using the encryption capabilities of the Notecard, along with examples of decrypting the data using OpenSSL, Node.js, and .NET.
Device to Cloud Walkthrough
Create an ECC Key Pair
First, create an ECC public and private key pair using OpenSSL:
openssl ecparam -genkey -name secp384r1 -noout -out privateKey.pem
openssl ec -in privateKey.pem -pubout -out publicKey.pem
These commands 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 the Notecard can encrypt Note bodies on your behalf.
Navigate to your Notehub project and click the "Environment" menu item in the left navigation.
Next, create a new environment variable with any name you wish. You can create the environment variable at the device, fleet, or project level. You'll use this name 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
your environment variable, making sure to prepend the value with
secp384r1-aes256cbc:
.
For example:
secp384r1-aes256cbc:-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECWghuYIN91/QPZfewFRkTiLKx8cd4jiD
gTiHnM1i97DkYmesEp3CMSO2HVREfVwtRFnQCg1ZvpUGQNrQoJ85N/8uZbbMSVza
AtlirKYB4l3gzQ7YB27oFhudt67cqMIb
-----END PUBLIC KEY-----
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 environment variable in Notehub and the Notecard will encrypt the Note
body
before it saves it to a Notefile.
If you try to encrypt data before the Notecard has synced with Notehub (to
download the environment variable you just created), you may see this error when
issuing the note.add
request:
{
"err": "encryption key environment var not found: <key name>"
}
To avoid this, make sure you complete at least one sync with Notehub after creating the environment variable.
{
"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
routed to your cloud service from Notehub.
- The Notecard generates a random 64 bit AES encryption key (this key rotates every time the Notecard encrypts data).
- The clear text Note
body
you specified in thenote.add
request is encrypted with the AES key and base64-encoded for transport. - The random AES key is then encrypted with your public key and base64-encoded for transport.
- Once encryption is complete, the original Note
body
is replaced with a newbody
that contains the original encryptedbody
and the encrypted AES key.
Route Your Data
After the next Notecard sync with Notehub, your Notes will appear with encrypted data.
{
"event": "6e94880b-34f9-83cd-ac38-b86b5f86af2b",
"when": 1729196079,
"file": "mysecrets.qo",
"body": {
"alg": "secp384r1-aes256cbc",
"data": "9MY2ZXrGJ5bOAFBr2nPfnEyrY2wsYRKTRk82BvH3stE=",
"env": "public_key",
"key": "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7y24fwyRduMX6oppm2PQhFeKWbuYuBUwGffYLsbfeMQsxegY79bKL7Lw1wwlRAlIrRsxnx1FxRl8YbSpXl4p6cawb6sTztooYEGbpejTyPQ0L5uYWvB0JRFyoWpvMbAd"
}
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
JSONata transformations
you might make prior to routing, as you'll need both to properly decrypt the
data.
Decrypt Your Data on Your Cloud Service
After you've safely received the encrypted data
and key
in the event routed
from Notehub, you can decrypt it with the following steps:
- Decode the base64 encrypted AES key and decrypt it using your private key.
- Decode the base64 encrypted data and decrypt it with the decrypted AES key from the previous step.
Use the code samples below as the basis for your cloud application to decrypt the data using OpenSSL, Node.js, or .NET.
# The "ciphertext_key.b64" file below is the ephemeral public `key` argument sent in the event from Notehub.
# The "ciphertext_data.b64" file below is the `data` argument sent in the event in notehub
# Decode the ephemeral public key and convert it to a PEM file.
openssl base64 -A -d -in ciphertext_key.b64 -out ciphertext_dec_key.der
# Use your private key to derive the shared secret from the ephemeral key.
openssl pkeyutl -derive -inkey privateKey.pem -peerform DER -peerkey ciphertext_dec_key.der -out dec_secret_key.bin
# Use a hash function to convert the derived key to a 256-bit AES key.
openssl sha256 -binary dec_secret_key.bin > dec_key.bin
# Decrypt the input file.
openssl base64 -A -d -in ciphertext_data.b64 -out ciphertext_dec_data.bin
# Write the decrypted output to "cleartext_data.bin".
KEYHEX=$(xxd -c 256 -p dec_key.bin) && openssl enc -d -aes-256-cbc -iv 0 -K $KEYHEX -in ciphertext_dec_data.bin -out cleartext_data.bin
const fs = require('fs');
const crypto = require('crypto');
// Step 1: Set the ephemeral public key (base64-encoded string) to the value of the "key" argument that came in from the Notehub event.
const ciphertextKeyBase64 = '[your-public-key-from-notehub-event]';
const ephemeralPublicKeyDer = Buffer.from(ciphertextKeyBase64, 'base64');
// Step 2: Create a KeyObject from the ephemeral public key
const ephemeralPublicKey = crypto.createPublicKey({
key: ephemeralPublicKeyDer,
format: 'der',
type: 'spki', // 'spki' is used for public keys in DER format
});
// Step 3: Read your private key from the file system
const privateKeyPem = fs.readFileSync('privateKey.pem', 'utf8');
const privateKey = crypto.createPrivateKey({
key: privateKeyPem,
format: 'pem',
type: 'sec1', // 'sec1' is used for EC private keys in PEM format
});
// Step 4: Derive the shared secret using ECDH
const sharedSecret = crypto.diffieHellman({
privateKey: privateKey,
publicKey: ephemeralPublicKey,
});
// Step 5: Hash the shared secret to obtain the AES-256 key
const aesKey = crypto.createHash('sha256').update(sharedSecret).digest();
// Step 6: Set the ciphertext data (base64-encoded string) to the value of the "data" argument that came in from the Notehub event.
const ciphertextDataBase64 = '[your-data-from-notehub-event]';
const ciphertextData = Buffer.from(ciphertextDataBase64, 'base64');
// Step 7: Decrypt the data using AES-256-CBC
const iv = Buffer.alloc(16, 0);
const decipher = crypto.createDecipheriv('aes-256-cbc', aesKey, iv);
let decryptedData = decipher.update(ciphertextData);
decryptedData = Buffer.concat([decryptedData, decipher.final()]);
// Step 8: Output the decrypted data to the console
console.log('Decrypted data:', decryptedData.toString());
// NOTE: this method requires using BouncyCastle
// dotnet add package BouncyCastle.Cryptography
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
class Program
{
static void Main(string[] args)
{
string ciphertextDataBase64 = "[your-data-from-notehub-event]";
string ciphertextKeyBase64 = "[your-public-key-from-notehub-event]";
// Step 1: Set the ephemeral public key (base64-encoded string)
byte[] ephemeralPublicKeyDer = Convert.FromBase64String(ciphertextKeyBase64);
// Step 2: Create a public key object from the DER bytes using BouncyCastle
AsymmetricKeyParameter publicKeyParam = PublicKeyFactory.CreateKey(ephemeralPublicKeyDer);
ECPublicKeyParameters ephPublicKey = (ECPublicKeyParameters)publicKeyParam;
// Step 3: Read our private key using BouncyCastle
string privateKeyPem = File.ReadAllText("privateKey.pem");
AsymmetricCipherKeyPair keyPair;
using (var reader = new StringReader(privateKeyPem))
{
PemReader pemReader = new PemReader(reader);
keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
}
ECPrivateKeyParameters privateKeyParam = (ECPrivateKeyParameters)keyPair.Private;
// Step 4: Derive the shared secret using ECDH
IBasicAgreement agreement = new ECDHBasicAgreement();
agreement.Init(privateKeyParam);
var sharedSecretBigInt = agreement.CalculateAgreement(ephPublicKey);
byte[] sharedSecret = sharedSecretBigInt.ToByteArrayUnsigned();
// Step 5: Hash the shared secret to obtain the AES-256 key
byte[] aesKey;
using (var sha256 = SHA256.Create())
{
aesKey = sha256.ComputeHash(sharedSecret);
}
// Step 6: Set the ciphertext data (base64-encoded string)
byte[] ciphertextData = Convert.FromBase64String(ciphertextDataBase64);
// Step 7: Decrypt the data using AES-256-CBC
byte[] iv = new byte[16];
byte[] decryptedData;
using (var aes = Aes.Create())
{
aes.KeySize = 256;
aes.Key = aesKey;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
using (var msDecrypt = new MemoryStream(ciphertextData))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var msPlain = new MemoryStream())
{
csDecrypt.CopyTo(msPlain);
decryptedData = msPlain.ToArray();
}
}
}
}
}
// Step 8: Output the decrypted data to the console
Console.WriteLine("Decrypted data: " + Encoding.UTF8.GetString(decryptedData));
}
}
Cloud to Device Overview
At a high-level, you'll need to take the following steps to send encrypted data from your cloud application to a Notecard:
- Generate an ECC key pair using the
secp384r1
curve. - Acquire your Notecard's static public key using the Notehub API.
- Encrypt the Note's
body
for transport in your cloud application. - Create an inbound Notefile (.qi) using the Notehub API.
- Sync the Notefile with your Notecard and decrypt the data.
The next section provides step-by-step instructions for encrypting data on your cloud application (using OpenSSL, Node.js, or .NET) and decrypting the data on the Notecard.
Cloud to Device Walkthrough
Get the Notecard's Static Public Key
You'll need to acquire the public key from each Notecard device that will receive encrypted Notefiles using the Get Device Public Key API (for a single device) or Get Public Keys API (for all devices in a project). These keys are static and do not change over time.
Consult the Notehub API documentation for examples of how to use the Get Device Public Key API or Get Public Keys API.
For example, the Get Public Keys API response will include an array of
device_public_keys
. You will need to identify the device(s) to which you will
be sending encrypted data using by comparing unique DeviceUIDs
with the uid
returned from this request.
Here is an example of a single element from an array of device_public_keys
:
{"uid":"dev:868050012345678","key":"-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYyyKpzaG9irLbqjcV1tz4x+YJcZDVKzcO\nr+kt5r5lEQ7HpzrxXQuWUT3Kz7HnGFphySqjATV4Y/HUti6jXPHO\niqaKWixrxQgz9biD\n-----END PUBLIC KEY-----\n"}
Save the key
from this JSON response to a file called device_public_key.pem
.
Make sure the \n
new line characters are actually new lines in the .pem
file
you create.
Encrypt the Data in Your Cloud Application
After you've gathered the public key(s) from your Notehub device(s), you can next encrypt an arbitrary JSON body in your cloud application.
Use the code samples below as the basis for your cloud service to encrypt data using OpenSSL, Node.js, or .NET.
# Generate a temporary private key, and then a public key from the private key as an ASN.1 DER-formatted key.
openssl ecparam -genkey -name secp384r1 -noout -out temp-private.pem
openssl ec -in temp-private.pem -pubout -outform DER -out temp-public.der
# Use the device's public key to derive what will be a shared secret.
openssl pkeyutl -derive -inkey temp-private.pem -peerkey device_public_key.pem -out enc_secret_key.bin
# Use a hash function to convert the derived key to a 256-bit AES key.
openssl sha256 -binary enc_secret_key.bin > enc_key.bin
# Encrypt the input binary using the first 256 bits of the 48-byte shared secret as an AES key.
# NOTE: The "cleartext_data.bin" file represents the JSON body you want to encrypt.
KEYHEX=$(xxd -c 256 -p enc_key.bin) && openssl enc -aes-256-cbc -iv 0 -K $KEYHEX -in cleartext_data.bin -out ciphertext_enc_data.bin
# Convert the encrypted binary file to base64 for transport.
openssl base64 -A -in ciphertext_enc_data.bin -out ciphertext_data.b64
# Convert the temporary public ASN.1 DER-formatted key to base64 for transport.
openssl base64 -A -in temp-public.der -out ciphertext_key.b64
# Create a Note body to be sent to the device
echo '{"alg":"secp384r1-aes256cbc","data":"'$(<ciphertext_data.b64)'","key":"'$(<ciphertext_key.b64)'"}' >body.json
# The result from the previous command will look something like this:
# {"alg":"secp384r1-aes256cbc","data":"cQAFX5QrzwDnmdPRZsmxRoZLkQZbSU/3BnE=","key":"MHYwEAYHKoZIzj0CAQYFgAEYkwodOA8VY4L+NbGtWSROVd8Sjo75D9x7dmHm++73q20NkgpqVXiPhDUy/kIBFRTAYxFTCZolhqI5nu5Rq8LP30i6R"}
const fs = require('fs');
const crypto = require('crypto');
// Generate a temporary EC key pair on the 'secp384r1' curve
const { publicKey: tempPublicKey, privateKey: tempPrivateKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'secp384r1',
publicKeyEncoding: { type: 'spki', format: 'der' }, // DER format for public key
privateKeyEncoding: { type: 'sec1', format: 'pem' } // PEM format for private key
});
// Read the device's public key from the filesystem
const devicePublicKeyPem = fs.readFileSync('device_public_key.pem', 'utf8');
// Extract the private key scalar for ECDH
const tempPrivateKeyObject = crypto.createPrivateKey(tempPrivateKey);
const tempPrivateKeyJWK = tempPrivateKeyObject.export({ format: 'jwk' });
const privateKeyScalar = Buffer.from(tempPrivateKeyJWK.d, 'base64url');
// Create an ECDH object and set the private key scalar
const ecdh = crypto.createECDH('secp384r1');
ecdh.setPrivateKey(privateKeyScalar);
// Import the device's public key and extract x and y coordinates
const devicePublicKeyObject = crypto.createPublicKey(devicePublicKeyPem);
const devicePublicKeyJWK = devicePublicKeyObject.export({ format: 'jwk' });
const xBuffer = Buffer.from(devicePublicKeyJWK.x, 'base64url');
const yBuffer = Buffer.from(devicePublicKeyJWK.y, 'base64url');
// Construct the uncompressed device public key
const uncompressedDevicePublicKey = Buffer.concat([Buffer.from([0x04]), xBuffer, yBuffer]);
// Compute the shared secret
const sharedSecret = ecdh.computeSecret(uncompressedDevicePublicKey);
// Derive the AES-256 key by hashing the shared secret
const aesKey = crypto.createHash('sha256').update(sharedSecret).digest();
// Specify the cleartext data to be encrypted for the Notefile
const cleartextData = Buffer.from('{"hello":"world"}', 'utf8');
// Encrypt the cleartext data using AES-256-CBC
const iv = Buffer.alloc(16, 0);
const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, iv);
const encryptedData = Buffer.concat([cipher.update(cleartextData), cipher.final()]);
// Convert the encrypted data to base64
const encryptedDataBase64 = encryptedData.toString('base64');
// Convert the temporary public key to base64
const tempPublicKeyBase64 = tempPublicKey.toString('base64');
// Create the JSON body to be sent to the device
const body = {
alg: 'secp384r1-aes256cbc',
data: encryptedDataBase64,
key: tempPublicKeyBase64
};
const bodyJson = JSON.stringify(body);
console.log(bodyJson);
// NOTE: this method requires using BouncyCastle
// dotnet add package BouncyCastle.Cryptography
using System.Text;
using System.Security.Cryptography;
using System.Text.Json;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X509;
class Program
{
static void Main(string[] args)
{
// Generate a temporary EC key pair on the 'secp384r1' curve
X9ECParameters ecParams = ECNamedCurveTable.GetByName("secp384r1");
ECDomainParameters domainParams = new ECDomainParameters(ecParams);
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
SecureRandom random = new SecureRandom();
ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, random);
keyGen.Init(keyGenParams);
AsymmetricCipherKeyPair tempKeyPair = keyGen.GenerateKeyPair();
ECPrivateKeyParameters tempPrivateKeyParams = (ECPrivateKeyParameters)tempKeyPair.Private;
ECPublicKeyParameters tempPublicKeyParams = (ECPublicKeyParameters)tempKeyPair.Public;
// Read the device's public key from the filesystem
string devicePublicKeyPem = File.ReadAllText("device_public_key.pem");
// Cleartext data to be encrypted for the Notefile
byte[] cleartextData = Encoding.UTF8.GetBytes("{\"hello\":\"world\"}");
// Parse the device's public key
AsymmetricKeyParameter devicePublicKey;
using (var reader = new StringReader(devicePublicKeyPem))
{
PemReader pemReader = new PemReader(reader);
devicePublicKey = (AsymmetricKeyParameter)pemReader.ReadObject();
}
// Perform ECDH to compute the shared secret
IBasicAgreement ecdhAgree = new ECDHBasicAgreement();
ecdhAgree.Init(tempPrivateKeyParams);
ECPublicKeyParameters devicePubKeyParams = (ECPublicKeyParameters)devicePublicKey;
var sharedSecretBigInt = ecdhAgree.CalculateAgreement(devicePubKeyParams);
// Convert shared secret to byte array
byte[] sharedSecret = sharedSecretBigInt.ToByteArrayUnsigned();
// Derive the AES-256 key by hashing the shared secret
byte[] aesKey;
using (SHA256 sha256 = SHA256.Create())
{
aesKey = sha256.ComputeHash(sharedSecret);
}
// Encrypt the cleartext data using AES-256-CBC
byte[] iv = new byte[16];
byte[] encryptedData;
using (var aes = Aes.Create())
{
aes.KeySize = 256;
aes.Key = aesKey;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.IV = iv;
using (var encryptor = aes.CreateEncryptor())
{
encryptedData = encryptor.TransformFinalBlock(cleartextData, 0, cleartextData.Length);
}
}
// Convert the encrypted data to base64
string encryptedDataBase64 = Convert.ToBase64String(encryptedData);
// Convert the temporary public key to DER format and then to base64
byte[] tempPublicKeyDer;
{
// Create SubjectPublicKeyInfo with the correct algorithm identifier
AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, SecObjectIdentifiers.SecP384r1);
SubjectPublicKeyInfo tempPublicKeyInfo = new SubjectPublicKeyInfo(algId, tempPublicKeyParams.Q.GetEncoded(false));
tempPublicKeyDer = tempPublicKeyInfo.GetDerEncoded();
}
string tempPublicKeyBase64 = Convert.ToBase64String(tempPublicKeyDer);
// Create the JSON body to be sent to the device
var body = new
{
alg = "secp384r1-aes256cbc",
data = encryptedDataBase64,
key = tempPublicKeyBase64
};
// Configure the JSON serializer options to avoid escaping characters
var jsonOptions = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string bodyJson = JsonSerializer.Serialize(body, jsonOptions);
Console.WriteLine(bodyJson);
}
}
Generate an Inbound (.qi) Notefile with the Notehub API
Next, you'll need to create and send a new inbound (.qi) Notefile on Notehub that includes the encrypted data. This is accomplished by using the Add Note API.
Consult the Notehub API documentation for examples of how to use the Add Note API.
As an example, a cURL request will end up looking something like this:
curl -X POST
-L 'https://api.notefile.net/v1/projects/<projectUID>/devices/<deviceUID>/notes/<file>/<note>'
-H 'Authorization: Bearer <access_token>'
-d '{"body": {"alg":"secp384r1-aes256cbc","data":"ji1O7IEZk5IOiYKAAEWWLyZ8r97TxQxdE=","key":"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2YeuC8lDvpSPYNa3b+CfHY0RyNJr2NsEfgix/rEpNEf4asGWuBkPojpVFraXM8cYtTbTLkviu0kq7FU0I5/5y1v7WCHyDw4x/Y"}}'
When the Notefile is created in your Notehub project, it will appear in the Events section:
Sync Your Notecard with Notehub and Decrypt the Note
With the inbound Notefile created, all that is left to do is perform a sync on each Notecard device and decrypt the downloaded data.
For example, you can use the hub.sync API to manually initiate a sync...
{"req":"hub.sync"}
...and then use the
note.get API with
the "decrypt":true
argument to decrypt, read, and delete the Notefile:
{"req":"note.get","file":"mysecrets.qi","decrypt":true,"delete":true}
{
"time": 1729110462,
"body": {
"hello": "world"
}
}