@memberjunction/encryption 0.0.1 → 2.130.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +391 -28
- package/dist/EncryptionEngine.d.ts +351 -0
- package/dist/EncryptionEngine.d.ts.map +1 -0
- package/dist/EncryptionEngine.js +683 -0
- package/dist/EncryptionEngine.js.map +1 -0
- package/dist/EncryptionKeySourceBase.d.ts +203 -0
- package/dist/EncryptionKeySourceBase.d.ts.map +1 -0
- package/dist/EncryptionKeySourceBase.js +133 -0
- package/dist/EncryptionKeySourceBase.js.map +1 -0
- package/dist/actions/EnableFieldEncryptionAction.d.ts +87 -0
- package/dist/actions/EnableFieldEncryptionAction.d.ts.map +1 -0
- package/dist/actions/EnableFieldEncryptionAction.js +308 -0
- package/dist/actions/EnableFieldEncryptionAction.js.map +1 -0
- package/dist/actions/RotateEncryptionKeyAction.d.ts +79 -0
- package/dist/actions/RotateEncryptionKeyAction.d.ts.map +1 -0
- package/dist/actions/RotateEncryptionKeyAction.js +343 -0
- package/dist/actions/RotateEncryptionKeyAction.js.map +1 -0
- package/dist/actions/index.d.ts +12 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +17 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +216 -0
- package/dist/interfaces.d.ts.map +1 -0
- package/dist/interfaces.js +15 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/providers/AWSKMSKeySource.d.ts +110 -0
- package/dist/providers/AWSKMSKeySource.d.ts.map +1 -0
- package/dist/providers/AWSKMSKeySource.js +245 -0
- package/dist/providers/AWSKMSKeySource.js.map +1 -0
- package/dist/providers/AzureKeyVaultKeySource.d.ts +109 -0
- package/dist/providers/AzureKeyVaultKeySource.d.ts.map +1 -0
- package/dist/providers/AzureKeyVaultKeySource.js +268 -0
- package/dist/providers/AzureKeyVaultKeySource.js.map +1 -0
- package/dist/providers/ConfigFileKeySource.d.ts +173 -0
- package/dist/providers/ConfigFileKeySource.d.ts.map +1 -0
- package/dist/providers/ConfigFileKeySource.js +310 -0
- package/dist/providers/ConfigFileKeySource.js.map +1 -0
- package/dist/providers/EnvVarKeySource.d.ts +152 -0
- package/dist/providers/EnvVarKeySource.d.ts.map +1 -0
- package/dist/providers/EnvVarKeySource.js +251 -0
- package/dist/providers/EnvVarKeySource.js.map +1 -0
- package/package.json +65 -6
package/README.md
CHANGED
|
@@ -1,45 +1,408 @@
|
|
|
1
1
|
# @memberjunction/encryption
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Comprehensive and general purpose encryption package. Used for field-level encryption for MemberJunction entities. Field-level encryption provides transparent encrypt-on-save and decrypt-on-load operations, configurable per field via entity metadata. This package can be used for any other use-cases where encryption/decryption is required.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **AES-256-GCM Encryption** - Industry-standard authenticated encryption (AEAD) that prevents tampering
|
|
8
|
+
- **Pluggable Key Sources** - Environment variables, config files, or custom providers (vault services, cloud KMS)
|
|
9
|
+
- **Declarative Configuration** - Enable encryption via EntityField metadata without code changes
|
|
10
|
+
- **Transparent Operation** - Automatic encryption on save, decryption on load
|
|
11
|
+
- **Key Rotation Support** - Full re-encryption with transactional safety
|
|
12
|
+
- **Secure Defaults** - API responses hide encrypted fields by default
|
|
8
13
|
|
|
9
|
-
##
|
|
14
|
+
## Installation
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
16
|
+
```bash
|
|
17
|
+
npm install @memberjunction/encryption
|
|
18
|
+
```
|
|
15
19
|
|
|
16
|
-
##
|
|
20
|
+
## Quick Start
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
### 1. Set Up Encryption Key
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
Create a 256-bit (32 byte) encryption key:
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
```bash
|
|
27
|
+
# Generate a secure key
|
|
28
|
+
openssl rand -base64 32
|
|
29
|
+
```
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
31
|
+
Store it in an environment variable:
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
```bash
|
|
34
|
+
export MJ_ENCRYPTION_KEY_PII=your-base64-key-here
|
|
35
|
+
```
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
37
|
+
### 2. Configure the Encryption Key in Database
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
Run the migration to create encryption infrastructure, then register your key:
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
```sql
|
|
42
|
+
-- Insert your encryption key (after running the migration)
|
|
43
|
+
INSERT INTO [${flyway:defaultSchema}].[EncryptionKey] (
|
|
44
|
+
ID, Name, Description, EncryptionKeySourceID, EncryptionAlgorithmID,
|
|
45
|
+
KeyLookupValue, KeyVersion, Marker, IsActive, Status, ActivatedAt
|
|
46
|
+
)
|
|
47
|
+
VALUES (
|
|
48
|
+
NEWID(),
|
|
49
|
+
'PII Master Key',
|
|
50
|
+
'Encryption key for personally identifiable information',
|
|
51
|
+
'38A961D2-022B-49C2-919F-1825A0E9C6F9', -- EnvVarKeySource
|
|
52
|
+
'B2E88E95-D09B-4DA6-B0AE-511B21B70952', -- AES-256-GCM
|
|
53
|
+
'MJ_ENCRYPTION_KEY_PII',
|
|
54
|
+
'1',
|
|
55
|
+
'$ENC$',
|
|
56
|
+
1,
|
|
57
|
+
'Active',
|
|
58
|
+
SYSDATETIMEOFFSET()
|
|
59
|
+
);
|
|
60
|
+
```
|
|
42
61
|
|
|
43
|
-
|
|
62
|
+
### 3. Enable Encryption on Entity Fields
|
|
44
63
|
|
|
45
|
-
|
|
64
|
+
Update the EntityField metadata to enable encryption:
|
|
65
|
+
|
|
66
|
+
```sql
|
|
67
|
+
UPDATE [${flyway:defaultSchema}].[EntityField]
|
|
68
|
+
SET Encrypt = 1,
|
|
69
|
+
EncryptionKeyID = 'your-key-id-here',
|
|
70
|
+
AllowDecryptInAPI = 0, -- Secure default: don't send plaintext to clients
|
|
71
|
+
SendEncryptedValue = 0 -- Secure default: send null instead of ciphertext
|
|
72
|
+
WHERE Entity = 'Contacts'
|
|
73
|
+
AND Name IN ('SSN', 'TaxID', 'BankAccountNumber');
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Encrypt Existing Data
|
|
77
|
+
|
|
78
|
+
After enabling encryption on a field, run the EnableFieldEncryption action:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { EnableFieldEncryptionAction } from '@memberjunction/encryption';
|
|
82
|
+
|
|
83
|
+
const action = new EnableFieldEncryptionAction();
|
|
84
|
+
const result = await action.Run({
|
|
85
|
+
Params: [
|
|
86
|
+
{ Name: 'EntityFieldID', Value: 'field-uuid-here' },
|
|
87
|
+
{ Name: 'BatchSize', Value: 100 }
|
|
88
|
+
],
|
|
89
|
+
ContextUser: currentUser
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Response Behavior
|
|
94
|
+
|
|
95
|
+
The encryption system provides secure-by-default API responses:
|
|
96
|
+
|
|
97
|
+
| AllowDecryptInAPI | SendEncryptedValue | API Response |
|
|
98
|
+
|-------------------|-------------------|--------------|
|
|
99
|
+
| true | N/A | Decrypted plaintext |
|
|
100
|
+
| false | true | Encrypted ciphertext ($ENC$...) |
|
|
101
|
+
| false | false | NULL (most secure, **default**) |
|
|
102
|
+
|
|
103
|
+
## Key Source Providers
|
|
104
|
+
|
|
105
|
+
### Environment Variable (Default)
|
|
106
|
+
|
|
107
|
+
The simplest option - store keys in environment variables:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Generate a 256-bit key
|
|
111
|
+
openssl rand -base64 32
|
|
112
|
+
|
|
113
|
+
# Set in environment
|
|
114
|
+
export MJ_ENCRYPTION_KEY_PII=your-base64-key-here
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Database configuration:
|
|
118
|
+
- **EncryptionKeySourceID**: `38A961D2-022B-49C2-919F-1825A0E9C6F9`
|
|
119
|
+
- **KeyLookupValue**: Environment variable name (e.g., `MJ_ENCRYPTION_KEY_PII`)
|
|
120
|
+
|
|
121
|
+
### Configuration File
|
|
122
|
+
|
|
123
|
+
Store keys in `mj.config.cjs` (not recommended for production):
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
module.exports = {
|
|
127
|
+
encryptionKeys: {
|
|
128
|
+
pii_master_key: 'base64-encoded-key-here'
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Database configuration:
|
|
134
|
+
- **EncryptionKeySourceID**: `CBF9632D-EF05-42E2-82F6-5BAC79FAA565`
|
|
135
|
+
- **KeyLookupValue**: Key name in config (e.g., `pii_master_key`)
|
|
136
|
+
|
|
137
|
+
### AWS KMS
|
|
138
|
+
|
|
139
|
+
Uses AWS Key Management Service with envelope encryption. Install the optional dependency:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install @aws-sdk/client-kms
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Setup:**
|
|
146
|
+
|
|
147
|
+
1. Create a symmetric CMK in AWS KMS
|
|
148
|
+
2. Generate a data key:
|
|
149
|
+
```bash
|
|
150
|
+
aws kms generate-data-key \
|
|
151
|
+
--key-id alias/your-cmk-alias \
|
|
152
|
+
--key-spec AES_256 \
|
|
153
|
+
--query 'CiphertextBlob' \
|
|
154
|
+
--output text
|
|
155
|
+
```
|
|
156
|
+
3. Store the output (base64 CiphertextBlob) as the KeyLookupValue
|
|
157
|
+
|
|
158
|
+
**Authentication:** Uses the standard AWS credential chain:
|
|
159
|
+
- Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
|
|
160
|
+
- IAM role (on EC2, ECS, Lambda)
|
|
161
|
+
- Shared credentials file
|
|
162
|
+
|
|
163
|
+
Database configuration:
|
|
164
|
+
- **EncryptionKeySourceID**: `D8E4F521-3A7B-4C9E-8F12-6B5A4C3D2E1F`
|
|
165
|
+
- **KeyLookupValue**: Base64-encoded CiphertextBlob from GenerateDataKey
|
|
166
|
+
|
|
167
|
+
```sql
|
|
168
|
+
INSERT INTO [${flyway:defaultSchema}].[EncryptionKey] (
|
|
169
|
+
ID, Name, EncryptionKeySourceID, EncryptionAlgorithmID,
|
|
170
|
+
KeyLookupValue, IsActive, Status
|
|
171
|
+
)
|
|
172
|
+
VALUES (
|
|
173
|
+
NEWID(),
|
|
174
|
+
'AWS KMS PII Key',
|
|
175
|
+
'D8E4F521-3A7B-4C9E-8F12-6B5A4C3D2E1F', -- AWS KMS
|
|
176
|
+
'B2E88E95-D09B-4DA6-B0AE-511B21B70952', -- AES-256-GCM
|
|
177
|
+
'AQIDAHh...base64-ciphertext-blob...', -- From GenerateDataKey
|
|
178
|
+
1,
|
|
179
|
+
'Active'
|
|
180
|
+
);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Azure Key Vault
|
|
184
|
+
|
|
185
|
+
Retrieves keys from Azure Key Vault secrets. Install the optional dependencies:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npm install @azure/keyvault-secrets @azure/identity
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Setup:**
|
|
192
|
+
|
|
193
|
+
1. Create an Azure Key Vault
|
|
194
|
+
2. Create a secret containing your base64-encoded key:
|
|
195
|
+
```bash
|
|
196
|
+
# Generate key
|
|
197
|
+
KEY=$(openssl rand -base64 32)
|
|
198
|
+
|
|
199
|
+
# Store in Key Vault
|
|
200
|
+
az keyvault secret set \
|
|
201
|
+
--vault-name your-vault-name \
|
|
202
|
+
--name mj-encryption-key \
|
|
203
|
+
--value "$KEY"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Authentication:** Uses DefaultAzureCredential:
|
|
207
|
+
- Managed Identity (on Azure VMs, App Service, Functions)
|
|
208
|
+
- Service principal (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`)
|
|
209
|
+
- Azure CLI credentials
|
|
210
|
+
|
|
211
|
+
Database configuration:
|
|
212
|
+
- **EncryptionKeySourceID**: `A2B3C4D5-E6F7-8901-2345-6789ABCDEF01`
|
|
213
|
+
- **KeyLookupValue**: Full secret URL or secret name (if `AZURE_KEYVAULT_URL` is set)
|
|
214
|
+
|
|
215
|
+
```sql
|
|
216
|
+
INSERT INTO [${flyway:defaultSchema}].[EncryptionKey] (
|
|
217
|
+
ID, Name, EncryptionKeySourceID, EncryptionAlgorithmID,
|
|
218
|
+
KeyLookupValue, IsActive, Status
|
|
219
|
+
)
|
|
220
|
+
VALUES (
|
|
221
|
+
NEWID(),
|
|
222
|
+
'Azure Key Vault PII Key',
|
|
223
|
+
'A2B3C4D5-E6F7-8901-2345-6789ABCDEF01', -- Azure Key Vault
|
|
224
|
+
'B2E88E95-D09B-4DA6-B0AE-511B21B70952', -- AES-256-GCM
|
|
225
|
+
'https://your-vault.vault.azure.net/secrets/mj-encryption-key',
|
|
226
|
+
1,
|
|
227
|
+
'Active'
|
|
228
|
+
);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Tip:** Set `AZURE_KEYVAULT_URL` to use short secret names:
|
|
232
|
+
```bash
|
|
233
|
+
export AZURE_KEYVAULT_URL=https://your-vault.vault.azure.net
|
|
234
|
+
# Then KeyLookupValue can just be: mj-encryption-key
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Custom Provider
|
|
238
|
+
|
|
239
|
+
Extend `EncryptionKeySourceBase` for other vault services:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
243
|
+
import { EncryptionKeySourceBase } from '@memberjunction/encryption';
|
|
244
|
+
|
|
245
|
+
@RegisterClass(EncryptionKeySourceBase, 'HashiCorpVaultKeySource')
|
|
246
|
+
export class HashiCorpVaultKeySource extends EncryptionKeySourceBase {
|
|
247
|
+
get SourceName(): string { return 'HashiCorp Vault'; }
|
|
248
|
+
|
|
249
|
+
ValidateConfiguration(): boolean {
|
|
250
|
+
return !!process.env.VAULT_ADDR && !!process.env.VAULT_TOKEN;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async GetKey(lookupValue: string): Promise<Buffer> {
|
|
254
|
+
// Implement vault API call to retrieve secret
|
|
255
|
+
// Return the key as a Buffer
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async KeyExists(lookupValue: string): Promise<boolean> {
|
|
259
|
+
// Check if secret exists at path
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Key Rotation
|
|
265
|
+
|
|
266
|
+
Rotate keys without downtime using the RotateEncryptionKey action:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { RotateEncryptionKeyAction } from '@memberjunction/encryption';
|
|
270
|
+
|
|
271
|
+
// 1. Deploy new key to environment
|
|
272
|
+
// export MJ_ENCRYPTION_KEY_PII_V2=new-base64-key-here
|
|
273
|
+
|
|
274
|
+
// 2. Run rotation
|
|
275
|
+
const action = new RotateEncryptionKeyAction();
|
|
276
|
+
const result = await action.Run({
|
|
277
|
+
Params: [
|
|
278
|
+
{ Name: 'EncryptionKeyID', Value: 'existing-key-uuid' },
|
|
279
|
+
{ Name: 'NewKeyLookupValue', Value: 'MJ_ENCRYPTION_KEY_PII_V2' },
|
|
280
|
+
{ Name: 'BatchSize', Value: 100 }
|
|
281
|
+
],
|
|
282
|
+
ContextUser: currentUser
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// 3. After rotation, update environment to use new key
|
|
286
|
+
// export MJ_ENCRYPTION_KEY_PII=new-key-value
|
|
287
|
+
// Remove MJ_ENCRYPTION_KEY_PII_V2
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Programmatic API
|
|
291
|
+
|
|
292
|
+
### EncryptionEngine
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { EncryptionEngine } from '@memberjunction/encryption';
|
|
296
|
+
|
|
297
|
+
const engine = EncryptionEngine.Instance;
|
|
298
|
+
|
|
299
|
+
// Encrypt a value
|
|
300
|
+
const encrypted = await engine.Encrypt(
|
|
301
|
+
'sensitive-data',
|
|
302
|
+
encryptionKeyId,
|
|
303
|
+
contextUser
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// Decrypt a value
|
|
307
|
+
const decrypted = await engine.Decrypt(encrypted, contextUser);
|
|
308
|
+
|
|
309
|
+
// Check if a value is encrypted
|
|
310
|
+
if (engine.IsEncrypted(someValue)) {
|
|
311
|
+
const parts = engine.ParseEncryptedValue(someValue);
|
|
312
|
+
console.log(`Encrypted with key: ${parts.keyId}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Clear caches (after key rotation)
|
|
316
|
+
engine.ClearCaches();
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Encrypted Value Format
|
|
320
|
+
|
|
321
|
+
Encrypted values are stored as self-describing strings:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
$ENC$<keyId>$<algorithm>$<iv>$<ciphertext>$<authTag>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
```
|
|
329
|
+
$ENC$550e8400-e29b-41d4-a716-446655440000$AES-256-GCM$Base64IV$Base64Ciphertext$Base64AuthTag
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
This format allows:
|
|
333
|
+
- Quick detection of encrypted values
|
|
334
|
+
- Identification of which key was used
|
|
335
|
+
- Algorithm-agnostic decryption
|
|
336
|
+
- Future-proof key rotation
|
|
337
|
+
|
|
338
|
+
## Security Considerations
|
|
339
|
+
|
|
340
|
+
1. **Key Management**
|
|
341
|
+
- Never store keys in the database
|
|
342
|
+
- Use environment variables or secure vault services
|
|
343
|
+
- Rotate keys regularly (recommended: annually)
|
|
344
|
+
- Generate keys with `openssl rand -base64 32`
|
|
345
|
+
|
|
346
|
+
2. **Authenticated Encryption**
|
|
347
|
+
- AES-256-GCM provides both confidentiality and integrity
|
|
348
|
+
- Auth tag prevents tampering with ciphertext
|
|
349
|
+
- Random IVs prevent pattern analysis
|
|
350
|
+
|
|
351
|
+
3. **API Security**
|
|
352
|
+
- Default: encrypted fields return `null` to clients
|
|
353
|
+
- Explicitly enable `AllowDecryptInAPI` only when needed
|
|
354
|
+
- Consider using `SendEncryptedValue` for client-side decryption scenarios
|
|
355
|
+
|
|
356
|
+
4. **Key Rotation**
|
|
357
|
+
- Plan for rotation before key compromise
|
|
358
|
+
- Test rotation in staging environment first
|
|
359
|
+
- Monitor rotation progress for large datasets
|
|
360
|
+
- Keep old keys accessible until rotation completes
|
|
361
|
+
|
|
362
|
+
## Database Schema
|
|
363
|
+
|
|
364
|
+
The encryption infrastructure includes three new tables:
|
|
365
|
+
|
|
366
|
+
- **MJ: Encryption Key Sources** - Where keys come from (env vars, config, vaults)
|
|
367
|
+
- **MJ: Encryption Algorithms** - Available algorithms (AES-256-GCM, etc.)
|
|
368
|
+
- **MJ: Encryption Keys** - Configured keys linking sources and algorithms
|
|
369
|
+
|
|
370
|
+
EntityField extensions:
|
|
371
|
+
- **Encrypt** - Enable encryption for this field
|
|
372
|
+
- **EncryptionKeyID** - Which key to use
|
|
373
|
+
- **AllowDecryptInAPI** - Whether to decrypt in API responses
|
|
374
|
+
- **SendEncryptedValue** - Send ciphertext when decryption not allowed
|
|
375
|
+
|
|
376
|
+
## Performance
|
|
377
|
+
|
|
378
|
+
- Key configurations are cached with 5-minute TTL
|
|
379
|
+
- Key material is cached with 5-minute TTL
|
|
380
|
+
- Encryption/decryption uses Node.js native crypto (fast)
|
|
381
|
+
- Batch processing for key rotation and initial encryption
|
|
382
|
+
- Lazy loading - encryption engine only activated when needed
|
|
383
|
+
|
|
384
|
+
## Troubleshooting
|
|
385
|
+
|
|
386
|
+
### "Encryption key not found"
|
|
387
|
+
- Check that the key exists in `MJ: Encryption Keys` table
|
|
388
|
+
- Verify `IsActive = 1` and `Status = 'Active'`
|
|
389
|
+
- Check that the referenced algorithm and source are also active
|
|
390
|
+
|
|
391
|
+
### "Key length mismatch"
|
|
392
|
+
- Ensure your key is exactly 32 bytes (256 bits) for AES-256
|
|
393
|
+
- Generate with: `openssl rand -base64 32`
|
|
394
|
+
- The base64 string should be ~44 characters
|
|
395
|
+
|
|
396
|
+
### "Failed to decrypt"
|
|
397
|
+
- The key may have been rotated - check KeyVersion
|
|
398
|
+
- The data may be corrupted
|
|
399
|
+
- Auth tag mismatch indicates tampering
|
|
400
|
+
|
|
401
|
+
### API returns null for encrypted fields
|
|
402
|
+
- Check `AllowDecryptInAPI` flag on the EntityField
|
|
403
|
+
- Default is `false` for security
|
|
404
|
+
- Update to `true` if API clients need plaintext
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
ISC
|