Search code examples
image-processingpublic-key-encryptionencryption-asymmetric

Create jpg/png from encrypted image


I'd like to be able to convert/display an AES256 asymmetric encrypted image even if it appears to be garbage, I've read a number of things on SO that suggest removing the headers and then reattaching them afterward so even if looks nutty it still displays it.

The point of this is that I want to see if it's possible to perform image classification on an image dataset encrypted with a known public key. If I have a picture of a cat and I encrypt it with exactly the same key, then the result will generally be reproducible and result in an image that in some way equates to the original.

Excuse the lack of code, I didn't want to pollute the discussion with ideas that I was considering in order to get a proper critique from you lovely people- I would say I'm not an encryption expert hence my asking for advice here.


Solution

  • There are many options, but I suggest to follow the following guidelines:

    • Encrypt the image data, and not the image file.
      In case the image is 100x100x3 bytes, encrypt the 30000 bytes (not the img.jpg file for example).
      (The down side is that metadata is not saved as part of the encrypt image).
    • Use lossless image file format to store the encrypted image (PNG file format for example, and not JPEG format).
      Lossy format like JPEG is going to be irreversible.
    • Set the resolution of the encrypted image to the same resolution as the input image.
      That way you don't need to store the image headers - the resolution is saved.
      You may need to add padding, so the size in bytes be a multiple of 32.

    I hope you know Python...

    Here is a Python code sample that demonstrates the encoding and decoding procedures:

    import cv2
    import numpy as np
    from Crypto.Cipher import AES
    
    # https://stackoverflow.com/questions/61240967/image-encryption-using-aes-in-python
    key = b'Sixteen byte key'
    iv = b'0000000000000000'
    
    # Read image to NumPy array - array shape is (300, 451, 3)
    img = cv2.imread('chelsea.png')
    
    # Pad zero rows in case number of bytes is not a multiple of 16 (just an example - there are many options for padding)
    if img.size % 16 > 0:
        row = img.shape[0]
        pad = 16 - (row % 16)  # Number of rows to pad (4 rows)
        img = np.pad(img, ((0, pad), (0, 0), (0, 0)))  # Pad rows at the bottom  - new shape is (304, 451, 3) - 411312 bytes.
        img[-1, -1, 0] = pad  # Store the pad value in the last element
    
    img_bytes = img.tobytes()  # Convert NumPy array to sequence of bytes (411312 bytes)
    enc_img_bytes = AES.new(key, AES.MODE_CBC, iv).encrypt(img_bytes)  # Encrypt the array of bytes.
    
    # Convert the encrypted buffer to NumPy array and reshape to the shape of the padded image (304, 451, 3)
    enc_img = np.frombuffer(enc_img_bytes, np.uint8).reshape(img.shape)
    
    # Save the image - Save in PNG format because PNG is lossless (JPEG format is not going to work).
    cv2.imwrite('enctypted_chelsea.png', enc_img)
    
    
    
    # Decrypt:
    ################################################################################
    key = b'Sixteen byte key'
    iv = b'0000000000000000'
    
    enc_img = cv2.imread('enctypted_chelsea.png')
    
    dec_img_bytes = AES.new(key, AES.MODE_CBC, iv).decrypt(enc_img.tobytes())
    
    dec_img = np.frombuffer(dec_img_bytes, np.uint8).reshape(enc_img.shape)  # The shape of the encrypted and decrypted image is the same (304, 451, 3)
    
    pad = int(dec_img[-1, -1, 0])  # Get the stored padding value
    
    dec_img = dec_img[0:-pad, :, :].copy()  # Remove the padding rows, new shape is (300, 451, 3)
    
    # Show the decoded image
    cv2.imshow('dec_img', dec_img)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    Encrypted image:
    enter image description here

    Decrypted image:
    enter image description here


    Idea for identifying the encrypted image:

    • Compute a hash of the encrypted image, and store it in your database, along the original image, the key and the iv.
    • When you have the encrypted image, compute the hash, and search for it in your database.