As per the Example given in the documentation of PyCryptodome
>>> from Crypto.Hash import CMAC
>>> from Crypto.Cipher import AES
>>> secret = b'Sixteen byte key'
>>> cobj = CMAC.new(secret, ciphermod=AES)
>>> cobj.update(b'Hello')
>>> print cobj.hexdigest()
it generates the AES CMAC but when I try the test vector from RFC4493, I get the wrong CMAC.
for example, the test vectors from RFC4493 are:
K 2b7e1516 28aed2a6 abf71588 09cf4f3c
M 6bc1bee2 2e409f96 e93d7e11 7393172a
AES-CMAC 070a16b4 6b4d4144 f79bdd9d d04a287c
But when I tried the same key and message
>>> from Crypto.Hash import CMAC
>>> from Crypto.Cipher import AES
>>> secret = b'2b7e151628aed2a6abf7158809cf4f3c'
>>> cobj = CMAC.new(secret, ciphermod=AES)
>>> cobj.update(b'6bc1bee2 2e409f96 e93d7e11 7393172a')
>>> print cobj.hexdigest()
I got the following output
a3f10a99bd83f4dee4392d65ed9f76c1
The problem in your code is that your are not treating the message or key 2b7e1516 28aed2a6 abf71588 09cf4f3c as a hex number which it is in this case but you are treating it as bytes where each character is stored in it's ASCII representation rather than being stored as the actual value of hex character so f is stored as binary 0100 0110 rather than as binary 1111. While the RFC deal with input as the numbers encoded as HEX characters, so use this code:
from Crypto.Hash import CMAC
from Crypto.Cipher import AES
secret = "2b7e151628aed2a6abf7158809cf4f3c"
msg = "6bc1bee22e409f96e93d7e117393172a"
cobj = CMAC.new(bytes.fromhex(secret), ciphermod=AES)
cobj.update(bytes.fromhex(msg))
res = cobj.hexdigest()
print(res)
which will print correct result of
070a16b46b4d4144f79bdd9dd04a287c
Hope this solve your problem.
Related
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
I always fail with the decrypting of a RC4 encrypted object. My key has to be the MD5-Hash of this hex string:
00 00 00 01 3e 2a 5b 71 00 00 03 a0
What i tried was to convert that hex string to ascii and calculate afterwards the MD5 hash of it, but it seems like my key is always wrong. I think there is a problem because some of the hex values are control characters, but how is then the correct way to calculate the MD5 of this hex string ? What i thought of was something like this:
from Crypto.Cipher import ARC4
from Crypto.Hash import MD5
def hexToAscii(hex_string):
return ''.join([chr(int(''.join(c), 16)) for c in zip(hex_string[0::2],hex_string[1::2])])
def main():
hex_string = '000000013e2a5b71000003a0'
# Key for Decryption
myKey = MD5.new(hexToAscii(hex_string)).hexdigest()
print 'hexToAscii(hex_string): %s' % hexToAscii(hex_string)
#open('myfile','wb').write(ARC4.new(hexToAscii(myKey)).decrypt(hexToAscii(CIPHER_TEXT)))
if __name__ == '__main__':
main()
The main function prints hexToAscii(hex_string) instead of myKey.
BTW, you'd better to use binascii.unhexlify instead of hexToAscii. And you can use hashlib module to calculate md5.
>>> import hashlib
>>> import binascii
>>> hex_string = '000000013e2a5b71000003a0'
>>> hashlib.md5(binascii.unhexlify(hex_string)).hexdigest()
'6afebf522c531575e96d6814be816c7c'
Use hashlib and binascii from Python standard lib, no conversion to ASCII involved:
import binascii
import hashlib
base = binascii.unhexlify("000000013e2a5b71000003a0")
key = hashlib.md5(base).digest()
I need help converting the following PHP to python
$plaintext = "MyPassword";
$utf_text = mb_convert_encoding( $plaintext, 'UTF-16LE' );
$sha1_text = sha1( $utf_text, true );
$base64_text = base64_encode( $sha1_text );
echo $base64_text; //ouput = QEy4TXy9dNgleLq+IEcjsQDYm0A=
Convert the string to UTF16LE
Hash the output of 1. using SHA1
Encode the output of 2. using base64 encoding.
Im trying hashlib.sha1 but its not working. Maybe due to this, maybe encodings. Can anyone help
Your PHP code encodes the password to UTF16, little endian; if your password value is a unicode value, that works just fine in Python:
>>> import hashlib
>>> plaintext = u"MyPassword"
>>> utf_text = plaintext.encode('UTF-16LE')
>>> hashlib.sha1(utf_text).digest().encode('base64')
'QEy4TXy9dNgleLq+IEcjsQDYm0A=\n'
The above session was done in Python 2; in Python 3 the u prefix can be omitted as all string values are Unicode.
Got it, i was using the hashlib.sha1 incorrectly.
import hashlib
password = "MYPASSWORD"
result ='QEy4TXy9dNgleLq+IEcjsQDYm0A='
utftext = password.encode("utf-16LE")
m = hashlib.sha1()
m.update(utftext)
sha1text = m.digest()
output = sha1text.encode('base64','strict')
print "output: %s" % output
print "expect: %s" % result
I don't see why this wouldn't work. This works like a charm and prints out the desired result, QEy4TXy9dNgleLq+IEcjsQDYm0A=
from hashlib import sha1
from base64 import b64encode
print(b64encode(sha1("MyPassword".encode("utf-16le")).digest()))
I'm using PyCrypto in an application to encrypt data, but for some reason the first 8 bytes (corresponding to the first block) are coming through corrupt no matter what I do.
>>> from Crypto.Cipher import DES3
>>> from Crypto import Random
>>> iv = Random.new().read(DES3.block_size)
>>> key = Random.new().read(DES3.key_size[-1])
>>> des3 = DES3.new(key, DES3.MODE_CBC, iv)
>>> des3.decrypt(des3.encrypt('12345678abcdefgh12345678'))
't\x1b\x0f\xcbD\x15M\xababcdefgh12345678'
I've read that that's a sign that the IV is corrupt, but those sources also say that using a mode other than CBC would result in the entire message corrupting. That isn't the case:
>>> des3 = DES3.new(key, DES3.MODE_CFB, iv)
>>> des3.decrypt(des3.encrypt('12345678abcdefgh12345678'))
'\xe1\x85\xae,\xf1m\x83\x9cabcdefgh12345678'
I can also rule out the cipher as the cause:
>>> from Crypto.Cipher import AES
>>> from Crypto import Random
>>> iv = Random.new().read(AES.block_size)
>>> key = Random.new().read(AES.key_size[-1])
>>> aes = AES.new(key, AES.MODE_CBC, iv)
>>> aes.decrypt(aes.encrypt('12345678abcdefgh12345678abcdefgh'))
'\xa7l\x00]\x1cW\xec\xd0\x04\x06\xba&\x1663\xd712345678abcdefgh'
Note that in this example the first 16 bytes are corrupt, which corresponds to AES' block size.
You have to reset IV vector before decryption. Try this code:
>>> from Crypto.Cipher import DES3
>>> from Crypto import Random
>>> iv = Random.new().read(DES3.block_size)
>>> key = Random.new().read(DES3.key_size[-1])
>>> des3enc = DES3.new(key, DES3.MODE_CBC, iv)
>>> des3dec = DES3.new(key, DES3.MODE_CBC, iv)
>>> des3dec.decrypt(des3enc.encrypt('12345678abcdefgh12345678'))
IV vector is changing after encryption / decryption each block. You used the same instance of DES3 class for encrypting and decrypting the message, therefore you had incorrect IV for decryption.
Hope above code works - I didn't test it.
More about CBC mode: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
This is the code:
>>> import base64
>>> id = 1
>>> key = "secret key very long"
>>> enc = base64.urlsafe_b64encode(str(id)+key)
>>> enc
'MXNlY3JldCBrZXkgdmVyeSBsb25n'
>>> base64.urlsafe_b64decode(enc)
'1secret key very long'
Works as intended on my machine, but when I upload this code to google appengine, both encoded and decoded strings are totally different. How come?
EDIT 1:
this is the actual code:
import base64
id = 18005
key = "r-$b*8hglm+858&9t043hlm6-&6-3d3vfc4((7yd0dbrakhvi"
enc = base64.urlsafe_b64encode(str(id)+key)
print enc
# local machine: MTgwMDVyLSRiKjhoZ2xtKzg1OCY5dDA0M2hsbTYtJjYtM2QzdmZjNCgoN3lkMGRicmFraHZp
# appengine: PXItJGIqOGhnbG0rODU4Jjl0MDQzaGxtNi0mNi0zZDN2ZmM0KCg3eWQwZGJyYWtodmkxODAwNQ==
I can't explain why per se, but decoding the string you got from appengine shows it prepended an '=' to your key; and appended, rather than prepended, the ID.
>>> key='r-$b*8hglm+858&9t043hlm6-&6-3d3vfc4((7yd0dbrakhvi'
>>> base64.urlsafe_b64decode('PXItJGIqOGhnbG0rODU4Jjl0MDQzaGxtNi0mNi0zZDN2ZmM0KCg3eWQwZGJyYWtodmkxODAwNQ==')
'=r-$b*8hglm+858&9t043hlm6-&6-3d3vfc4((7yd0dbrakhvi18005'
>>> '=' + key + str(18005) == _
True
are you absolutely sure you used the same code on the server?