Encryption not working properly - python

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.

Related

Bytes to string in AES encryption and decryption in Python 3

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")

encrypt a binary data into binary and also decrypt [closed]

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.

Using a byte array as key for AES algorithm in Python

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?

Python RSA encrypted message pass to other languages to decypt

I am using the RSA python package to encrypt a message and try to pass it to a PHP site to decrypt. See below:
message = rsa.encrypt('abc', pubkey)
print message
print type(message)
What I get is some encrypted text
q??$$??kK?Y??p?[e?[??f???x??s!?s?>?z?*y?p?????????分?
? ???({u????NH?B???N?%?#5|?~?????\U?.??r?Y?q
<type 'str'>
What's the best way to pass it to other languages to decrypt?
That's not a text, it's a binary data. As you are using python2 it doesn't fully distinguish bytes from str so you should care about this.
The other side should get this bytes exactly as rsa outputs them so you can just write them into your connection or file (presuming you are talking binary to them).
For web you can base64 encode the data. It's a common and good way to encode binary data.
>>> import base64
>>> base64.b64encode(b"data")
'ZGF0YQ=='
>>> base64.b64decode(base64.b64encode(b"data"))
'data'
By the way you are not suppose to use RSA that way. It's highly insecure to use raw RSA. You must use a probabilistic encryption scheme with proper padding, e.g. RSAES-OAEP. PKCS#1 defines such scheme for encryption and signatures.

PyCrypto: Encode chinese charaters with RSA asymmetric key

I am trying to use PyCrypto to encrypt/decrypt some strings, and I am having troubles with Chinese characters.
When I try to encrypt "ni-hao" (hello)...
pemFile = open("/home/borrajax/keys/myKey.pem", "r")
encryptor = RSA.importKey(pemFile, passphrase="f00")
return encryptor.encrypt("你好", 0)[0]
... I keep getting errors:
Module Crypto.PublicKey.pubkey:64 in encrypt
>> ciphertext=self._encrypt(plaintext, K)
Module Crypto.PublicKey.RSA:92 in _encrypt
>> return (self.key._encrypt(c),)
ValueError: Plaintext too large
I have tried many combinations,
encryptor.encrypt(u"你好"...
encryptor.encrypt(u"你好".encode("utf-8")...
without any luck.
I guess I could always try to use base64 before encoding, but I'd like to leave that as a "last resource"... I was hoping for a more "elegant" way of doing this.
Has anyone encountered the same problems? Any hint will be appreciated. Thank you in advance.
First, you should be encrypting only binary data, not Unicode text. That means str type (in Python 2.x) or bytes (in Python 3.x and most recent Python 2.x). You must encode text before encrypting it, and you must decode it after you decrypt.
Second, the byte string you are encrypting must be smaller than the RSA modulus (e.g. less than 256 bytes for RSA2048). If your data is longer, think of using an intermediate AES session key.
Third, if you use PyCrypto 2.5, there is no good reason for using the .encrypt/.decrypt methods of the RSA key object. It is better and more secure to use one of the PKCS#1 methods: OAEP or v1.5. With them, the plaintext must be even shorter.
I tested with PyCrypto v2.5 installed from pip on Ubuntu Linux 10.04 on python 2.6.5 in the interactive interpreter from the yakuake terminal.
I'm not able to reproduce the error you're seeing, especially the "Plaintext too large" bit. Some errors I have seen:
encryptor.encrypt(u"你好",0)[0]
TypeError: argument 1 must be long, not unicode
Seems like it doesn't like unicode objects - only wants str.
Both of these work on my setup, and both produce the same output, however the first solution is more correct:
encryptor.encrypt(u"你好".encode("utf-8"), 0)[0]
encryptor.encrypt("你好", 0)[0]
Are you trying this from the interactive interpreter, or from a file? If file, is the file UTF-8 encoded? If console, does it have proper UTF-8 support?
I checked related codes of PyCrypto, this error is only thrown when plain-text (after converted to long) is bigger than one of the key parameter. Assuming encoding of the script is correctly set, it may be because your RSA key is invalid or too short. I tried this snippet, it works with no problem:
rsa = RSA.generate(1024)
print(rsa.encrypt("你好", 0))

Categories