I have encrypted some of the data. Here my encryption converter is AES-256-CBC.
import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
class AESCipher(object):
def __init__(self, key):
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def _pad(self, s):
return s + (AES.block_size- len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
key = some key
raw='한글 (한국말)이라고도하는 한국어는 남한과 한국의 공식 언어입니다'
AESCipher_obj=AESCipher(key)
val = AESCipher_obj.encrypt(raw)
print(val)
When I execute my code it gives me that error.
> Traceback (most recent call last): File "aes2.py", line 27, in
> <module>
> s= AESCipher_obj.encrypt(raw) File "aes2.py", line 17, in encrypt
> return base64.b64encode(iv + cipher.encrypt(raw)) File "/usr/local/lib/python3.5/dist-packages/Crypto/Cipher/blockalgo.py",
> line 244, in encrypt
> return self._cipher.encrypt(plaintext) ValueError: Input strings must be a multiple of 16 in lengt
My input data is in the Korean language but When I try with the English language it is working perfectly. So how to encrypt for a specific language?
Assuming that you use a Python3 version, your string is an unicode string. You should encode it to an utf8 encoded byte string before padding it because the length will change. It works for ascii strings, because the length of the encoded byte string is the length of the unicode string.
So in your code, you must simply do:
key = some key
raw ='한글 (한국말)이라고도하는 한국어는 남한과 한국의 공식 언어입니다'
byte_raw = raw.encode('utf8')
AESCipher_obj=AESCipher(key)
val = AESCipher_obj.encrypt(byte_raw)
print(val)
After decrypting, you will get a byte string that should be decoded with .decode('utf8') to convert it to a Python3 (unicode) string.
Your Error:
ValueError: Input strings must be a multiple of 16 in length
AES is a block cipher, it works on 16-byte (128-bit) blocks.
The data needs to be padded until they're 16 bytes.
See this answer.
Hope this helps you!
try to encode() your string into byte string maybe ? and then padding it.
you can see here https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/Cipher/blockalgo.py
that it's working with bytestring instead of unicode (you are using python3)
your step are correct, and I see nothing wrong with the padding mechanism
Related
I am creating a simple XOR program in python. The user inputs simple plaintext which is converted to a string of bits. A key is generated of equal length to the plaintext bits, and it is XOR'd. I convert the XOR'd bits back to text and write it as ciphertext. This all works perfectly without issues.
The issue I am having is I want to take that same ciphertext and convert it back to its binary form in preparation for decryption. When I do so, I get a completely different string of bits.
Reproducible example:
import secrets
import sys
def encrypt(plaintext):
binary_plaintext = [format(x, 'b').zfill(8) for x in bytearray(plaintext, 'utf-8')] # Convert Plaintext to Binary and separates into bytes in a list
binary_plaintext = "".join(binary_plaintext) # Take list of bytes and join together in one long binary string
key = key_generator(binary_plaintext) # Create key of 'n' bits where n == len(binary_plaintext)
ciphertext_binary_string = "".join([str(int(bit1, 2) ^ int(bit2, 2)) for bit1, bit2 in zip(binary_plaintext, key[0])]) # XOR Operation with Key and Plaintext
ciphertext_binary_bytes = " ".join([ciphertext_binary_string[idex:idex+8] for idex in range(0, len(ciphertext_binary_string), 8)]) # Split the Ciphertext into bytes
ciphertext = ''.join([chr(int(byte,2)) for byte in ciphertext_binary_bytes.split(" ")]) # Convert Ciphertext to text i.e. ~ýÄÞ$~Ý
decrypt(ciphertext, key[0], ciphertext_binary_string) # Pass the exact same ciphertext to the decryption
def decrypt(ciphertext, key, previous_ciphertext):
ciphertext_binary = bin(int.from_bytes(ciphertext.encode('utf-8'), sys.byteorder))
print(f"{ciphertext_binary[2:]} does not match {previous_ciphertext}")
def key_generator(binary_plaintext):
integer_key = secrets.randbits(len(binary_plaintext)) # Build a random key that is the same length as the plaintext
binary_key = bin(integer_key)[2:].zfill(len(binary_plaintext))
return binary_key, integer_key
encrypt("Hello There")
Essentially there is:
Ciphertext in the form of bits on line 10
Convert this Ciphertext to text on line 12
Pass this same Ciphertext to decryption()
Convert the same Ciphertext back to it's binary format (as we have on line 10) on line 17 text back to bits on line 17.
Line 10 and line 17 do not match. This is what I am trying to understand. Binary > Text > Binary but the two binary forms do not match.
Any insight is appreciated. Thanks
def DecryptMethod(txt_to_decrypt, key):
#ct = txt_to_decrypt
f = open(txt_to_decrypt,'rb')
ct = f.read()
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = unpad(cipher.decrypt(ct), 16)
#decoded_txt = pt.decode()
print(pt)
return pt
testData = DecryptMethod("test1.enc",decrypted_key)
Within my tes1.enc the encrypted version of the text test1.
However, I am unable to decrypt. Everything was alright until
This part.
pt = unpad(cipher.decrypt(ct), 16)
The above part will constantly show the error:
TypeError: Object type <class 'str'> cannot be passed to C code
I've already checked multiple times, but this part constantly throws out this error.
Thank you in advance.
Plaintexts and ciphertexts (input/output) can only be bytes, bytearray or memoryview. In Python 3, you cannot pass strings. In Python 2, you cannot pass Unicode strings
You can convert the string to bytes like this:
b = bytes(mystring, 'utf-8')
For more information, read these docs
how do I use this pycrypto code in pycryptodome:
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
import os
# the block size for the cipher object; must be 16 per FIPS-197
BLOCK_SIZE = 16
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
PADDING = '{'
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
# generate a random secret key
secret = os.urandom(BLOCK_SIZE)
# create a cipher object using the random secret
cipher = AES.new(secret)
# encode a string
encoded = EncodeAES(cipher, 'password')
print 'Encrypted string:', encoded
# decode the encoded string
decoded = DecodeAES(cipher, encoded)
print 'Decrypted string:', decoded
At the very least, you need to replace:
cipher = AES.new(secret)
with:
cipher = AES.new(secret, AES.MODE_ECB)
PyCryptodome is almost compatible with PyCrypto, with the exceptions of a few dangeours APIs (like leaving ECB as default encryption mode).
I'm trying to encrypt a timestamp using AES-256 and Python with base64. The OpenSSL equivalent of the output is generated with this command:
openssl enc -aes256 -pass pass:'1Lw2*kx18#AvNuij*iRL1nY1UA_#k8$+' -nosalt -base64 <<< "1489355323"
My python code looks like so:
import time
from base64 import b64encode
from Crypto.Cipher import AES
key = '1Lw2*kx18#AvNuij*iRL1nY1UA_#k8$+'
timestamp = "1489355323"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
iv = "\x00" * 16
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt( pad( timestamp ) )
print b64encode(ciphertext)
Currently the output is different, and I need to get the same output as the OpenSSL command. Any idea what I'm doing wrong?
The key and iv that the OpenSSL enc command use are derived from the password by the EVP_BytesToKey function. You will need to reproduce that function to get your code to behave the same way.
In Python it might look like:
from hashlib import md5
# ...
last = ''
bytes = ''
# 32 byte key (256 bits) + 16 byte IV = 48 bytes needed
while len(bytes) < 48:
last = md5(last + password).digest()
bytes += last
key = bytes[0:32]
iv = bytes[32:48]
# ...
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt( pad( timestamp ) )
This scheme isn’t really recommended anymore, but the enc command still uses it. I believe OpenSSL is looking at providing a more up to date key derivation function in the future.
You also need to take care with newlines. The here string (<<<) adds a newline to the end of the string, you would need to add that to the string you are encrypting to get identical results:
timestamp = "1489355323\n"
Trying to solve Cryptopals Challenge 10 where have to CBC decrypt a text file against "YELLOW SUBMARINE" with an IV of all ASCII 0 (\x00\x00\x00 &c).
Link to the text file is following:
http://cryptopals.com/static/challenge-data/10.txt
I have followed the algorithm CBC uses by taking cipher text, decrypting(using ECB decryption) and then taking xor with Initialization Vector for first block and ciphertext(i-1) for subsequent blocks. However for some not-understandable reason I am not getting a readable decryption. I just see some weird characters when I print after decryption:
from Crypto.Cipher import AES
key ='YELLOW SUBMARINE'
iv = "%00%00%00"*32
iv = iv.replace('%',r'\x')
#XOR-ing function
def xor_strings(a, b):
return "".join(chr(ord(a1) ^ ord(b1)) for a1, b1 in zip(a, b))
#Taking input file and converting it into a single string
file = open('10.txt','r')
data = file.read()
block = 128
obj = AES.new(key, AES.MODE_ECB)
def split_len(string, size):
return [string[i:i+size] for i in range(0, len(string), size)]
mylist = split_len(data,block)
decrypted = ""
for i in range (0,len(mylist)):
mystr = obj.decrypt(mylist[i])
if (i==0):
decrypted = decrypted + xor_strings(mystr,iv)
else:
decrypted = decrypted + xor_strings(mystr, mylist[i-1])
print decrypted
What might be the problem here ?
The iv needs to be 16 zero bytes (the question is not clearly worded here when it says “ASCII 0”):
iv = "\x00" * 16
You need to base64 decode the file before decrypting it:
from base64 import b64decode
#...
file = open('10.txt','r')
data = file.read()
data = b64decode(data)
Finally your block size needs to be in bytes for this code to work, not bits:
block = 16