I want to do an AES encryption and decryption to string. but the key and message must be in bytes, so I converted the message to bytes by doing this:
b"string"
This is my AES code:
# Encryption
encryption_suite = AES.new(b'1234567812345678', AES.MODE_OCB)
cipher_text = encryption_suite.encrypt(b"A really secret message. Not for prying eyes.")
# Decryption
decryption_suite = AES.new(b'1234567812345678', AES.MODE_OCB)
plaintext = decryption_suite.decrypt(cipher_text)
however i need to turn the decrypted plain text back to string to be readable.
Currently the plaintext looks like this:
b'x\x85\x92\x9d\xe6\x0bJ\xfe\x9b(\x10G\x8e\x05\xc5\xf4\xcdA9\xc18\xb8_\xf9vbmK\x16\xf8\xa3\xb6'
I tried using
plaintext.decode(encoding='windows-1252')
and
plaintext.decode("utf-8").strip('\x00')
but all i get is this:
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3: character maps to
or this:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb1 in position 1: invalid start byte
I need to convert these bytes back to a readable string.. if you can help, that would be appreciated.
The main issue with your code is that you don't supply a nonce to AES.new(). OCB requires a nonce; if you don't supply one a random nonce will be created every time you create a new AES object, and so decryption will fail.
From the docs:
nonce (byte string): a non-repeatable value, of length between 1 and 15 bytes.. If not present, a random nonce of the recommended length (15 bytes) will be created.
You have two options, either create a nonce and pass it to AES.new() (in encryption and decryption) or use the random nonce created by AES during encryption.
Next, OCB is an authenticated encryption algorithm but it seems that you don't check the MAC. This is important because the MAC verifies the integrity of the ciphertext.
The encryption and decryption methods of AES accept and return bytes. You can convert the plaintext (if it is text) to string with .decode(). If you want to convert the ciphertext to string you'll have to base64-encode it first, in order to encode the raw bytes to ASCII characters (just remember to decode before decryption). b64encode() also returns bytes but can be converted to string easily.
An example,
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from base64 import b64encode
key = get_random_bytes(16) # A 16 byte key for AES-128
nonce = get_random_bytes(15)
message = "A really secret message. Not for prying eyes.".encode()
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
ciphertext, mac = cipher.encrypt_and_digest(message)
cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, mac)
print(b64encode(ciphertext).decode())
#CSwHy3ir3MZ7yvZ4CzHbgYOsKgzhMqjq6wEuutU7vJJTJ0c38ExWkAY1QkLO
print(plaintext.decode())
#A really secret message. Not for prying eyes.
Note that if .decrypt_and_verify() fails to verify the MAC a ValueError exception will be raised, so you may want to use a try-except block. The nonce and mac values are not secret and it's safe to store them next to the ciphertext.
Finally, if you plan to derive a key from a passphrase you should use a password-based KDF. KDFs create strong keys, use salt and iterations, and they are very resistant to fruteforce attacks. You will find KDF functions in Crypto.Protocol.KDF.
You can't convert binary data to a string because it is inherently not a string. It's binary data. The chances of the output of your encryption just happening, by chance, to be a correctly formatted UTF8 string is pretty unlikely.
You should look at base64 instead. It bloats the data (3 bytes to 4 characters) but is better suited for your use case.
EDIT:. My mistake, I misunderstood your question. The output you have isn't correct, the first byte is not a '1' in UTF8. This is likely an issue with the encryption/decryption.
I think your encoding is "ISO-8859-1". So you can do:
plaintext.decode("ISO-8859-1")
Related
When I decrypt my string with the correct password following this simple guide on the library's website I get a correct string response. When I change the final w to an e to get an invalid key, I get a padding bytes error like this stemming from:
lib/python3.8/site-packages/cryptography/hazmat/primitives/padding.py",
line 101, in _byte_unpadding_check
Which is then followed by the cryptography.fernet.InvalidToken error I was expecting.
ValueError: Invalid padding bytes.
During handling of the above exception, another exception occurred:
cryptography.fernet.InvalidToken
I've read Stackoverflow and can only find one example of this but it didn't seem to apply when this is just the actual example from their site.
What am I missing?
from cryptography.fernet import Fernet
# print(Fernet.generate_key()) # used this to get the below
bytes_key = b'tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOew='
# changed just one digit of key to test incorrect key
# bytes_key = b'tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOee='
f = Fernet(bytes_key)
# encrypt a message
# token = f.encrypt(b"A really secret message. Not for prying eyes.")
# print(token)
# b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd'
print(f.decrypt(b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd'))
print(f.decrypt(b'gAAAAABiIOCx9Pp8HB9Asq0HUXb-t0SwX-W5ue7O3GJfuF9Rm4ue8I0Drk36NIyuZ0Mufknad5xkoL3091ZGOOS-QTqMbM-MeNrEC9jvmYx_Y0ojoSTaZWO9AXMJFZgdnvgPTB-wQIQd').decode('utf-8'))
Details about the structure of the Fernet key, the Fernet token and the algorithms involved can be found here.
The Fernet key consists of the concatenation of a 16 bytes signing key and a 16 bytes encryption key, Base64url encoded.
For the posted valid Fernet key is:
Fernet key (Base64url): tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOew=
Signing key (hex): b6cb86ee8aa1c320ac7d4a1fdf96ed0c
Encryption key (hex): 903678cec07467f8b40334a35cd739ec
and for the posted invalid key:
Fernet key (Base64url): tsuG7oqhwyCsfUof35btDJA2eM7AdGf4tAM0o1zXOee=
Signing key (hex): b6cb86ee8aa1c320ac7d4a1fdf96ed0c
Encryption key (hex): 903678cec07467f8b40334a35cd739e7
Note that both encryption keys differ in the last byte (0xec vs 0xe7), i.e. your change has modified the encryption key!
The wrong key causes that the decryption generates a wrong plaintext with a wrong padding. It is not the length of the padding that is wrong, but the values of the padding bytes themselves.
Details: The plaintext used in the example:
A really secret message. Not for prying eyes.
consists of 45 bytes, i.e. the padding consists of 3 bytes, namely the byte sequence 0x030303, since PKCS#7 padding is applied. For PKCS7# padding all padding bytes have the same value and this value corresponds to the number of padding bytes, for details see PKCS#7. If the ciphertext is decrypted with the wrong key, the last three bytes are 0x07e3f2, which does not correspond to a valid PKCS#7 padding and generates the ValueError: Invalid padding bytes error message.
Note: If the padding is correct, it is automatically removed.
Similarly, an invalid signing key generates a corresponding error message InvalidSignature: Signature did not match digest.
The padding 0x07e3f2 can be determined as follows:
The structure of the token is: version (1 byte) || timestamp (8 bytes) || IV (16 bytes) || ciphertext || HMAC (32 bytes). Thus, from the token, the IV and ciphertext below can be derived:
IV: f4fa7c1c1f40b2ad075176feb744b05f
ciphertext: e5b9b9eecedc625fb85f519b8b9ef08d03ae4dfa348cae67432e7e49da779c64a0bdf4f7564638e4be413a8c6ccf8c78
This allows decryption with AES/CBC without padding using the appropriate tools (e.g. here) and results in the following byte sequence for decryption with the wrong key:
6afb18a9791e407ce0eafd1d2c2ae1cef403e94903cf80e90193fafd0681e58f0e8fff0d3f3542901bced6fc8e07e3f2
with 0x07e3f2 as invalid padding bytes.
In contrast, if the correct key is supplied for decryption, the result is:
41207265616c6c7920736563726574206d6573736167652e204e6f7420666f7220707279696e6720657965732e030303
with the correct padding 0x030303.
i have this piece of code for encryption.
from cryptography.fernet import Fernet
key = Fernet.generate_key()
f = Fernet(key)
token = f.encrypt(b"something cool")
k = f.decrypt(token)
print(k) `
This is the output
b'something cool'
According to the example on the website, that "b" should've gone. I'm very new at this and would like to know or understand how exactly the solution works.
Thanks
That ‘b’ means bytes. So instead of working with strings encryption algorythms are actually using bytes. My experience is that what you give a library (str/bytes/array) it should give you back, which Fernet is doing. I would simply convert the bytes back to a string k.decode(“utf-8”)
The encryption functions are doing what they should: bytes in and bytes out.
Cryptography and encryption work with bytes, not strings or other encoding, decrypt returns bytes. The actual low level decrypt has no idea of encodings, it can't the decryption could be a string, it could be an image, etc.
It is up to the caller to provide encodings in and out that are appropriate to the data being encrypted/decrypted.
As the caller wrap the encryption in a function you write that provides the correct encodings, in this case a string to bytes on encryption and bytes back to a string on decryption.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I want to encrypt a binary data into binary and then also decrypt in binary. How can I do this in python? I was trying to use AES but was unable to successfully do it.
Key = '00000000’
des = DES.new(key', DES.MODE_ECB)
plain_text = "10101011"
#encryption
cipher_text = des.encrypt(plain_text)
#decryption
decrypted_pt = des.decrypt(cipher_text)
You didn't specify, but your code makes it look like you're using ECB mode. Here's a short example of code I wrote for the cryptopals challenge, slightly modified to better fit your sample code. Make sure your key is 16 bytes long. Also, the plain text must be a multiple of 16 bytes. Another one of the challenges has you implementing a padding function.
Another thing to note is that after encrypting your data, the safest way to store that is in some sort of encoding, usually Base64 is used. Then when you go to decrypt it, you base64 decode the data first.
from Crypto.Cipher import AES
import base64
def ecb_encrypt(message, key):
""" Encrypts a message in AES ECB mode with a given key
ACCEPTS: Two strings, the plaintext message and the key
RETURNS: A bytes string of base64 encoded ciphertext
"""
aes = AES.new(key, AES.MODE_ECB)
return base64.b64encode(aes.encrypt(message)).decode()
def ecb_decrypt(encrypted, key):
""" Decrypts a ciphertext in AES ECB mode with a given key
ACCEPTS: Two strings, the base64 encoded ciphertext and the key
RETURNS: A bytes string of the plaintext message
"""
aes = AES.new(key, AES.MODE_ECB)
return aes.decrypt(base64.b64decode(encrypted))
if __name__ == "__main__":
Key = "0000000000000000"
plain_text = "1010101110101011"
cipher_text = ecb_encrypt(plain_text, Key)
decrypted_pt = ecb_decrypt(cipher_text, Key).decode()
print("Original message: {}".format(plain_text))
print("Encrypted message: {}".format(cipher_text))
print("Decrypted message: {}".format(decrypted_pt))
What you might be looking for is the xor bitwise operator in python.
Basically it takes every pair of bits in two numbers and returns 1 only and only if one of the bits is 1, otherwise it returns 0.
Input = int(raw_input('Encrypt/Decrypt this >>>'), 2) #input must be in bit format
key = 0b0100110 #'0b' indicates this is in second base
Encryption = key ^ Input
print Encryption
with "1101001" as an input the code will print 79 (which is 1001111)
Repeating that same process like so:
Decryption = key ^ Encryption
print Decryption
will print 105 which was our original input (105 = 1101001)
for more reading go to: https://wiki.python.org/moin/BitwiseOperators or https://www.tutorialspoint.com/python/bitwise_operators_example.htm
I assume you're using PyCrypto, so I'd suggest taking a look at this blog post which includes sample code and walks you through the process of encrypting/decrypting binary files (not worth duplicating the code here).
You might also want to check out simple-crypt which abstracts aways some of the tedious work in using PyCrypto.
I have a byte array that is a 128 bits AES key and I want to use that one on a Python script to cipher some information using the aforementioned key.
I have the key stored as a hexadecimal string, something like "27821D90D240EA4F56D0E7612396C69E" (obviously this is not the real key, but has the same format).
I have generated a byte array from that key, that is the way I have been using AES keys in other languages (Java, C# and PHP) so far, like this:
AES_KEY = bytearray.fromhex('27821D90D240EA4F56D0E7612396C69E')
That works fine, but then when I try to use it for creating the cipher, it complains that it wants an string in the first parameter:
cipher = AES.new(AES_KEY, AES.MODE_CBC, os.urandom(16));
TypeError: argument 1 must be string or read-only buffer, not
bytearray
I have tried to get an string from the byte array instead, as:
AES_KEY = bytearray.fromhex('27821D90D240EA4F56D0E7612396C69E').decode()
or
AES_KEY = bytearray.fromhex('27821D90D240EA4F56D0E7612396C69E').decode('utf-8')
to no avail because there are non-ascii and non-unicode values in that key.
Replacing the key is NOT an option.
Any ideas?
Thanks a lot in advance,
Apparently this does the trick:
AES_KEY = str(bytearray.fromhex('27821D90D240EA4F56D0E7612396C69E'))
It looks pretty obvious now, doesn't it?
I am working to decrypt data that was encrypted with DCPcrypt using Rijndael. I wanted to use Python to decrypt it but I'm running into issues. I'll mention that I'm not particularly crypto savvy (I took a college course, but that's about it) and I'm also not a Delphi programmer, so that is also probably hindering my efforts to decipher what precisely DCPcrypt is doing.
This is the meat of the Delphi code:
Cipher: TDCP_rijndael;
begin
Cipher:= TDCP_rijndael.Create(nil);
Cipher.InitStr(PasswordField.Text);
Cipher.EncryptCBC(encryptString[1],encryptString[1],Length(encryptString));
So the implementation uses a key (obtained from the password field) but no IV. PyCrypto on the other hand requires an IV. Searching through the internals of the DCPcrypt code, it appears that if the IV is nil, then an ECB encryption is used to populate the IV from a string of 0xff?
procedure TDCP_rijndael.Init(var Key; Size: longint; IVector: pointer);
....
if IVector= nil then
begin
FillChar(IV,Sizeof(IV),$FF);
{$IFDEF CFORM}Encrypt(IV,IV){$ELSE}RijndaelEncryptECB(Data,IV,IV){$ENDIF};
Move(IV,LB,Sizeof(LB));
end
It appears that I'm using a static IV. However, I am not able to make this work. Here's my implementation in PyCrypto. Any ideas what I'm doing wrong?
key = "password"
s = hashlib.sha1()
s.update(key)
key = s.digest()
key = key[:16]
# Set up the IV, note that in ECB the third parameter to the AES.new function is ignored since ECB doesn't use an IV
ecb = AES.new(key, AES.MODE_ECB, '\xff' * 16)
iv = ecb.encrypt('\xff' * 16)
cipher = AES.new(key, AES.MODE_CFB, iv)
msg = cipher.decrypt(ct[:16])
I have some plain text that was encrypted using the Delphi code and then base64 encoded. The key used was the string password, as hardcoded in above. Using my implementation, I decrypt a bunch of garbled bytes.
k8b+uce5Fkp7Hbk/CaGYcuEWTfxlI05as88lJL0mHmJxLsKWqki2YwiFPU9Rx8qiUC2cvWZrQIOnkw==
Any help is greatly appreciated.
A random assortment of suggestions and thoughts:
Static IVs are generally a security risk as they open the door to known plaintext attacks.
Looking at the dcpcrypt source, it looks like there is a method to specify an IV. Any reason not to, if for no other reason than to eliminate incorrect IV as an error source?
Likewise, you could experiment with ECB instead of CBC to eliminate the IV altogether and isolate whether the problem is in the IV or elsewhere (key, data, or configuration).
DISCLAIMER: I'm extremely new to Python, but try this:
Try changing
key = key[:16]
to
key = key + bytes([0,0,0,0])
This will give you a 24 byte key which, I think, should work.
DCP allows any length of key whereas Crypto insists on keys of 16, 24 or 32 bytes. By default DCP will use SHA1 to generate the key, which is then 20 bytes long. Based on this DCP uses logic for keylength <= 24 and just zero pads the key rather than logic for keylength <= 16 which is the effect that key[:16] is having.
Also, don't know if this is just a typo but try changing the AES mode
cipher = AES.new(key, AES.MODE_CFB, iv)
to
cipher = AES.new(key, AES.MODE_CBC, iv)
That will give a decryption of your input. But then you'll need to consider padding of the original source text because DCP does not pad (I think) but Crypto requires multiples of 16 for decryption.