Secure File Handling With PGP Encryption
Secure File Handling With PGP Encryption
Secure file handling is crucial for protecting sensitive information. This article explores a robust approach to file encryption, compression, and handling using Node.js.
PGP (Pretty Good Privacy) encryption is a widely used method for securing digital communications and files. It employs a combination of symmetric and asymmetric encryption to ensure data confidentiality and integrity. In PGP, data is encrypted with a public key and can only be decrypted by the corresponding private key, making it highly secure. It is commonly used for encrypting emails, files, and securing sensitive information. PGPβs strength lies in its ability to ensure that only intended recipients can access the encrypted content.
Overview
This project demonstrates a secure file handling system using Node.js. It includes modules for ZIP file compression, PGP encryption, and secure file handling.
graph TD
subgraph fileProcessing["π’ File Processing"]
style fileProcessing fill:#e6f7ff,stroke:#4d94ff
fileGeneration["π File Generation"]
zipCreation["ποΈ ZIP Creation"]
pgpEncryption["π PGP Encryption"]
end
subgraph fileHandling["π₯ File Handling"]
style fileHandling fill:#e6ffe6,stroke:#4dff4d
pgpDecryption["π PGP Decryption"]
zipExtraction["π ZIP Extraction"]
end
fileGeneration --> zipCreation
zipCreation --> pgpEncryption
pgpEncryption --> pgpDecryption
pgpDecryption --> zipExtraction
pgpPublicKey["π PGP Public Key"]
pgpPrivateKey["ποΈ PGP Private Key"]
pgpPublicKey -->|"Used for encryption"| pgpEncryption
pgpPrivateKey -->|"Used for decryption"| pgpDecryption
Run the project
Souce code is available at GitHub
1
node index.js
Output:
1
2
3
4
5
6
7
8
Output directory cleaned.
Keys loaded from disk.
ZIP file created: output/archive.zip
Encrypted file created: output/encrypted_archive.zip.gpg
Decrypted file created: output/decrypted_archive.zip
Files extracted to: output/extracted_files
Extracted files:
- test.txt
Project Structure
Our secure file handling system is divided into three main files:
zipUtils.js
: Handles ZIP file creation and extractionpgpUtils.js
: Manages PGP encryption and decryptionindex.js
: Orchestrates the entire process
ZIP Utilities (zipUtils.js)
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
const fs = require('fs');
const fsPromises = require('fs').promises;
const path = require('path');
const archiver = require('archiver');
const unzipper = require('unzipper');
// Function to create a ZIP file
async function createZip(files, outputZip) {
await fsPromises.mkdir(path.dirname(outputZip), { recursive: true });
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(outputZip);
const archive = archiver('zip', { zlib: { level: 9 } });
output.on('close', () => resolve(outputZip));
archive.on('error', reject);
archive.pipe(output);
files.forEach(file => archive.file(file, { name: path.basename(file) }));
archive.finalize();
});
}
// Function to extract a ZIP file
async function extractZip(zipFile, outputDir) {
await fsPromises.mkdir(outputDir, { recursive: true });
return new Promise((resolve, reject) => {
fs.createReadStream(zipFile)
.pipe(unzipper.Extract({ path: outputDir }))
.on('close', () => resolve(outputDir))
.on('error', reject);
});
}
module.exports = { createZip, extractZip };
This module provides two main functions:
createZip
: Compresses given files into a ZIP archiveextractZip
: Extracts contents of a ZIP file to a specified directory
These functions use the archiver
and unzipper
libraries to handle ZIP operations efficiently.
PGP Utilities (pgpUtils.js)
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
const fs = require('fs').promises;
const openpgp = require('openpgp');
async function encryptFile(inputFile, outputFile, publicKeyArmored) {
const fileContent = await fs.readFile(inputFile);
const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: fileContent }),
encryptionKeys: publicKey,
format: 'binary'
});
await fs.writeFile(outputFile, encrypted);
return outputFile;
}
async function decryptFile(inputFile, outputFile, privateKeyArmored, passphrase) {
const encryptedData = await fs.readFile(inputFile);
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
passphrase
});
const message = await openpgp.readMessage({ binaryMessage: encryptedData });
const { data: decrypted } = await openpgp.decrypt({
message,
decryptionKeys: privateKey,
format: 'binary'
});
await fs.writeFile(outputFile, decrypted);
return outputFile;
}
module.exports = { encryptFile, decryptFile };
This module provides two essential functions:
encryptFile
: Encrypts a file using PGP public key encryptiondecryptFile
: Decrypts a PGP-encrypted file using a private key and passphrase
These functions utilize the openpgp
library to perform cryptographic operations.
Main Process (index.js)
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const fsPromises = require('fs').promises;
const fs = require('fs');
const openpgp = require('openpgp');
const path = require('path');
const { createZip, extractZip } = require('./zipUtils');
const { encryptFile, decryptFile } = require('./pgpUtils');
async function cleanOutputDir(outputDir) {
try {
await fsPromises.rmdir(outputDir, { recursive: true });
console.log('Output directory cleaned.');
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
}
async function demonstrateSecureFileHandling() {
try {
const keysDir = './keys';
const outputDir = './output';
const inputFilePath = './input/test.txt';
const privateKeyPath = path.join(keysDir, 'privateKey.asc');
const publicKeyPath = path.join(keysDir, 'publicKey.asc');
await cleanOutputDir(outputDir);
await fsPromises.mkdir(keysDir, { recursive: true });
let privateKey, publicKey;
try {
privateKey = await fsPromises.readFile(privateKeyPath, 'utf8');
publicKey = await fsPromises.readFile(publicKeyPath, 'utf8');
console.log('Keys loaded from disk.');
} catch (err) {
const keyPair = await openpgp.generateKey({
type: 'rsa',
rsaBits: 4096,
userIDs: [{ name: 'Test User', email: 'test@example.com' }],
passphrase: 'secure123'
});
privateKey = keyPair.privateKey;
publicKey = keyPair.publicKey;
await fsPromises.writeFile(privateKeyPath, privateKey);
await fsPromises.writeFile(publicKeyPath, publicKey);
console.log('Keys generated and saved to disk.');
}
const zipFile = await createZip([inputFilePath], path.join(outputDir, 'archive.zip'));
console.log(`ZIP file created: ${zipFile}`);
const encryptedFile = await encryptFile(zipFile, path.join(outputDir, 'encrypted_archive.zip.gpg'), publicKey);
console.log(`Encrypted file created: ${encryptedFile}`);
const decryptedFile = await decryptFile(encryptedFile, path.join(outputDir, 'decrypted_archive.zip'), privateKey, 'secure123');
console.log(`Decrypted file created: ${decryptedFile}`);
const extractionDir = path.join(outputDir, 'extracted_files');
await extractZip(decryptedFile, extractionDir);
console.log(`Files extracted to: ${extractionDir}`);
const files = await fsPromises.readdir(extractionDir);
console.log('Extracted files:');
files.forEach(file => console.log(`- ${file}`));
} catch (error) {
console.error('An error occurred:', error.message);
}
}
demonstrateSecureFileHandling();
This main script ties everything together and demonstrates the complete secure file handling process:
- Setup: It creates necessary directories and manages PGP keys.
- Key Management: It either loads existing PGP keys or generates new ones if not found.
- File Processing:
- Creates a ZIP archive of the input file
- Encrypts the ZIP file using PGP
- Decrypts the encrypted file
- Extracts the contents of the decrypted ZIP file
- Verification: Lists the extracted files to confirm successful processing
Considerations
To use this system in a production environment, consider implementing additional security measures such as secure key storage, regular key rotation, and audit logging. Always follow best practices for handling sensitive data and stay updated with the latest security recommendations for the libraries used in this implementation.
Itβs important to note that this implementation focuses on demonstrating the process and may not include all necessary security measures for a production environment. Always consult with security experts and follow industry best practices when implementing cryptographic systems in real-world applications.