TL;DR
This guide explains how to securely store data using asymmetric encryption (public/private key pairs) and avoid storing passwords directly by using a strong password hashing function. We’ll cover generating keys, encrypting/decrypting data, and safely handling passwords.
1. Understanding the Problems
Storing data in plain text is a huge security risk. Similarly, storing passwords (even if encrypted) directly is dangerous; a breach could expose them. We need better methods:
- Data Security: Asymmetric encryption allows you to encrypt data with a public key and only decrypt it with the corresponding private key.
- Password Security: Password hashing creates a one-way function – easy to compute the hash from the password, but extremely difficult (ideally impossible) to reverse engineer the password from the hash.
2. Asymmetric Encryption Setup
We’ll use OpenSSL for key generation and encryption/decryption. If you don’t have it installed, you will need to install it (e.g., sudo apt-get install openssl on Debian/Ubuntu).
2.1 Generating Key Pair
openssl genrsa -out private.pem 4096
This creates a 4096-bit RSA private key named private.pem. Keep this file extremely safe!
2.2 Extracting Public Key
openssl rsa -in private.pem -pubout -out public.pem
This extracts the public key from private.pem and saves it as public.pem. You can share this file freely.
3. Data Encryption & Decryption
3.1 Encrypting Data
Let’s encrypt a simple message:
openssl rsautl -encrypt -inkey public.pem -pubin -in message.txt -out encrypted.enc
Replace message.txt with the file containing your data.
3.2 Decrypting Data
Decrypt the data using your private key:
openssl rsautl -decrypt -inkey private.pem -in encrypted.enc -out decrypted.txt
This decrypts encrypted.enc and saves the result as decrypted.txt.
4. Secure Password Storage (Hashing)
Never store passwords directly! Use a strong password hashing function like bcrypt, Argon2, or scrypt.
4.1 Using bcrypt
Many programming languages have libraries for bcrypt. Here’s an example using Python:
import bcrypt
hash = bcrypt.hashpw('your_password'.encode('utf-8'), bcrypt.gensalt())
print(hash)
# To verify:
if bcrypt.checkpw('your_password'.encode('utf-8'), hash):
print("Password matches!")
else:
print("Password does not match.")
Important: The gensalt() function generates a random salt, which is crucial for security. Always store the salt along with the hash.
4.2 Important Password Hashing Considerations
- Salt: Always use a unique, randomly generated salt for each password.
- Cost Factor: Increase the cost factor (work factor) of the hashing function to slow down brute-force attacks. Modern libraries handle this automatically.
- Library Choice: Use well-vetted and maintained cryptography libraries.
5. Putting it Together
In a typical application:
- User enters password.
- Hash the password with bcrypt (including salt). Store the hash and salt in your database.
- Encrypt sensitive data using the user’s public key (if applicable).
- When the user logs in, retrieve the stored hash and salt. Hash the entered password again and compare it to the stored hash.
6. Security Best Practices
- Key Management: Protect your private keys! Use hardware security modules (HSMs) or secure key storage solutions.
- Regular Updates: Keep your cryptography libraries up to date.
- Transport Layer Security (TLS/SSL): Always use HTTPS to encrypt communication between the client and server.
- Input Validation: Validate all user input to prevent injection attacks.

