问题
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:
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:
Grab the original image in .BMP format.
Keep the original BMP header unencrypted.
Encrypt just the image, not the header.
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