Produce the ECB penguin with AES in python

时间秒杀一切 提交于 2021-01-28 03:13:50

问题


I want to encrypt data in an image but the resulting ciphertext to still be a valid image. I encrypt the image with AES in python and then, I replace header in the files, but windows can't open the encrypted image.

Code

def encrypt_file(self, in_filename, out_filename):
    filesize = os.path.getsize(in_filename)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(self.key, AES.MODE_ECB, iv)
    chunksize = 64 * 1024
    with open(in_filename, 'rb') as infile:
        with open(out_filename, 'wb') as outfile:
            outfile.write(struct.pack('<Q', filesize))
            outfile.write(iv)

            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                cifrado = base64.b64encode(cipher.encrypt(chunk))
                print cifrado
                outfile.write(cipher.encrypt(chunk))

I want this effect: The ECB Penguin


回答1:


PyCrypto and the Python Image Class had very useful examples on playing around with images and the AES encryption.

This implementation only works with BMP images that have certain characteristics. The main characteristic that an image must have for this solution to work is that it has to be of a size that's multiple of 16 bytes (for the encryption part, AES ECB operates on 16 byte chunks). You can work on improving this to accept more image formats and pad to 16 byte multiples :)

If you don't provide an image, an appropriate image from the web is automatically downloaded.

im_show from Image is known to cause problems on some platforms. I tested this on an Ubuntu 14.10 distro and didn't run into any problems. This was tested on Python 2.7, I'm still working on my portability skills (You didn't specify a Python version in your question so...)

#!/usr/bin/python
import binascii, os.path, urllib, random, Image
from Crypto.Cipher import AES

class ECBPenguin(object):
    '''
    A penguin class
    '''
    def __init__(self, img_clr=""):
        if not img_clr:
            self.__demo_image__()
            self.img_clr = "tux_clear.bmp"
        else:
            self.img_clr = img_clr
        self.__get_header__()

    def __demo_image__(self):
        ''' 
        Downloads a TUX image compatible for this program: square and with size multiple of 16
        '''
        print "Downloading image..."
        image = urllib.URLopener()
        image.retrieve("http://fp-games.googlecode.com/svn/trunk/CodeWeek1/graviTux/data/tux.bmp","tux_clear.bmp") 

    def __get_sizes__(self, dibheader):
        # Get image's dimensions (at offsets 4 and 8 of the DIB header)
        DIBheader = []
        for i in range(0,80,2):
            DIBheader.append(int(binascii.hexlify(dibheader)[i:i+2],16))
        self.width = sum([DIBheader[i+4]*256**i for i in range(0,4)])
        self.height = sum([DIBheader[i+8]*256**i for i in range(0,4)])

    def __get_header__(self):
        '''
        Read BMP and DIB headers from input image and write them to output image
        '''
        f_in = open(self.img_clr, 'rb')
        # BMP is 14 bytes
        bmpheader = f_in.read(14)
        # DIB is 40 bytes
        dibheader = f_in.read(40)
        self.__get_sizes__(dibheader)
        self._bmpheader = bmpheader
        self._dibheader = dibheader
        f_in.close()

    def encrypt(self, img_enc = "tux_enc.bmp", key = '0123456789abcdef'):
        '''
        Encrypt the my_penguin
        '''
        self.img_enc = img_enc
        f_in = open(self.img_clr, 'rb')
        f_out = open(img_enc, 'wb')
        f_out.write(self._bmpheader)
        f_out.write(self._dibheader)
        row_padded = (self.width * self.height * 3)
        image_data = f_in.read(row_padded)
        cleartext =  binascii.unhexlify(binascii.hexlify(image_data))

        # Initialization Vector
        IV = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
        # AES ECB mode
        mode = AES.MODE_ECB
        # Encryptor
        encryptor = AES.new(key, mode, IV=IV)
        # Perform the encryption and write output to file
        f_out.write(encryptor.encrypt(cleartext))
        f_in.close()
        f_out.close()

    def show_clr(self):
        '''
        Display cleartext penguin
        '''
        im = Image.open(self.img_clr)
        im.show()

    def show_enc(self):
        '''
        Display ciphertext penguin
        '''
        im = Image.open(self.img_enc)
        im.show()

def main():
    my_penguin = ECBPenguin()
    my_penguin.show_clr()
    my_penguin.encrypt()
    my_penguin.show_enc()

if __name__ == "__main__":
    main()

The initial and encrypted images look like this:

Plaintext TUXECB "Encrypted" TUX

I couldn't find the same image as the one in your link, but the point of weakness of ECB is still made!




回答2:


In simple terms:

  1. Grab the original image in .BMP format.

  2. Keep the original BMP header unencrypted.

  3. Encrypt just the image, not the header.

  4. Put the original unencrypted header back in front of the encrypted image.

You may need to tweak the header a little if the encryption has added a few padding bytes to the image size.



来源:https://stackoverflow.com/questions/29039773/produce-the-ecb-penguin-with-aes-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!