Skip to content

Instantly share code, notes, and snippets.

@kennwhite
Last active October 26, 2020 10:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kennwhite/6fce02fc707cd33f147b3b8d31ee5cfa to your computer and use it in GitHub Desktop.
Save kennwhite/6fce02fc707cd33f147b3b8d31ee5cfa to your computer and use it in GitHub Desktop.
MongoDB Client Side Field Level Encryption Quickstart Part 2 (local key version)
/*
Simple demonstration using MongoDB Client-Side Field Level Encryption (local key version)
Requires Community or (preferrably) Enterprise Shell and a MongoDB 4.2+ database
Local, stand-alone, or Atlas MongoDB will all work.
To use this, just open Mongo shell, with this file, e.g.:
mongo --nodb --shell hello_world_shell_local.js
# For a self-signed cert: mongo --tls --tlsCAFile /opt/mongodb/certs/ca.pem --shell hello_world_shell_local.js
Note, you will need the attached `localkey_config.env` file, see below.
See: Client-Side Field Level Encryption Quickstart Part 1:
https://gist.github.com/kennwhite/e64e5b6770e89a797c3a08ecaa0cb7d0
*/
var demoDB = "demoFLE"
var keyVaultColl = "__keystore" // nothing special about this key vault collection name, but make it stand out
const ENC_DETERM = 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'
const ENC_RANDOM = 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'
var env = {}
print("\nLoading local key configuration file...")
try {
load( 'localkey_config.js' );
} catch (err) {
print("Exiting: Unable to open local config file." );
quit()
}
if (env.keyString == "PASTE GENERATED KEY STRING HERE"){
print("\nPlease generate a new local key (see `localkey_config.js` file). Exiting. \n\n"); quit();
}
var localDevMasterKey = { key: BinData( 0, env.keyString ) }
var clientSideFLEOptions = {
kmsProviders : { local : localDevMasterKey } ,
schemaMap: {}, // on first invocation prior to field key generation, this should be empty
keyVaultNamespace: demoDB + "." + keyVaultColl
};
encryptedSession = new Mongo(env.connStr, clientSideFLEOptions);
// javascript shell script equivalent of: use demoFLE
db = encryptedSession.getDB( demoDB )
// Wipe sandbox. Approximate Atlas equivalent of: db.dropDatabase()
db.getCollectionNames().forEach(function(c){db.getCollection(c).drop()});
var keyVault = encryptedSession.getKeyVault();
print("Attempting to create field keys...")
keyVault.createKey("local", "", ["fieldKey1"])
keyVault.createKey("local", "", ["fieldKey2"])
print("Attempting to retrieve field keys...")
var key1 = db.getCollection( keyVaultColl ).find({ keyAltNames: 'fieldKey1' }).toArray()[0]._id
var key2 = db.getCollection( keyVaultColl ).find({ keyAltNames: 'fieldKey2' }).toArray()[0]._id
print("Setting server-side json schema for automatic encryption on `people` collection...")
db.createCollection("people")
db.runCommand({
collMod: "people",
validator: {
$jsonSchema: {
"bsonType": "object",
"properties": {
"ssn": {
"encrypt": {
"bsonType": "string",
"algorithm": ENC_DETERM,
"keyId": [ key1 ]
}
},
"dob": {
"encrypt": {
"bsonType": "date",
"algorithm": ENC_RANDOM,
"keyId": [ key1 ]
}
},
}
}
}
})
print("Creating client-side json schema config for automatic encryption on `people` collection...")
var peopleSchema = {
"demoFLE.people": {
"bsonType": "object",
"properties": {
"ssn": {
"encrypt": {
"bsonType": "string",
"algorithm": ENC_DETERM,
"keyId": [ key1 ]
}
},
"dob": {
"encrypt": {
"bsonType": "date",
"algorithm": ENC_RANDOM,
"keyId": [ key1 ]
}
},
"contact": {
"bsonType": "object",
"properties": {
"email": {
"encrypt": {
"bsonType": "string",
"algorithm": ENC_DETERM,
"keyId": [ key2 ]
}
},
"mobile": {
"encrypt": {
"bsonType": "string",
"algorithm": ENC_DETERM,
"keyId": [ key2 ]
}
}
},
},
}
}
}
print("Updating FLE mode session to enable server- and client-side json schema for automatic encryption...")
var clientSideFLEOptions = {
kmsProviders: { local: localDevMasterKey },
schemaMap: peopleSchema,
keyVaultNamespace: demoDB + "." + keyVaultColl
}
var encryptedSession = new Mongo(env.connStr, clientSideFLEOptions)
var db = encryptedSession.getDB( demoDB );
print("Attempting to detect server-side Enterprise edition mode...")
var edition = db.runCommand({buildInfo:1}).modules
var enterprise = false
if ( edition !== undefined && edition.length != 0 ){
var enterprise = true
}
print("MongoDB server running in enterprise mode: " + enterprise + "\n")
print("Attempting to insert sample document with automatic encryption...")
try {
var res = null
res = db.people.insert({
firstName: 'Grace',
lastName: 'Hopper',
ssn: "901-01-0001",
dob: new Date('1989-12-13'),
address: {
street: '123 Main Street',
city: 'Omaha',
state: 'Nebraska',
zip: '90210'
},
contact: {
mobile: '202-555-1212',
email: 'grace@example.com',
}
})
} catch (err) {
res = err
}
print("Result: " + res)
print("Attempting to insert sample document with explicit encryption...")
try{
var res = null
res = db.people.insert({
firstName: 'Alan',
lastName: 'Turing',
ssn: db.getMongo().encrypt( key1 , "901-01-0002" , ENC_DETERM ),
dob: db.getMongo().encrypt( key1 , new Date('1912-06-23'), ENC_RANDOM ),
address: {
street: '123 Oak Lane',
city: 'Cleveland',
state: 'Ohio',
zip: '90210'
},
contact: {
mobile: db.getMongo().encrypt( key2 , '202-555-1234', ENC_DETERM ),
email: db.getMongo().encrypt( key2 , 'alan@example.net', ENC_DETERM ),
}
})
} catch (err) {
res = err
}
print("Result: " + res)
print("\nEnabling session bypass on automatic encrypt/decrypt... \n")
var clientSideFLEOptions = {
"kmsProviders": { "local": localDevMasterKey },
bypassAutoEncryption: true,
schemaMap: peopleSchema,
keyVaultNamespace: demoDB + "." + keyVaultColl
}
var encryptedSession = new Mongo(env.connStr, clientSideFLEOptions)
var db = encryptedSession.getDB( demoDB );
print("Attempting to insert sample document with explicit encryption...")
try{
var res = null
res = db.people.insert({
firstName: 'Alan',
lastName: 'Turing',
ssn: db.getMongo().encrypt( key1 , "901-01-0002" , ENC_DETERM ),
dob: db.getMongo().encrypt( key1 , new Date('1912-06-23'), ENC_RANDOM ),
address: {
street: '123 Oak Lane',
city: 'Cleveland',
state: 'Ohio',
zip: '90210'
},
contact: {
mobile: db.getMongo().encrypt( key2 , '202-555-1234', ENC_DETERM ),
email: db.getMongo().encrypt( key2 , 'alan@example.net', ENC_DETERM ),
}
})
} catch (err) {
res = err
}
print("Result: " + res)
print("Dumping (raw) records from `people`:")
var records = db.people.find().pretty()
while (records.hasNext()) {
printjson(records.next());
}
print("\nDisabling session bypass for automatic encrypt/decrypt...\n")
var clientSideFLEOptions = {
kmsProviders: { local: localDevMasterKey },
schemaMap: peopleSchema,
keyVaultNamespace: demoDB + "." + keyVaultColl
}
var encryptedSession = new Mongo(env.connStr, clientSideFLEOptions)
var db = encryptedSession.getDB( demoDB );
print("Dumping (automatic decrypted) records from `people`:")
var records = db.people.find().pretty()
while (records.hasNext()) {
printjson(records.next());
}
print("\nDemo complete.")
/*
** Note: a local key file should only be used in a non-production test environment **
Instructions for generate a local master key:
For Linux & Mac, from a terminal:
echo $(head -c 96 /dev/urandom | base64 | tr -d '\n')
For Windows 8.x/2012+, from a command prompt:
powershell -command "$r=[byte[]]::new(64);$g=[System.Security.Cryptography.RandomNumberGenerator]::Create();$g.GetBytes($r);[Convert]::ToBase64String($r)"
Paste the hex string generated above into the key string value below
*/
env.keyString = "PASTE GENERATED KEY STRING HERE";
env.connStr = "localhost"
// For Atlas:
// env.connStr = "mongodb+srv://USERNAME:PASSWORD@cluster1-sample.mongodb.net"
// For on-prem TLS:
// env.connStr = "mongodb://USERNAME:PASSWORD@example.com:27017/?authMechanism=SCRAM-SHA-256&replicaSet=rs1"
$ mongo localhost refactor_local.js
MongoDB shell version v4.2.0
connecting to: mongodb://127.0.0.1:27017/localhost?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("1a03036b-1a82-4597-8518-09dc89188eb7") }
MongoDB server version: 4.2.0-82-g5eeba7b
Loading local key configuration file...
Attempting to create field keys...
Attempting to retrieve field keys...
Setting server-side json schema for automatic encryption on `people` collection...
Creating client-side json schema config for automatic encryption on `people` collection...
Updating FLE mode session to enable server- and client-side json schema for automatic encryption...
Attempting to detect server-side Enterprise edition mode...
MongoDB server running in enterprise mode: false
Attempting to insert sample document with automatic encryption...
Result: WriteResult({ "nInserted" : 1 })
Attempting to insert sample document with explicit encryption...
Result: Error: Cannot encrypt element of type binData because schema requires that type is one of: [ string ]
Enabling session bypass on automatic encrypt/decrypt...
Attempting to insert sample document with explicit encryption...
Result: WriteResult({ "nInserted" : 1 })
Dumping (raw) records from `people`
{
"_id" : ObjectId("5d6956119a916c22f2b6db38"),
"firstName" : "Grace",
"lastName" : "Hopper",
"ssn" : BinData(6,"ASRaYbcf1E0zg2JinCp6GGwCw/Gk3MBpgu+gyEU64B/vWEXMDo0jZ6iu3sFqCbuE9KSg0t70LLICxBcQ4OXsJLz046HFYy/Sd/1tnxPXgrTfvQlKV3cBvIPGIEt19RCuFUU="),
"dob" : BinData(6,"AiRaYbcf1E0zg2JinCp6GGwJON2dV0lmckr0gOR9VZdr5drjhtIx3H2vc/W+3w+pnG9faPP5hWZj92qDJIrDhntriXt7Al5Dijtke+LKjQdEDg=="),
"address" : {
"street" : "123 Main Street",
"city" : "Omaha",
"state" : "Nebraska",
"zip" : "90210"
},
"contact" : {
"mobile" : BinData(6,"ASsJPMjeiEVyp5zvrHwdpAcCPvWWGJRjIi4ZNGRD9UgtRLN5hv+Rk8i7KIkFQ3ITmnuOIFDr+L384s9Ukh9v1QOtVH2k2MYjlqLTK7+ejHDqEx40mD4rfOkX59LYcH6+VAs="),
"email" : BinData(6,"ASsJPMjeiEVyp5zvrHwdpAcCuy3r5pD3m3B4SXB+z3VGS4h7OzQHN63mDOjY14/sGj1w40x0bgnl+B/YJbLTT7caVbvOVqw/FNKmKLcX1UJ4IvjAgvfap6uLcuxD2s/LwOQ=")
}
}
{
"_id" : ObjectId("5d6956119a916c22f2b6db3a"),
"firstName" : "Alan",
"lastName" : "Turing",
"ssn" : BinData(6,"ASRaYbcf1E0zg2JinCp6GGwCKL1IVykOA6ZfETtfXbmXX/dGHNKC1glTTVe8ut3ggskSGdKZ5H8eA6RyADyZAtlf+G6SsPnFTsmf1iNsHtEO9vS+Oc2v06rN2cdNXIiUbGo="),
"dob" : BinData(6,"AiRaYbcf1E0zg2JinCp6GGwJxiOCIFXyQ6+kb6w8ZjAl0FO0iPbH0bGSEGx45KvnW37zyhWtftCuRwoAFSKnPNDflcEMZ7evAw23QSjye9l8RA=="),
"address" : {
"street" : "123 Oak Lane",
"city" : "Cleveland",
"state" : "Ohio",
"zip" : "90210"
},
"contact" : {
"mobile" : BinData(6,"ASsJPMjeiEVyp5zvrHwdpAcCM2eRrqWFp+byqc9f+XNEw3uhkQskAZvOHCzuFeCpqAugHq9c6H+aWygrRtv15NTfE3cRJ5U4LNUKTzoy5vv6CKzbkwp8mbSHYHp4iFemt8A="),
"email" : BinData(6,"ASsJPMjeiEVyp5zvrHwdpAcCa9CsILfy8Jd7jkr81bKht+Fk7Q9bwaAAeZi+i/HZ/7q38lpnrRzYEut53vbssstNd0GtcjjCgVGlGq4gypeY5y96SGdlbdRySllS+jtRUuw=")
}
}
Disabling session bypass for automatic encrypt/decrypt...
Dumping (automatic decrypted) records from `people`:
{
"_id" : ObjectId("5d6956119a916c22f2b6db38"),
"firstName" : "Grace",
"lastName" : "Hopper",
"ssn" : "901-01-0001",
"dob" : ISODate("1989-12-13T00:00:00Z"),
"address" : {
"street" : "123 Main Street",
"city" : "Omaha",
"state" : "Nebraska",
"zip" : "90210"
},
"contact" : {
"mobile" : "202-555-1212",
"email" : "grace@example.com"
}
}
{
"_id" : ObjectId("5d6956119a916c22f2b6db3a"),
"firstName" : "Alan",
"lastName" : "Turing",
"ssn" : "901-01-0002",
"dob" : ISODate("1912-06-23T00:00:00Z"),
"address" : {
"street" : "123 Oak Lane",
"city" : "Cleveland",
"state" : "Ohio",
"zip" : "90210"
},
"contact" : {
"mobile" : "202-555-1234",
"email" : "alan@example.net"
}
}
Demo complete.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment