Skip to content

Commit 31a8d3b

Browse files
authored
PHPLIB-1176: Various improvements for In-Use Encryption tutorial (#1122)
Adds additional non-enterprise examples from the PyMongo tutorial: "Explicit Encryption with Automatic Decryption" and "Explicit Queryable Encryption". Examples are now broken out into separate files, which are tested in ExamplesTest. Renames "Client-Side Encryption" to "In-Use Encryption" (PHPLIB-997). This will warrant adding a redirect from "/tutorial/client-side-encryption/" to "/tutorial/encryption/" in the related docs-php-library project (DOCSP-31241). Adds docs for crypt_shared and mongocryptd (PHPLIB-985). Demonstrates schema validation errors in example scripts, and clarify that local schemas should be used in conjunction with server-side schemas (not instead of). Create key vault index in key management scripts and remove setup code elsewhere. This replaces the setup code with a top-of-script comment to avoid repetition. ExamplesTest now prepares the cleans up the necessary collections. Add a documentation note about creating the partial, unique index on keyAltNames.
1 parent c1e3a33 commit 31a8d3b

15 files changed

+1172
-381
lines changed

docs/examples/create_data_key.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
7+
require __DIR__ . '/../../vendor/autoload.php';
8+
9+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
10+
11+
// Generate a secure local key to use for this script
12+
$localKey = new Binary(random_bytes(96));
13+
14+
// Create a client with no encryption options
15+
$client = new Client($uri);
16+
17+
/* Prepare the database for this script. Drop the key vault collection and
18+
* ensure it has a unique index for keyAltNames. This would typically be done
19+
* during application deployment. */
20+
$client->selectCollection('encryption', '__keyVault')->drop();
21+
$client->selectCollection('encryption', '__keyVault')->createIndex(['keyAltNames' => 1], [
22+
'unique' => true,
23+
'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]],
24+
]);
25+
26+
// Create a ClientEncryption object to manage data encryption keys
27+
$clientEncryption = $client->createClientEncryption([
28+
'keyVaultNamespace' => 'encryption.__keyVault',
29+
'kmsProviders' => [
30+
'local' => ['key' => $localKey],
31+
],
32+
]);
33+
34+
/* Create a data encryption key. To store the key ID for later use, you can use
35+
* serialize(), var_export(), etc. */
36+
$keyId = $clientEncryption->createDataKey('local');
37+
38+
print_r($keyId);
39+
40+
// Encrypt a value using the key that was just created
41+
$encryptedValue = $clientEncryption->encrypt('mySecret', [
42+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
43+
'keyId' => $keyId,
44+
]);
45+
46+
print_r($encryptedValue);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
use MongoDB\Driver\Exception\ServerException;
7+
8+
require __DIR__ . '/../../vendor/autoload.php';
9+
10+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
11+
12+
/* Note: this script assumes that the test database is empty and that the key
13+
* vault collection exists and has a partial, unique index on keyAltNames (as
14+
* demonstrated in the encryption key management scripts). */
15+
16+
// Generate a secure local key to use for this script
17+
$localKey = new Binary(random_bytes(96));
18+
19+
// Create a client with no encryption options
20+
$client = new Client($uri);
21+
22+
// Create a ClientEncryption object to manage data encryption keys
23+
$clientEncryption = $client->createClientEncryption([
24+
'keyVaultNamespace' => 'encryption.__keyVault',
25+
'kmsProviders' => [
26+
'local' => ['key' => $localKey],
27+
],
28+
]);
29+
30+
/* Create a data encryption key. Alternatively, this key ID could be read from a
31+
* configuration file. */
32+
$keyId = $clientEncryption->createDataKey('local');
33+
34+
/* Define a JSON schema for the encrypted collection. Since this only utilizes
35+
* encryption schema syntax, it can be used for both the server-side and local
36+
* schema. */
37+
$schema = [
38+
'bsonType' => 'object',
39+
'properties' => [
40+
'encryptedField' => [
41+
'encrypt' => [
42+
'keyId' => [$keyId],
43+
'bsonType' => 'string',
44+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
45+
],
46+
],
47+
],
48+
];
49+
50+
/* Create another client with automatic encryption enabled. Configure a local
51+
* schema for the encrypted collection using the "schemaMap" option. */
52+
$encryptedClient = new Client($uri, [], [
53+
'autoEncryption' => [
54+
'keyVaultNamespace' => 'encryption.__keyVault',
55+
'kmsProviders' => ['local' => ['key' => $localKey]],
56+
'schemaMap' => ['test.coll' => $schema],
57+
],
58+
]);
59+
60+
/* Create a new collection for this script. Configure a server-side schema by
61+
* explicitly creating the collection with a "validator" option.
62+
*
63+
* Note: without a server-side schema, another client could potentially insert
64+
* unencrypted data into the collection. Therefore, a local schema should always
65+
* be used in conjunction with a server-side schema. */
66+
$encryptedClient->selectDatabase('test')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
67+
$encryptedCollection = $encryptedClient->selectCollection('test', 'coll');
68+
69+
/* Using the encrypted client, insert and find a document to demonstrate that
70+
* the encrypted field is automatically encrypted and decrypted. */
71+
$encryptedCollection->insertOne(['_id' => 1, 'encryptedField' => 'mySecret']);
72+
73+
print_r($encryptedCollection->findOne(['_id' => 1]));
74+
75+
/* Using the client configured without encryption, find the same document and
76+
* observe that the field is not automatically decrypted. */
77+
$unencryptedCollection = $client->selectCollection('test', 'coll');
78+
79+
print_r($unencryptedCollection->findOne(['_id' => 1]));
80+
81+
/* Attempt to insert another document with an unencrypted field value to
82+
* demonstrate that the server-side schema is enforced. */
83+
try {
84+
$unencryptedCollection->insertOne(['_id' => 2, 'encryptedField' => 'myOtherSecret']);
85+
} catch (ServerException $e) {
86+
printf("Error inserting document: %s\n", $e->getMessage());
87+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
use MongoDB\Driver\Exception\ServerException;
7+
8+
require __DIR__ . '/../../vendor/autoload.php';
9+
10+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
11+
12+
/* Note: this script assumes that the test database is empty and that the key
13+
* vault collection exists and has a partial, unique index on keyAltNames (as
14+
* demonstrated in the encryption key management scripts). */
15+
16+
// Generate a secure local key to use for this script
17+
$localKey = new Binary(random_bytes(96));
18+
19+
// Create a client with no encryption options
20+
$client = new Client($uri);
21+
22+
// Create a ClientEncryption object to manage data encryption keys
23+
$clientEncryption = $client->createClientEncryption([
24+
'keyVaultNamespace' => 'encryption.__keyVault',
25+
'kmsProviders' => [
26+
'local' => ['key' => $localKey],
27+
],
28+
]);
29+
30+
/* Create a data encryption key. Alternatively, this key ID could be read from a
31+
* configuration file. */
32+
$keyId = $clientEncryption->createDataKey('local');
33+
34+
// Create another client with automatic encryption enabled
35+
$encryptedClient = new Client($uri, [], [
36+
'autoEncryption' => [
37+
'keyVaultNamespace' => 'encryption.__keyVault',
38+
'kmsProviders' => ['local' => ['key' => $localKey]],
39+
],
40+
]);
41+
42+
// Define a JSON schema for the encrypted collection
43+
$schema = [
44+
'bsonType' => 'object',
45+
'properties' => [
46+
'encryptedField' => [
47+
'encrypt' => [
48+
'keyId' => [$keyId],
49+
'bsonType' => 'string',
50+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
51+
],
52+
],
53+
],
54+
];
55+
56+
/* Create a new collection for this script. Configure a server-side schema by
57+
* explicitly creating the collection with a "validator" option. */
58+
$encryptedClient->selectDatabase('test')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]);
59+
$encryptedCollection = $encryptedClient->selectCollection('test', 'coll');
60+
61+
/* Using the encrypted client, insert and find a document to demonstrate that
62+
* the encrypted field is automatically encrypted and decrypted. */
63+
$encryptedCollection->insertOne(['_id' => 1, 'encryptedField' => 'mySecret']);
64+
65+
print_r($encryptedCollection->findOne(['_id' => 1]));
66+
67+
/* Using the client configured without encryption, find the same document and
68+
* observe that the field is not automatically decrypted. */
69+
$unencryptedCollection = $client->selectCollection('test', 'coll');
70+
71+
print_r($unencryptedCollection->findOne(['_id' => 1]));
72+
73+
/* Attempt to insert another document with an unencrypted field value to
74+
* demonstrate that the server-side schema is enforced. */
75+
try {
76+
$unencryptedCollection->insertOne(['_id' => 2, 'encryptedField' => 'myOtherSecret']);
77+
} catch (ServerException $e) {
78+
printf("Error inserting document: %s\n", $e->getMessage());
79+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
7+
require __DIR__ . '/../../vendor/autoload.php';
8+
9+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
10+
11+
/* Note: this script assumes that the test database is empty and that the key
12+
* vault collection exists and has a partial, unique index on keyAltNames (as
13+
* demonstrated in the encryption key management scripts). */
14+
15+
// Generate a secure local key to use for this script
16+
$localKey = new Binary(random_bytes(96));
17+
18+
// Create a client with no encryption options
19+
$client = new Client($uri);
20+
21+
// Create a ClientEncryption object to manage data encryption keys
22+
$clientEncryption = $client->createClientEncryption([
23+
'keyVaultNamespace' => 'encryption.__keyVault',
24+
'kmsProviders' => [
25+
'local' => ['key' => $localKey],
26+
],
27+
]);
28+
29+
/* Create a data encryption key. Alternatively, this key ID could be read from a
30+
* configuration file. */
31+
$keyId = $clientEncryption->createDataKey('local');
32+
33+
// Insert a document with a manually encrypted field
34+
$encryptedValue = $clientEncryption->encrypt('mySecret', [
35+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
36+
'keyId' => $keyId,
37+
]);
38+
39+
$collection = $client->selectCollection('test', 'coll');
40+
$collection->insertOne(['_id' => 1, 'encryptedField' => $encryptedValue]);
41+
42+
/* Using the client configured without encryption, find the document and observe
43+
* that the field is not automatically decrypted. */
44+
$document = $collection->findOne();
45+
46+
print_r($document);
47+
48+
// Manually decrypt the field
49+
printf("Decrypted: %s\n", $clientEncryption->decrypt($document->encryptedField));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
7+
require __DIR__ . '/../../vendor/autoload.php';
8+
9+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
10+
11+
/* Note: this script assumes that the test database is empty and that the key
12+
* vault collection exists and has a partial, unique index on keyAltNames (as
13+
* demonstrated in the encryption key management scripts). */
14+
15+
// Generate a secure local key to use for this script
16+
$localKey = new Binary(random_bytes(96));
17+
18+
// Create a client with automatic encryption disabled
19+
$client = new Client($uri, [], [
20+
'autoEncryption' => [
21+
'keyVaultNamespace' => 'encryption.__keyVault',
22+
'kmsProviders' => ['local' => ['key' => $localKey]],
23+
'bypassAutoEncryption' => true,
24+
],
25+
]);
26+
27+
// Create a ClientEncryption object to manage data encryption keys
28+
$clientEncryption = $client->createClientEncryption([
29+
'keyVaultNamespace' => 'encryption.__keyVault',
30+
'kmsProviders' => [
31+
'local' => ['key' => $localKey],
32+
],
33+
]);
34+
35+
/* Create a data encryption key. Alternatively, this key ID could be read from a
36+
* configuration file. */
37+
$keyId = $clientEncryption->createDataKey('local');
38+
39+
// Insert a document with a manually encrypted field
40+
$encryptedValue = $clientEncryption->encrypt('mySecret', [
41+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
42+
'keyId' => $keyId,
43+
]);
44+
45+
$collection = $client->selectCollection('test', 'coll');
46+
$collection->insertOne(['_id' => 1, 'encryptedField' => $encryptedValue]);
47+
48+
/* Using the client configured with encryption (but not automatic encryption),
49+
* find the document and observe that the field is automatically decrypted. */
50+
$document = $collection->findOne();
51+
52+
print_r($document);

docs/examples/key_alt_name.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use MongoDB\BSON\Binary;
4+
use MongoDB\Client;
5+
use MongoDB\Driver\ClientEncryption;
6+
use MongoDB\Driver\Exception\ServerException;
7+
8+
require __DIR__ . '/../../vendor/autoload.php';
9+
10+
$uri = getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/';
11+
12+
// Generate a secure local key to use for this script
13+
$localKey = new Binary(random_bytes(96));
14+
15+
// Create a client with no encryption options
16+
$client = new Client($uri);
17+
18+
/* Prepare the database for this script. Drop the key vault collection and
19+
* ensure it has a unique index for keyAltNames. This would typically be done
20+
* during application deployment. */
21+
$client->selectCollection('encryption', '__keyVault')->drop();
22+
$client->selectCollection('encryption', '__keyVault')->createIndex(['keyAltNames' => 1], [
23+
'unique' => true,
24+
'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]],
25+
]);
26+
27+
// Create a ClientEncryption object to manage data encryption keys
28+
$clientEncryption = $client->createClientEncryption([
29+
'keyVaultNamespace' => 'encryption.__keyVault',
30+
'kmsProviders' => [
31+
'local' => ['key' => $localKey],
32+
],
33+
]);
34+
35+
// Create a data encryption key with an alternate name
36+
$clientEncryption->createDataKey('local', ['keyAltNames' => ['myDataKey']]);
37+
38+
/* Attempt to create a second key with the same name to demonstrate that the
39+
* unique index is enforced. */
40+
try {
41+
$clientEncryption->createDataKey('local', ['keyAltNames' => ['myDataKey']]);
42+
} catch (ServerException $e) {
43+
printf("Error creating key: %s\n", $e->getMessage());
44+
}
45+
46+
// Encrypt a value, using the "keyAltName" option instead of "keyId"
47+
$encryptedValue = $clientEncryption->encrypt('mySecret', [
48+
'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC,
49+
'keyAltName' => 'myDataKey',
50+
]);
51+
52+
print_r($encryptedValue);

0 commit comments

Comments
 (0)