Symmetric encryption
Symmetric encryption is a type of encryption where the same key is used for both encryption and decryption. We will cover the basics of encryption, how to implement it using Node.js, and how to ensure the security of your encrypted data.
Introduction to Symmetric Encryption
graph TD;
symmetricEncryption["fa:fa-lock Symmetric Encryption"]:::mainNode
useCasesSymmetric["fa:fa-key single key for encryption and decryption"]:::useCaseNode
aesGcm["fa:fa-key AES-256-GCM"]:::overviewNode
symmetricEncryption --> useCasesSymmetric
symmetricEncryption --> aesGcm
subgraph useCases["Use Cases"]
securingData["fa:fa-database Securing Data at Rest"]:::dataNode
protectingData["fa:fa-bus Protecting Data in Transit"]:::transitNode
encryptingInfo["fa:fa-lock Encrypting Sensitive Information"]:::infoNode
end
useCasesSymmetric --> securingData
useCasesSymmetric --> protectingData
useCasesSymmetric --> encryptingInfo
classDef mainNode fill:#ffcccc,stroke:#333,stroke-width:2px;
classDef useCaseNode fill:#ccffcc,stroke:#333,stroke-width:2px;
classDef overviewNode fill:#ccccff,stroke:#333,stroke-width:2px;
classDef useCaseSubgraph fill:#f9f9f9,stroke:#333,stroke-width:2px;
classDef dataNode fill:#ffecb3,stroke:#333,stroke-width:2px;
classDef transitNode fill:#c3e6cb,stroke:#333,stroke-width:2px;
classDef infoNode fill:#b3e5fc,stroke:#333,stroke-width:2px;
Symmetric encryption is a method of encryption where a single key is used to both encrypt and decrypt data. This means that both the sender and receiver must have access to the same key. Symmetric encryption is generally faster and less complex than asymmetric encryption, making it suitable for encrypting large amounts of data.
Use Cases of Symmetric Encryption Symmetric encryption is commonly used in various scenarios, including:
- Securing data at rest (e.g., encrypting files on disk)
- Protecting data in transit (e.g., securing network communication)
- Encrypting sensitive information in databases
AES-256-GCM AES (Advanced Encryption Standard) is a widely used symmetric encryption algorithm. The AES-256-GCM (Galois/Counter Mode) variant provides both encryption and authentication, ensuring data confidentiality and integrity.
Writing the Encryption Code
To implement symmetric encryption in Node.js, we need to create functions for encrypting and decrypting data using the AES-256-GCM algorithm. We will use the crypto
module, which provides cryptographic functionality.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const crypto = require('crypto');
function encryptSymmetric(key, plaintext) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key, 'base64'), iv);
let ciphertext = cipher.update(plaintext, 'utf8', 'base64');
ciphertext += cipher.final('base64');
const tag = cipher.getAuthTag().toString('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag
};
}
function decryptSymmetric(key, ciphertext, iv, tag) {
const decipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'));
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let plaintext = decipher.update(ciphertext, 'base64', 'utf8');
plaintext += decipher.final('utf8');
return plaintext;
}
encryptSymmetric
graph TD
subgraph input["fa:fa-key Input"]
plaintext["fa:fa-file-alt Plaintext"]:::inputNode
key["fa:fa-key Key"]:::inputNode
end
input --> encryptSymmetric["fa:fa-lock Encryption (AES-256-GCM)"]:::encryptionNode
subgraph encryptSymmetric["fa:fa-lock Encryption (AES-256-GCM)"]
iv["fa:fa-random Initialization Vector (IV)"]:::processNode --> cipher["fa:fa-lock Cipher"]:::processNode
end
encryptSymmetric --> ciphertext["fa:fa-file-code Ciphertext"]:::outputNode
encryptSymmetric --> authTag["fa:fa-tag Authentication Tag"]:::outputNode
encryptSymmetric --> ivOutput["fa:fa-random Initialization Vector (IV)"]:::outputNode
subgraph output["fa:fa-file-export Output"]
ciphertext
authTag
ivOutput
end
classDef inputNode fill:#ffcccc,stroke:#333,stroke-width:2px;
classDef encryptionNode fill:#e6f2ff,stroke:#0066cc,stroke-width:2px;
classDef processNode fill:#ccffcc,stroke:#333,stroke-width:2px;
classDef outputNode fill:#ccf,stroke:#333,stroke-width:2px;
Encryption Process: A Simplified Explanation
- Input:
- Plaintext: This is your original data—the information you want to protect. It could be a text message, a document, or any digital data.
- Key: This is your secret password. It’s a string of characters or numbers that you and the recipient of the encrypted data must both know.
- Initialization Vector (IV): This is a random value generated for each encryption operation. It adds an extra layer of security to prevent identical plaintexts from resulting in the same ciphertext.
- Encryption (AES-256-GCM):
- The encryption algorithm (AES-256-GCM) is like a powerful machine. It takes the plaintext, key, and IV as input.
- Inside the encryption “machine,” these inputs are combined through a complex mathematical process. This process scrambles the plaintext in a way that’s impossible to understand without the correct key and IV.
- Output:
- Ciphertext: The result of encryption is the ciphertext. This is the transformed version of your original data. It is unreadable without the key and IV.
- Authentication Tag: This is a unique code generated during the encryption process. It’s like a digital signature that ensures the ciphertext hasn’t been tampered with.
decryptSymmetric
graph TD
subgraph input["fa:fa-key Input"]
ciphertext["fa:fa-file-code Ciphertext"]:::inputNode
key["fa:fa-key Key"]:::inputNode
ivInput["fa:fa-random Initialization Vector (IV)"]:::inputNode
authTag["fa:fa-tag Authentication Tag"]:::inputNode
end
input --> decryptSymmetric["fa:fa-unlock Decryption (AES-256-GCM)"]:::decryptionNode
subgraph decryptSymmetric["fa:fa-unlock Decryption (AES-256-GCM)"]
decipher["fa:fa-unlock Decipher"]:::processNode
decipher --> plaintext1["fa:fa-file-alt Plaintext"]:::processNode
end
decryptSymmetric --> plaintext["fa:fa-file-alt Plaintext"]:::outputNode
subgraph output["fa:fa-file-import Output"]
plaintext
end
classDef inputNode fill:#ffcccc,stroke:#333,stroke-width:2px;
classDef decryptionNode fill:#e6f2ff,stroke:#0066cc,stroke-width:2px;
classDef processNode fill:#ccffcc,stroke:#333,stroke-width:2px;
classDef outputNode fill:#ccf,stroke:#333,stroke-width:2px;
- Input:
- Ciphertext: The encrypted data that needs to be decrypted.
- Key: The secret key used for both encryption and decryption.
- Initialization Vector (IV): The IV used during encryption, needed for decryption.
- Authentication Tag: The tag used to verify the integrity and authenticity of the data.
- Decryption Process (AES-256-GCM):
- Decipher: The process of transforming ciphertext back into plaintext using the AES-256-GCM algorithm.
- Plaintext: The original data after decryption.
- Output:
- Plaintext: The result of the decryption process.
Security Considerations
Key Management
Proper key management is crucial for the security of your encrypted data. Store keys securely and avoid hardcoding them in your source code.
Using a Strong IV
Always use a strong and random initialization vector (IV) for each encryption operation. Reusing an IV can compromise the security of your encrypted data.
Handling Sensitive Data
Ensure that sensitive data, such as keys and plaintext, are securely handled in your application. Avoid logging sensitive information and use secure memory management practices.
Full Source Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const crypto = require('crypto');
function encryptSymmetric(key, plaintext) {
// Generate a random initialization vector (IV)
const iv = crypto.randomBytes(12);
// Create a Cipher object with the AES algorithm, key, and IV
const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key, 'base64'), iv);
// Encrypt the plaintext
let ciphertext = cipher.update(plaintext, 'utf8', 'base64');
ciphertext += cipher.final('base64');
// Retrieve the authentication tag
const tag = cipher.getAuthTag().toString('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag
};
}
function decryptSymmetric(key, ciphertext, iv, tag) {
// Create a Decipher object with the same algorithm, key, and IV
const decipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(key, 'base64'), Buffer.from(iv, 'base64'));
// Set the authentication tag
decipher.setAuthTag(Buffer.from(tag, 'base64'));
// Decrypt the ciphertext
let plaintext = decipher.update(ciphertext, 'base64', 'utf8');
plaintext += decipher.final('utf8');
return plaintext;
}
// Example Usage
const originalText = 'This is a secret message.';
const key = crypto.randomBytes(32).toString('base64');
const { ciphertext, iv, tag } = encryptSymmetric(key, originalText);
console.log('Ciphertext:', ciphertext);
console.log('IV:', iv);
console.log('Tag:', tag);
const decryptedText = decryptSymmetric(key, ciphertext, iv, tag);
console.log('Decrypted Text:', decryptedText);
Keywords To Remember
graph
subgraph " "
AES["fa:fa-lock AES-256-GCM"]:::componentNode2
plaintext["fa:fa-file-alt Plaintext"]:::componentNode2
ciphertext["fa:fa-file-code Ciphertext"]:::componentNode2
end
subgraph " "
cryptoModule["fa:fa-b Crypto Module"]:::componentNode
key["fa:fa-key key"]:::componentNode
IV["fa:fa-random Initialization Vector (IV)"]:::componentNode
authTag["fa:fa-tag Authentication Tag"]:::componentNode
ciphertext["fa:fa-file-code Ciphertext"]:::componentNode
end
subgraph " "
dataAtRest["fa:fa-database Data at Rest"]:::stateNode
dataInTransit["fa:fa-bus Data in Transit"]:::stateNode
end
classDef componentNode fill:#e6f2ff,stroke:#0066cc,stroke-width:2px;
classDef componentNode2 fill:#eeffaa,stroke:#eeeeaa,stroke-width:2px;
classDef stateNode fill:#ccffcc,stroke:#333,stroke-width:2px;