Hiding Private Keys
Steganography includes the concealment of information within computer files.
It comes from the Greek words steganos
, which means “covered” or “hidden,” and graph
, which means “to write.” Hence, “hidden writing.”
You can use steganography to hide text, video, images, or even audio data, and although the technique is centuries old there are unique ways we can use it within computer science.
Why
Ummm because it's cool 😎? How awesome would it be to split a RSA key pair into raw text files, encrypt each part, and maybe hide them inside the Windows 10 default background images? In the event you ever need to recreate that key data, pull your innocuous Win10_x64.iso & mount to grab the backgrounds folder. A bit like hiding your key under a rock. Each part was encrypted with something you know so if this key file didn't have a password on it you aren't at risk of losing control of the key if someone hashes all the files to the Windows iso and notices the few wallpaper files sizes that are a bit larger.
How
You should not trust security through obscurity. Encrypt the data before hiding it.
openssl rsa -aes256 -in private_key.pem -out encrypted_file.pem
or GPG is great too!
gpgtar -c -o encrypted_file.gpg private_key.pem
steganopy.api
Simply import a python
library called steganopy.api
Create
We will take an image called photo1.png
and hide encrypted_file.pem
data created with openssl
earlier, then output a new image called background_in_hiding.png
.
import steganopy.api
steganopy.api.create_stegano_image(original_image="photo1.png", data_to_hide="encrypted_file.pem").save("background_in_hiding.png")
Deobfuscate
hidden
variable calls steganopy.api.extract_data
and on the final line of this python script we call this variable with print
. The rest is just taking the output of print(hidden)
and sending it to a file on the local system.
import steganopy.api
from contextlib import redirect_stdout
hidden = steganopy.api.extract_data_from_stegano_image(image="background-in-hiding.png")
pathname = "encrypted_file.pem"
pathname = input("Save the file as: ", pathname)
with open(pathname, 'w') as f:
with redirect_stdout(f):
print(hidden)
Decrypt
Finally we can decrypt the data that we deobfuscated.
openssl rsa -in encrypted_file.pem -out private_key.pem
Bonus
We are going to split your RSA keys into multiple encrypted pieces with checksum encrypted pieces (at this point we should decrypt and assemble the key to test). Use Steganography to hide the encrypted bits in plain sight. Use command line tools to identify the images hiding the information, deobfuscate the subject images, and verify the integrity of the encrypted files with the checksums. Decrypt the file parts and reassemble the key parts.
Lets bash
it out
Since I am more familiar with shell scripting lets create a bash
script that does the following:
- Takes a keyfile (or any text file)
- Splits it into 6 parts
- Creates a checksum for each part
- Encrypts each part with
openssl
- Checksums the encrypted parts
You can split a keyfile: ./split_key.sh your_private_key
and it will create a directory called key_parts
in whatever directory you are currently running the script from.
#! /usr/bin/env bash
if [ $# -ne 1 ]; then
echo "Usage: $0 <private_key_file>"
exit 1
fi
PRIVATE_KEY="$1"
OUTPUT_DIR="key_parts"
PARTS=6
# Check if private key file exists
if [ ! -f "$PRIVATE_KEY" ]; then
echo "Error: Private key file '$PRIVATE_KEY' not found"
exit 1
fi
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Get the size of the private key file
KEY_SIZE=$(wc -c < "$PRIVATE_KEY")
PART_SIZE=$((KEY_SIZE / PARTS))
REMAINDER=$((KEY_SIZE % PARTS))
echo "Splitting $PRIVATE_KEY ($KEY_SIZE bytes) into $PARTS parts..."
# Split the private key into parts
split -b $PART_SIZE -d -a 1 "$PRIVATE_KEY" "$OUTPUT_DIR/key_part_"
# Handle remainder if file doesn't divide evenly
if [ $REMAINDER -ne 0 ]; then
# The last part will be slightly larger
echo "Note: Last part will be $REMAINDER bytes larger due to uneven division"
fi
# Encrypt each part and generate checksums
for i in $(seq 0 $((PARTS-1))); do
PART_FILE="$OUTPUT_DIR/key_part_$i"
ENCRYPTED_FILE="$OUTPUT_DIR/key_part_$i.enc"
CHECKSUM_FILE="$OUTPUT_DIR/key_part_$i.sha256"
if [ -f "$PART_FILE" ]; then
echo "Processing part $i..."
# Encrypt the part with AES256
openssl enc -aes-256-cbc -salt -in "$PART_FILE" -out "$ENCRYPTED_FILE"
# Generate SHA256 checksum of the original part
sha256sum "$PART_FILE" > "$CHECKSUM_FILE"
# Generate SHA256 checksum of the encrypted part
sha256sum "$ENCRYPTED_FILE" > "$ENCRYPTED_FILE.sha256"
# Remove the unencrypted part for security
rm "$PART_FILE"
echo "Created: $ENCRYPTED_FILE and checksums"
fi
done
echo "Done! Files created in $OUTPUT_DIR/"
echo "Encrypted parts: *.enc"
echo "Checksums: *.sha256"
Files:
key_part_*.enc
: Encrypted key parts (AES-256-CBC)key_part_*.sha256
: SHA256 checksums of original partskey_part_*.enc.sha256
: SHA256 checksums of encrypted parts
To reassemble:
- Decrypt each part:
openssl enc -aes-256-cbc -d -in key_part_X.enc -out key_part_X
- Verify checksums:
sha256sum -c key_part_X.sha256
- Concatenate parts:
cat key_part_0 key_part_1 key_part_2 key_part_3 key_part_4 key_part_5 > reassembled_key.pem
- Remove temporary parts for security
Hide That Data
Like we demonstrated before, let's take the encrypted key parts and hide them inside images. We should also do something about all those checksums but let's just focus on hiding the data, deobfuscate it, and reassemble the key with the instructions above.
- This script assumes you are calling it from the same directory as the
bash
script above - This script assumes you have your images in the same directory as this
python
script - This script assumes you have named your images
image{0-5}
.png pip install steganopy
python3 embed_keys.py
- Will output obfuscated images inside
key_parts/stego_images{0-5}
The script will process:
- image0.png + key_part_0.enc → stego_image0.png
- image1.png + key_part_1.enc → stego_image1.png
- ... and so on through image5/key_part_5
#!/usr/bin/env python3
import os
import sys
from steganopy.api import encode_image, decode_image
def embed_keys_in_images():
"""
Embed encrypted key parts into images using steganography
"""
key_parts_dir = "key_parts"
output_dir = os.path.join(key_parts_dir, "stego_images")
# Create output directory
os.makedirs(output_dir, exist_ok=True)
# Check if key_parts directory exists
if not os.path.exists(key_parts_dir):
print(f"Error: {key_parts_dir} directory not found")
print("Please run the key splitting script first")
return False
# Process each image and corresponding encrypted key part (0-5)
for i in range(6):
image_file = f"image{i}.png" # image0.png to image5.png
key_part_file = os.path.join(key_parts_dir, f"key_part_{i}.enc")
output_file = os.path.join(output_dir, f"stego_image{i}.png") # stego_image0.png to stego_image5.png
# Check if image exists
if not os.path.exists(image_file):
print(f"Warning: {image_file} not found, skipping...")
continue
# Check if encrypted key part exists
if not os.path.exists(key_part_file):
print(f"Warning: {key_part_file} not found, skipping...")
continue
try:
print(f"Embedding {key_part_file} into {image_file}...")
# Read the encrypted key part as binary data
with open(key_part_file, 'rb') as f:
key_data = f.read()
# Encode the key data into the image
encode_image(image_file, key_data, output_file)
print(f"Successfully created: {output_file}")
except Exception as e:
print(f"Error processing {image_file}: {str(e)}")
continue
def verify_images_exist():
"""Verify that all required images exist"""
missing_images = []
for i in range(6): # 0-5
# Check multiple common image formats
image_found = False
for ext in ['.png', '.jpg', '.jpeg', '.bmp', '.tiff']:
image_file = f"image{i}{ext}"
if os.path.exists(image_file):
image_found = True
break
if not image_found:
missing_images.append(f"image{i}")
if missing_images:
print("Missing images:", ", ".join(missing_images))
print("Expected image files: image0 to image5 (with extensions: .png, .jpg, .jpeg, .bmp, .tiff)")
return False
return True
if __name__ == "__main__":
print("RSA Key Steganography Embedder")
print("=" * 40)
# Verify prerequisites
if not verify_images_exist():
sys.exit(1)
# Embed keys in images
if embed_keys_in_images():
print("\nEmbedding completed successfully!")
else:
print("\nEmbedding failed!")
sys.exit(1)
Deobfuscate
Run the script extract_keys_from_images.py
and it will look for images within key_parts/stego_images
and decrypt the data into key_parts/extracted
- This script assumes you are calling it from the same directory as the
bash
script above
#!/usr/bin/env python3
import os
import sys
from steganopy.api import decode_image
def extract_keys_from_images():
"""
Extract encrypted key parts from steganographic images
"""
stego_dir = os.path.join("key_parts", "stego_images")
output_dir = os.path.join("key_parts", "extracted")
# Create output directory
os.makedirs(output_dir, exist_ok=True)
if not os.path.exists(stego_dir):
print(f"Error: {stego_dir} directory not found")
return False
extracted_files = []
for i in range(6): # 0-5
stego_file = os.path.join(stego_dir, f"stego_image{i}.png")
output_file = os.path.join(output_dir, f"extracted_key_part_{i}.enc")
if not os.path.exists(stego_file):
print(f"Warning: {stego_file} not found, skipping...")
continue
try:
print(f"Extracting data from {stego_file}...")
# Decode the key data from the image
key_data = decode_image(stego_file)
# Write the extracted data to file
with open(output_file, 'wb') as f:
f.write(key_data)
extracted_files.append(output_file)
print(f"Extracted: {output_file}")
except Exception as e:
print(f"Error extracting from {stego_file}: {str(e)}")
continue
print(f"\nExtracted {len(extracted_files)} key parts to: {output_dir}")
return len(extracted_files) > 0
if __name__ == "__main__":
print("RSA Key Steganography Extractor")
print("=" * 40)
if extract_keys_from_images():
print("\nExtraction completed successfully!")
else:
print("\nExtraction failed!")
sys.exit(1)
Assemble
✨ Ta-Da ✨
We now have the 6 encrypted parts of the private key that we can use openssl
to decrypt each part
openssl enc -aes-256-cbc -d -in key_part_X.enc -out key_part_X
Once you have decrypted each part you can cat
the pieces back together
cat key_part_0 key_part_1 key_part_2 key_part_3 key_part_4 key_part_5 > reassembled_key
Look at your hacker genius skills💥
The final part of this would be to check the checksums against both the encrypted parts before decryption, and the decrypted parts before the key is assembled.