It has become very normalised to store Ethereum private keys in plain text within .env
files. While this is convenient, it’s a disaster waiting to happen when working in production with wallets that contain real funds.
Today, I’ll show you a better way to manage your hot wallet keys using AES encryption.
Full code for this is available at: https://github.com/jamesbachini/DotEnv-Encrypted-Private-Keys
The Problem with .env Private Keys
First off, let’s talk about why storing private keys in plain text is a terrible idea:
- Git accidents Ever accidentally pushed your .env file to a public repo? Congratulations, you’ve just broadcasted your private key to the world.
- Local security If someone gains access to your machine, they’ve got instant access to your funds. This is true if you are using environmental variables too.
- Trading When programmatically trading on decentralized exchanges you need wallets with real funds attached to the internet, this is an issue. It is possible to store funds in a contract but this introduces it’s own risks and slows development.
Encrypted Private Keys
To address these issues, we can encrypt our private keys before storing them in our .env file or as an environmental variable. Here’s how it works:
- You encrypt your private key with a passphrase.
- The encrypted key is stored in your .env file.
- When you need to use the key, to deploy a contract for example or fire up a trading bot, you decrypt it using your passphrase from the command line.
Is this perfect? no someone could still install a key logger or something on the system to trace std input but it will save you if you do something silly like share some code and forget to add .env to .gitignore
If someone gets hold of your encrypted private key, they can’t use your private key without the passphrase.
Using the Tool: Generating a New Testnet Wallet
Let’s walk through how to use this tool to generate a new testnet wallet with an encrypted private key:
- First, clone the repository:
git clone https://github.com/jamesbachini/DotEnv-Encrypted-Private-Keys.git
cd DotEnv-Encrypted-Private-Keys
- Install the dependencies:
npm install
- Run the example script:
node example.js
- Choose option 1 to “Encrypt a New Key”.
- Enter a strong passphrase when prompted. Bare in mind that someone could try to brute force this if they found your encrypted key.
- The tool will generate a new wallet and display its address. It will also give you an
ENCRYPTED_PRIVATE_KEY
value. Copy this to your .env file.
Voila! You’ve now got a new testnet wallet with an encrypted private key.
ENCRYPTED_PRIVATE_KEY=U2FsdGVkX19F0bnyrH4F7QpzIWXC1wSxM4NHiuq7jxITivwfcNddGLyRaxzLR6F0wauvA2a2/XEcr5iB0XzvlZ/6fBb/ZdN8g0/jXUWRQTWNA3qkRvYqanF2KhcW8/UW
Setting Up Encrypted Private Keys in Hardhat
Now, let’s integrate this into a Hardhat project:
- First, ensure you have the required dependencies:
npm install ethers crypto-js dotenv
- Use in your hardhat.config.js file
require("@nomicfoundation/hardhat-toolbox");
const CryptoJS = require("crypto-js");
const readline = require('readline');
require("dotenv").config();
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
let sepoliaPrivateKey
rl.question('Enter the pass phrase to unlock the private key: ', (passString) => {
const sepoliaPrivateKey = CryptoJS.AES.decrypt(process.env.ENCRYPTED_PRIVATE_KEY, passString).toString(CryptoJS.enc.Utf8);
});
exports = {
solidity: "0.8.26",
networks: {
sepolia: {
url: `https://sepolia.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: [sepoliaPrivateKey],
},
},
};
Now, when you run Hardhat commands, it will prompt you for your passphrase to decrypt the private key. Sure this will be annoying, infuriating even during tests. You can probably add some checks to only prompt if it’s a certain chain id which might make it more bearable. Ultimately when dealing with critical wallet credentials it might save some funds going missing.
Encryption < Hardware Wallets & Multisigs
By encrypting our private keys, we’ve significantly increased the opsec of our development process. Even if our key credentials are compromised, our funds remain safe behind an additional layer of encryption.
Remember, this method is great for development and testing, but for production environments with significant funds, if possible, consider using hardware wallets. These physical devices, when combined with multisig wallets offer the best in class security.
Stay safe out there, and happy building!
Full code at: https://github.com/jamesbachini/DotEnv-Encrypted-Private-Keys