Hiding Files in PNG Images with OpenSSL

This article explains three approaches to securely encrypt files using Bash and OpenSSL, including a more "paranoid" technique that allows you to hide encrypted files inside a PNG image.

All solutions work on Linux and macOS.


1. Quick & Dirty — Plain OpenSSL, No Scripts

Just want to encrypt a file fast without any scripts? These are the raw OpenSSL commands.

Encrypt a file

openssl enc -aes-256-cbc -pbkdf2 -salt -in secret.txt -out secret.txt.enc

OpenSSL will ask for a password. The result is secret.txt.enc.

Decrypt a file

openssl enc -d -aes-256-cbc -pbkdf2 -in secret.txt.enc -out secret.txt

Workflow

graph LR A[secret.txt] --> B[openssl enc] B --> C[secret.txt.enc] C --> D[openssl enc -d] D --> A

2. Simple File Encryption With Scripts

Wrap the OpenSSL commands in reusable scripts.

Example

Encrypt a file:

./encrypt.sh secret.txt

Result:

secret.txt.enc

Decrypt the file:

./decrypt.sh secret.txt.enc

Result:

secret.txt

Workflow

graph LR A[Original File] --> B[Encrypt AES-256-CBC + PBKDF2] B --> C[Encrypted File] C --> D[Decrypt] D --> A

Scripts

encrypt.sh

#!/usr/bin/env bash
set -euo pipefail

if [ $# -lt 1 ]; then
    echo "Usage: $0 file_to_encrypt"
    exit 1
fi

FILE="$1"
OUT="$FILE.enc"

openssl enc -aes-256-cbc -pbkdf2 -salt -in "$FILE" -out "$OUT"

echo "Encrypted file: $OUT"

Download encrypt.sh

decrypt.sh

#!/usr/bin/env bash
set -euo pipefail

if [ $# -lt 1 ]; then
    echo "Usage: $0 file_to_decrypt"
    exit 1
fi

FILE="$1"
OUT="${FILE%.enc}"

openssl enc -d -aes-256-cbc -pbkdf2 -in "$FILE" -out "$OUT"

echo "Decrypted file: $OUT"

Download decrypt.sh


3. Paranoid File Encryption With PNG Embedding

This technique encrypts files and then appends them to a PNG image.

The PNG image still opens normally, but secretly contains encrypted data.

Example

Hide files inside PNG:

./encrypt_into_png.sh image.png secret1.txt secret2.pdf

Result:

hidden_image.png

Extract hidden files:

./extract_from_png.sh hidden_image.png

Result:

secret1.txt
secret2.pdf

Workflow

graph TD A[Original PNG] --> B[Encrypt secret1.txt] A --> C[Encrypt secret2.pdf] B --> D[Append encrypted data] C --> D D --> E[Hidden PNG] E --> F[Extract data] F --> G[Decrypt secret1.txt] F --> H[Decrypt secret2.pdf]

Scripts

encrypt_into_png.sh

#!/usr/bin/env bash
set -euo pipefail

if [ $# -lt 2 ]; then
    echo "Usage: $0 source.png file1 [file2 ...]"
    exit 1
fi

PNG="$1"
shift

OUT="hidden_$PNG"
cp "$PNG" "$OUT"

get_file_size() {
    local file="$1"
    if [[ "$(uname)" == "Darwin" ]]; then
        stat -f%z "$file"
    else
        stat -c%s "$file"
    fi
}

for FILE in "$@"; do
    ENC="$FILE.enc"

    openssl enc -aes-256-cbc -pbkdf2 -salt -in "$FILE" -out "$ENC"

    FNAME_LEN=$(echo -n "$FILE" | wc -c)
    DATA_LEN=$(get_file_size "$ENC")

    {
        echo -n "MAGICHID"
        printf "%04d" "$FNAME_LEN"
        echo -n "$FILE"
        printf "%010d" "$DATA_LEN"
        cat "$ENC"
    } >> "$OUT"

    rm "$ENC"

    echo "Added $FILE"
done

echo "Output file: $OUT"

Download encrypt_into_png.sh

extract_from_png.sh

#!/usr/bin/env bash
set -euo pipefail

if [ $# -lt 1 ]; then
    echo "Usage: $0 hidden_file.png"
    exit 1
fi

PNG="$1"
MAGIC="MAGICHID"

while true; do

POS=$(grep -aob "$MAGIC" "$PNG" | head -n1 | cut -d: -f1 || true)

if [ -z "$POS" ]; then
    echo "No more hidden files found"
    break
fi

OFFSET=$((POS + ${#MAGIC}))

FNAME_LEN=$(dd if="$PNG" bs=1 skip="$OFFSET" count=4 2>/dev/null)
FNAME_LEN_NUM=$((10#$FNAME_LEN))

OFFSET=$((OFFSET+4))

FNAME=$(dd if="$PNG" bs=1 skip="$OFFSET" count="$FNAME_LEN_NUM" 2>/dev/null)

OFFSET=$((OFFSET+FNAME_LEN_NUM))

DATA_LEN=$(dd if="$PNG" bs=1 skip="$OFFSET" count=10 2>/dev/null)
DATA_LEN_NUM=$((10#$DATA_LEN))

OFFSET=$((OFFSET+10))

dd if="$PNG" bs=1 skip="$OFFSET" count="$DATA_LEN_NUM" of="$FNAME.enc" 2>/dev/null

openssl enc -d -aes-256-cbc -pbkdf2 -in "$FNAME.enc" -out "$FNAME"

rm "$FNAME.enc"

echo "Extracted $FNAME"

OFFSET=$((OFFSET+DATA_LEN_NUM))

tail -c +$OFFSET "$PNG" > tmpfile && mv tmpfile "$PNG"

done

Download extract_from_png.sh


Security Notes

  • Always use strong passwords.
  • Remove metadata from files before encryption.
  • Test extraction before deleting originals.
  • Store backups of encrypted data.