Skip to content

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 parts
  • key_part_*.enc.sha256: SHA256 checksums of encrypted parts

To reassemble:

  1. Decrypt each part: openssl enc -aes-256-cbc -d -in key_part_X.enc -out key_part_X
  2. Verify checksums: sha256sum -c key_part_X.sha256
  3. Concatenate parts: cat key_part_0 key_part_1 key_part_2 key_part_3 key_part_4 key_part_5 > reassembled_key.pem
  4. 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.