Beginner's Guide to Cryptography
This guide is designed for developers new to cryptography. We’ll cover common use cases with simple, copy-paste ready examples that follow security best practices.
Quick Start: What Do You Need?
I need to hash data (checksums, integrity verification)
→ Use BLAKE3 Hashing or SHA-256
I need to encrypt data (protecting confidentiality)
→ Use AES-GCM Encryption
I need to hash passwords (user authentication)
→ Use Argon2 Password Hashing
I need to verify data hasn’t been tampered with
→ Use HMAC
I need to generate random data (keys, tokens, IDs)
→ Use Random Number Generation
Hashing Data (BLAKE3)
When to use: File integrity, content addressing, checksums
What it does: Creates a unique “fingerprint” of your data. Same input = same output. Different input = different output.
Security: One-way function (can’t reverse it to get original data)
Simple Example
#include <cryptopp/blake3.h>
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <iostream>
#include <string>
std::string hashData(const std::string& data) {
CryptoPP::BLAKE3 hash;
std::string digest;
CryptoPP::StringSource(data, true,
new CryptoPP::HashFilter(hash,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(digest))));
return digest;
}
int main() {
std::string data = "Hello, World!";
std::string hash = hashData(data);
std::cout << "Data: " << data << std::endl;
std::cout << "Hash: " << hash << std::endl;
return 0;
}Compile:
g++ -std=c++11 hash_example.cpp -o hash_example -lcryptoppHash a File
#include <cryptopp/blake3.h>
#include <cryptopp/hex.h>
#include <cryptopp/files.h>
#include <iostream>
std::string hashFile(const std::string& filename) {
CryptoPP::BLAKE3 hash;
std::string digest;
try {
CryptoPP::FileSource(filename.c_str(), true,
new CryptoPP::HashFilter(hash,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(digest))));
return digest;
}
catch (const CryptoPP::Exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return "";
}
}
int main() {
std::string hash = hashFile("document.pdf");
if (!hash.empty()) {
std::cout << "File hash: " << hash << std::endl;
}
return 0;
}Hashing Data (SHA-256)
When to use: When you need FIPS compliance or industry standard hashing
Difference from BLAKE3: Slower but more widely standardized
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <iostream>
#include <string>
std::string hashDataSHA256(const std::string& data) {
CryptoPP::SHA256 hash;
std::string digest;
CryptoPP::StringSource(data, true,
new CryptoPP::HashFilter(hash,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(digest))));
return digest;
}
int main() {
std::string data = "Hello, World!";
std::string hash = hashDataSHA256(data);
std::cout << "SHA-256: " << hash << std::endl;
return 0;
}Encrypting Data (AES-GCM)
When to use: Protecting sensitive data (files, messages, database entries)
What it does: Scrambles data so only someone with the key can read it. Also prevents tampering.
Important: Keep the key secret! If someone gets your key, they can decrypt everything.
Simple Encryption Class
#include <cryptopp/aes.h>
#include <cryptopp/gcm.h>
#include <cryptopp/filters.h>
#include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
#include <iostream>
#include <string>
class SimpleEncryption {
private:
CryptoPP::SecByteBlock key;
CryptoPP::AutoSeededRandomPool prng;
public:
// Constructor: generates a random encryption key
SimpleEncryption() : key(CryptoPP::AES::DEFAULT_KEYLENGTH) {
prng.GenerateBlock(key, key.size());
}
// Encrypt a string
std::string encrypt(const std::string& plaintext) {
// Generate random nonce (must be unique for each encryption!)
// 💡 Why? See: /docs/guides/security-concepts#nonce-and-iv-management
CryptoPP::SecByteBlock nonce(12);
prng.GenerateBlock(nonce, nonce.size());
// Perform encryption
std::string ciphertext;
CryptoPP::GCM<CryptoPP::AES>::Encryption enc;
enc.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource(plaintext, true,
new CryptoPP::AuthenticatedEncryptionFilter(enc,
new CryptoPP::StringSink(ciphertext)
)
);
// Return: nonce + ciphertext (you need both to decrypt!)
std::string result((char*)nonce.data(), nonce.size());
result += ciphertext;
return result;
}
// Decrypt a string
bool decrypt(const std::string& encrypted, std::string& plaintext) {
if (encrypted.size() < 12) {
return false; // Too short to be valid
}
// Extract nonce and ciphertext
CryptoPP::SecByteBlock nonce((const CryptoPP::byte*)encrypted.data(), 12);
std::string ciphertext = encrypted.substr(12);
try {
CryptoPP::GCM<CryptoPP::AES>::Decryption dec;
dec.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource(ciphertext, true,
new CryptoPP::AuthenticatedDecryptionFilter(dec,
new CryptoPP::StringSink(plaintext)
)
);
return true;
}
catch (const CryptoPP::Exception&) {
return false; // Decryption failed (wrong key or tampered data)
}
}
// Get key as hex string (for saving to file/database)
std::string getKeyHex() const {
std::string keyHex;
CryptoPP::HexEncoder encoder(new CryptoPP::StringSink(keyHex));
encoder.Put(key, key.size());
encoder.MessageEnd();
return keyHex;
}
// Load key from hex string
void setKeyFromHex(const std::string& keyHex) {
CryptoPP::HexDecoder decoder;
decoder.Put((CryptoPP::byte*)keyHex.data(), keyHex.size());
decoder.MessageEnd();
key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH);
decoder.Get(key, key.size());
}
};
int main() {
SimpleEncryption crypto;
// Encrypt
std::string secret = "This is my secret message!";
std::string encrypted = crypto.encrypt(secret);
std::cout << "Original: " << secret << std::endl;
std::cout << "Encrypted size: " << encrypted.size() << " bytes" << std::endl;
// Decrypt
std::string decrypted;
if (crypto.decrypt(encrypted, decrypted)) {
std::cout << "Decrypted: " << decrypted << std::endl;
} else {
std::cout << "Decryption failed!" << std::endl;
}
// Save key for later use
std::string keyHex = crypto.getKeyHex();
std::cout << "Save this key: " << keyHex << std::endl;
return 0;
}Encrypting Files
#include <cryptopp/aes.h>
#include <cryptopp/gcm.h>
#include <cryptopp/filters.h>
#include <cryptopp/osrng.h>
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
bool encryptFile(const std::string& inputFile,
const std::string& outputFile,
const CryptoPP::SecByteBlock& key) {
try {
// Read whole file into memory (fine for most beginner examples)
std::ifstream in(inputFile, std::ios::binary);
if (!in) {
std::cerr << "Error opening input file: " << inputFile << std::endl;
return false;
}
std::string plaintext(
(std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>()
);
in.close();
// Generate random 96-bit nonce (must be unique per encryption)
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock nonce(12);
prng.GenerateBlock(nonce, nonce.size());
// Encrypt plaintext
std::string ciphertext;
CryptoPP::GCM<CryptoPP::AES>::Encryption enc;
enc.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource ss(
plaintext,
true,
new CryptoPP::AuthenticatedEncryptionFilter(
enc,
new CryptoPP::StringSink(ciphertext)
)
);
// Write file as: [nonce || ciphertext]
std::ofstream out(outputFile, std::ios::binary);
if (!out) {
std::cerr << "Error opening output file: " << outputFile << std::endl;
return false;
}
out.write(reinterpret_cast<const char*>(nonce.data()), nonce.size());
out.write(ciphertext.data(), ciphertext.size());
out.close();
return true;
}
catch (const CryptoPP::Exception& ex) {
std::cerr << "Encryption error: " << ex.what() << std::endl;
return false;
}
}
bool decryptFile(const std::string& inputFile,
const std::string& outputFile,
const CryptoPP::SecByteBlock& key) {
try {
// Read whole file
std::ifstream in(inputFile, std::ios::binary);
if (!in) {
std::cerr << "Error opening input file: " << inputFile << std::endl;
return false;
}
// Read nonce (first 12 bytes)
CryptoPP::SecByteBlock nonce(12);
in.read(reinterpret_cast<char*>(nonce.data()), nonce.size());
if (!in) {
std::cerr << "Error reading nonce from file" << std::endl;
return false;
}
// Read ciphertext (rest of file)
std::string ciphertext(
(std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>()
);
in.close();
// Decrypt
std::string plaintext;
CryptoPP::GCM<CryptoPP::AES>::Decryption dec;
dec.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource ss(
ciphertext,
true,
new CryptoPP::AuthenticatedDecryptionFilter(
dec,
new CryptoPP::StringSink(plaintext)
)
);
std::ofstream out(outputFile, std::ios::binary);
if (!out) {
std::cerr << "Error opening output file: " << outputFile << std::endl;
return false;
}
out.write(plaintext.data(), plaintext.size());
out.close();
return true;
}
catch (const CryptoPP::Exception& ex) {
std::cerr << "Decryption error: " << ex.what() << std::endl;
return false;
}
}
int main() {
// Generate encryption key
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock key(CryptoPP::AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());
// Encrypt file
if (encryptFile("document.pdf", "document.pdf.encrypted", key)) {
std::cout << "File encrypted successfully" << std::endl;
}
// Decrypt file
if (decryptFile("document.pdf.encrypted", "document_decrypted.pdf", key)) {
std::cout << "File decrypted successfully" << std::endl;
}
return 0;
}Password Hashing (Argon2)
When to use: Storing user passwords in a database
What it does: Creates a hash that’s slow to compute (makes brute-force attacks impractical)
Important: Never store plain passwords! Always hash them.
Simple Password Class
#include <cryptopp/argon2.h>
#include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
#include <cryptopp/filters.h>
#include <iostream>
#include <string>
class PasswordHasher {
public:
// Hash a password - returns salt+hash combined as hex string
static std::string hashPassword(const std::string& password) {
CryptoPP::AutoSeededRandomPool prng;
// Generate random salt
CryptoPP::SecByteBlock salt(16);
prng.GenerateBlock(salt, salt.size());
// Hash password with Argon2id
CryptoPP::SecByteBlock hash(32);
CryptoPP::Argon2id argon2;
argon2.DeriveKey(
hash, hash.size(),
(const CryptoPP::byte*)password.data(), password.size(),
salt, salt.size(),
nullptr, 0, // No secret
nullptr, 0, // No additional data
3, // Time cost (iterations)
65536 // Memory cost (64 MB)
);
// Combine salt + hash and convert to hex
std::string combined;
combined.append((char*)salt.data(), salt.size());
combined.append((char*)hash.data(), hash.size());
std::string hexOutput;
CryptoPP::HexEncoder encoder(new CryptoPP::StringSink(hexOutput));
encoder.Put((CryptoPP::byte*)combined.data(), combined.size());
encoder.MessageEnd();
return hexOutput;
}
// Verify a password against stored hash
static bool verifyPassword(const std::string& password,
const std::string& storedHashHex) {
// Decode hex
std::string decoded;
CryptoPP::HexDecoder decoder(new CryptoPP::StringSink(decoded));
decoder.Put((CryptoPP::byte*)storedHashHex.data(), storedHashHex.size());
decoder.MessageEnd();
if (decoded.size() != 48) { // 16 bytes salt + 32 bytes hash
return false;
}
// Extract salt and expected hash
CryptoPP::SecByteBlock salt((const CryptoPP::byte*)decoded.data(), 16);
CryptoPP::SecByteBlock expectedHash((const CryptoPP::byte*)decoded.data() + 16, 32);
// Compute hash with same salt
CryptoPP::SecByteBlock computedHash(32);
CryptoPP::Argon2id argon2;
argon2.DeriveKey(
computedHash, computedHash.size(),
(const CryptoPP::byte*)password.data(), password.size(),
salt, salt.size(),
nullptr, 0,
nullptr, 0,
3,
65536
);
// Compare (constant-time to prevent timing attacks)
// 💡 Why constant-time? See: /docs/guides/security-concepts#constant-time-operations
return CryptoPP::VerifyBufsEqual(
computedHash, expectedHash, 32
);
}
};
int main() {
std::string password = "MySecurePassword123!";
// Hash password (do this when user registers)
std::string hashedPassword = PasswordHasher::hashPassword(password);
std::cout << "Hashed password: " << hashedPassword << std::endl;
std::cout << "Store this in your database!" << std::endl;
// Verify password (do this when user logs in)
bool valid = PasswordHasher::verifyPassword(password, hashedPassword);
std::cout << "Password valid: " << (valid ? "YES" : "NO") << std::endl;
// Try wrong password
bool invalid = PasswordHasher::verifyPassword("WrongPassword", hashedPassword);
std::cout << "Wrong password: " << (invalid ? "YES" : "NO") << std::endl;
return 0;
}Message Authentication (HMAC)
When to use: Verifying that a message hasn’t been tampered with
What it does: Creates a “signature” using a secret key. Anyone with the key can verify the message is authentic.
Use case: API authentication, message integrity
#include <cryptopp/hmac.h>
#include <cryptopp/sha.h>
#include <cryptopp/filters.h>
#include <cryptopp/hex.h>
#include <cryptopp/secblock.h>
#include <cryptopp/osrng.h>
#include <string>
#include <iostream>
class MessageAuth {
private:
CryptoPP::SecByteBlock key;
public:
MessageAuth() : key(32) {
CryptoPP::AutoSeededRandomPool prng;
prng.GenerateBlock(key, key.size());
}
// Create authentication tag for a message (returns hex)
std::string sign(const std::string& message) {
std::string mac; // raw MAC bytes
std::string macHex; // hex-encoded MAC
CryptoPP::HMAC<CryptoPP::SHA256> hmac(key, key.size());
// Compute MAC
CryptoPP::StringSource ss(
message,
true,
new CryptoPP::HashFilter(
hmac,
new CryptoPP::StringSink(mac)
)
);
// Encode MAC as hex for easy storage/transmission
CryptoPP::StringSource ss2(
mac,
true,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(macHex),
false // lowercase
)
);
return macHex;
}
// Verify a message's authentication tag (constant-time comparison)
// 💡 Why constant-time? See: /docs/guides/security-concepts#constant-time-operations
bool verify(const std::string& message, const std::string& macHex) {
// 1) Decode provided MAC from hex to raw bytes
std::string mac;
try {
CryptoPP::StringSource ss(
macHex,
true,
new CryptoPP::HexDecoder(
new CryptoPP::StringSink(mac)
)
);
}
catch (const CryptoPP::Exception& e) {
// Invalid hex input
std::cerr << "MAC decode error: " << e.what() << std::endl;
return false;
}
// 2) Recompute MAC for this message (raw bytes, not hex)
std::string computed;
CryptoPP::HMAC<CryptoPP::SHA256> hmac(key, key.size());
CryptoPP::StringSource ss2(
message,
true,
new CryptoPP::HashFilter(
hmac,
new CryptoPP::StringSink(computed)
)
);
// 3) Constant-time comparison (prevents timing attacks)
if (mac.size() != computed.size()) {
return false;
}
return CryptoPP::VerifyBufsEqual(
reinterpret_cast<const CryptoPP::byte*>(mac.data()),
reinterpret_cast<const CryptoPP::byte*>(computed.data()),
mac.size()
);
}
};
int main() {
MessageAuth auth;
std::string message = "Transfer $100 to account 12345";
// Create authentication tag
std::string mac = auth.sign(message);
std::cout << "Message: " << message << std::endl;
std::cout << "MAC: " << mac << std::endl;
// Verify message
bool valid = auth.verify(message, mac);
std::cout << "Valid: " << (valid ? "YES" : "NO") << std::endl;
// Try tampering with message
std::string tamperedMessage = "Transfer $999 to account 12345";
bool tampered = auth.verify(tamperedMessage, mac);
std::cout << "Tampered message valid: " << (tampered ? "YES" : "NO") << std::endl;
return 0;
}Random Number Generation
When to use: Generating keys, tokens, session IDs, nonces
What it does: Provides cryptographically secure random numbers
Important: Never use rand() or srand() for security! Use AutoSeededRandomPool.
💡 Learn more: Why weak RNGs are dangerous
#include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
#include <iostream>
class RandomGenerator {
private:
CryptoPP::AutoSeededRandomPool prng;
public:
// Generate random bytes
std::string generateBytes(size_t length) {
CryptoPP::SecByteBlock random(length);
prng.GenerateBlock(random, random.size());
std::string hex;
CryptoPP::HexEncoder encoder(new CryptoPP::StringSink(hex));
encoder.Put(random, random.size());
encoder.MessageEnd();
return hex;
}
// Generate random integer in range [min, max]
// Note: modulo has slight bias unless (max-min+1) divides 2^32.
// Fine for tokens/IDs; for high-assurance work, use rejection sampling.
unsigned int generateInt(unsigned int min, unsigned int max) {
unsigned int value;
prng.GenerateBlock((CryptoPP::byte*)&value, sizeof(value));
return min + (value % (max - min + 1));
}
// Generate session token
std::string generateToken() {
return generateBytes(32); // 32 bytes = 256 bits
}
// Generate encryption key
CryptoPP::SecByteBlock generateKey(size_t keySize = 16) {
CryptoPP::SecByteBlock key(keySize);
prng.GenerateBlock(key, key.size());
return key;
}
};
int main() {
RandomGenerator rng;
// Generate session token
std::string token = rng.generateToken();
std::cout << "Session token: " << token << std::endl;
// Generate random bytes
std::string randomBytes = rng.generateBytes(16);
std::cout << "Random bytes: " << randomBytes << std::endl;
// Generate random number
unsigned int randomNum = rng.generateInt(1, 100);
std::cout << "Random number (1-100): " << randomNum << std::endl;
// Generate encryption key
CryptoPP::SecByteBlock key = rng.generateKey(32);
std::cout << "Generated 256-bit key" << std::endl;
return 0;
}Common Questions
How do I store encryption keys?
Bad: Hard-code in source code
// DON'T DO THIS!
std::string key = "mysecretkey123";Good: Load from secure configuration
#include <cstdlib> // for std::getenv
// Store in environment variable, config file, or key management system
const char* keyHex = std::getenv("ENCRYPTION_KEY");
if (keyHex) {
// Load key from hex string
}Better: Use OS key storage (Windows: DPAPI, Linux: Keyring, macOS: Keychain)
💡 Learn more: Key storage best practices
How do I transmit encrypted data?
Store as: nonce || ciphertext || auth_tag
All three components can be transmitted in the clear - only the key must be secret.
What key size should I use?
- AES: 128-bit is fine for most uses, 256-bit for maximum security
- HMAC: 256-bit (32 bytes)
- Argon2: Handled automatically
How often should I rotate keys?
- Encryption keys: Yearly, or after 2^32 encryptions (for GCM)
- API keys: Quarterly or on suspected compromise
- Password hashes: Never rotate (each password has unique salt)
Can I use this in production?
Yes, cryptopp-modern is designed for production use, and these examples follow security best practices.
However, your overall system still needs proper testing and review:
- Test thoroughly in your specific environment
- Consider professional security audit for critical applications
- Keep library updated for security patches
- These examples are educational - adapt error handling for your needs
Next Steps
Now that you understand the basics:
⚠️ Essential Security Reading:
- Security Concepts Guide - Start here to understand:
- Why constant-time comparison prevents timing attacks
- Why GCM nonce reuse is catastrophic
- Why you need separate keys for encryption and authentication
- Why
rand()is dangerous for cryptography - How to properly store keys
- Security Concepts Guide - Start here to understand:
Read the detailed algorithm guides:
Explore advanced topics:
- Public key cryptography (RSA, ECDSA)
- Key derivation functions
- Digital signatures
Complete Example: Secure Note Application
Putting it all together - a simple encrypted note storage application.
File format: [salt (16 bytes) || nonce (12 bytes) || ciphertext]
This ensures the same password produces the same key when loading a previously saved note.
#include <cryptopp/aes.h>
#include <cryptopp/gcm.h>
#include <cryptopp/argon2.h>
#include <cryptopp/osrng.h>
#include <cryptopp/filters.h>
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
class SecureNotes {
private:
std::string password;
CryptoPP::AutoSeededRandomPool prng;
// Derive a 256-bit key from password + salt using Argon2id
CryptoPP::SecByteBlock deriveKey(const std::string& pwd,
const CryptoPP::SecByteBlock& salt) {
CryptoPP::SecByteBlock key(32); // 256-bit key
CryptoPP::Argon2id argon2;
argon2.DeriveKey(
key, key.size(),
reinterpret_cast<const CryptoPP::byte*>(pwd.data()), pwd.size(),
salt, salt.size(),
nullptr, 0, // no secret
nullptr, 0, // no additional data
3, // time cost
65536 // memory cost (64 MB)
);
return key;
}
public:
explicit SecureNotes(const std::string& pwd)
: password(pwd) {}
// Save encrypted note to disk
bool saveNote(const std::string& filename, const std::string& note) {
try {
// 1) Generate random salt and nonce
CryptoPP::SecByteBlock salt(16); // 128-bit salt
CryptoPP::SecByteBlock nonce(12); // 96-bit GCM nonce
prng.GenerateBlock(salt, salt.size());
prng.GenerateBlock(nonce, nonce.size());
// 2) Derive key from password + salt
CryptoPP::SecByteBlock key = deriveKey(password, salt);
// 3) Encrypt note with AES-GCM
std::string ciphertext;
CryptoPP::GCM<CryptoPP::AES>::Encryption enc;
enc.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource ss(
note,
true,
new CryptoPP::AuthenticatedEncryptionFilter(
enc,
new CryptoPP::StringSink(ciphertext)
)
);
// 4) Write file as: [salt || nonce || ciphertext]
std::ofstream out(filename, std::ios::binary);
if (!out) {
std::cerr << "Error opening file for writing: " << filename << std::endl;
return false;
}
out.write(reinterpret_cast<const char*>(salt.data()), salt.size());
out.write(reinterpret_cast<const char*>(nonce.data()), nonce.size());
out.write(ciphertext.data(), ciphertext.size());
out.close();
return true;
}
catch (const std::exception& ex) {
std::cerr << "Error saving: " << ex.what() << std::endl;
return false;
}
}
// Load and decrypt note from disk
bool loadNote(const std::string& filename, std::string& note) {
try {
std::ifstream in(filename, std::ios::binary);
if (!in) {
std::cerr << "Error opening file for reading: " << filename << std::endl;
return false;
}
// 1) Read salt (16 bytes) and nonce (12 bytes)
CryptoPP::SecByteBlock salt(16);
CryptoPP::SecByteBlock nonce(12);
in.read(reinterpret_cast<char*>(salt.data()), salt.size());
if (!in) {
std::cerr << "Error reading salt from file" << std::endl;
return false;
}
in.read(reinterpret_cast<char*>(nonce.data()), nonce.size());
if (!in) {
std::cerr << "Error reading nonce from file" << std::endl;
return false;
}
// 2) Read ciphertext (rest of file)
std::string ciphertext(
(std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>()
);
in.close();
// 3) Derive key from password + salt
CryptoPP::SecByteBlock key = deriveKey(password, salt);
// 4) Decrypt
CryptoPP::GCM<CryptoPP::AES>::Decryption dec;
dec.SetKeyWithIV(key, key.size(), nonce, nonce.size());
CryptoPP::StringSource ss(
ciphertext,
true,
new CryptoPP::AuthenticatedDecryptionFilter(
dec,
new CryptoPP::StringSink(note)
)
);
return true;
}
catch (const std::exception& ex) {
std::cerr << "Error loading: " << ex.what() << std::endl;
return false;
}
}
};
int main() {
std::string password = "MyMasterPassword123!";
SecureNotes notes(password);
// Save a note
std::string myNote =
"This is my secret note!\n"
"It's encrypted with my password.";
if (notes.saveNote("secret.enc", myNote)) {
std::cout << "Note saved successfully" << std::endl;
}
// Load the note (works even after program restart!)
std::string loadedNote;
if (notes.loadNote("secret.enc", loadedNote)) {
std::cout << "Note loaded successfully:" << std::endl;
std::cout << loadedNote << std::endl;
}
return 0;
}This example demonstrates:
- Password-based key derivation (Argon2)
- Authenticated encryption (AES-GCM)
- Proper salt + nonce handling (both persisted in file)
- File I/O with encrypted data
- Error handling
Compile:
g++ -std=c++11 secure_notes.cpp -o secure_notes -lcryptoppYou now have a solid foundation for using cryptopp-modern securely!