So I am using pycryptodome to encrypt a message using a secret key with AES. I want to then, as a test, decrypt the encrypted message using AES with the same secret key. I have done that here, but the result of the decrypted message is not the same as the encrypted message. Maybe I am misunderstanding how AES works, but I would assume that I should be able to decrypt a message encrypted with AES if I have the same secret key, but it would appear that I'm wrong. How do I make this work properly?
finalCipher = AES.new(sKey, AES.MODE_CFB)
message = input()
#Encrypt the message using the cipher
enMessage = message.encode('utf-8')
encMessage = finalCipher.encrypt(enMessage)
print(encMessage)
#Create a new cipher to decrypt the encrypted message, using the same key
otherCipher = AES.new(sKey, AES.MODE_CFB)
print(otherCipher.decrypt(encMessage))
I realized that I need more than just the original secret key to create a cipher that can decrypt messages encrypted using the original cipher. The original cipher I created has an attribute "iv" that I need to use in the constructor of the new cipher in order to be able to use it to decrypt properly, by doing this instead:
finalCipher = AES.new(sKey, AES.MODE_CFB)
message = input()
#Encrypt the message using the cipher
enMessage = message.encode('utf-8')
encMessage = finalCipher.encrypt(enMessage)
print(encMessage)
#Create a new cipher to decrypt the encrypted message, using the same key
otherCipher = AES.new(sKey, AES.MODE_CFB, finalCipher.iv)
print(otherCipher.decrypt(encMessage))
Related
New rephrased Question
There are two programs that work together, a client and a server.
The client is having issues decrypting, and i have ran the following test on the client without any server interaction and this does not work.
I get rsa.pkcs1.DecryptionError: Decryption failed when i run this code on the client.
# Public key saved in ini file as this format "PublicKey(n, e)"
# Private key saved in ini file as this format "PrivateKey(n, e, d, p, q)"
key_string = public_key.strip("PublicKey(").strip(")")
n, e = key_string.split(", ", 1)
value = rsa.encrypt(b"Hello", public_key)
key_string = self.private_key.strip("PrivateKey(").strip(")")
n, e, d, p, q = key_string.split(", ", 4)
private_key = rsa.PrivateKey(int(n), int(e), int(d), int(p), int(q))
decrypted = rsa.decrypt(value, private_key)
Old "Question" asked
I am writing a python program that is essentially a P2P chat
application utilising a rendezvous server for new connections.
Walkthrough of the steps taken by client/server.
Client:
Connects to server using sockets
Sends its public key to server
Server:
Reads public key
Creates AES key and ciphers a message (list of already connected peers)
Encrypts the AES Key using the clients RSA public key
Sends the key and ciphertext
Client:
Reads the information and splits into the key portion and the ciphertext portion
Decrypts the AES Key (However this fails even though the same code works on the server to decode)
Decrypts the cipher text using the now unencrypted AES Key
# Encrypt with AES cipher_text, key, nonce = self.aes.encrypt(json.dumps(message))
# Encrypt AES Key with RSA encrypted_key = self.rsa.encrypt(key, peer['public_key'])
# Send data to peer self.socket.sendto(encrypted_key + nonce + cipher_text, peer['address']) ```
``` CLIENT CODE
data, address = self.socket.recvfrom(65536) recv = {"key": data[:256],
"nonce": data[256:272], "data": data[272:]}
key = self.rsa.decrypt(recv["key"]) peers =
json.loads(self.aes.decrypt(recv["data"], key, recv["nonce"])) ```
Solved this, Thanks for the help!
The error was with my import of the config file, my statement was checking if there was a valid RSA-pub/priv key and if there wasn't it would generate a new pair for the user.
The problem was it was always generating a new keypair. meaning it was attempting to decrypt with the incorrect private key.
I've got a encrypt/decrypt class setup based on this SO answer. I've tested it and it works fine. It's not working for a new API I'm pulling information from. The new API is built with PHP and is using the following package to encrypt information: https://laravel.com/docs/8.x/encryption using Laravel Crypt() command. All encrypted values are encrypted using OpenSSL and the AES-256-CBC cipher.
The enc value after the first line of the decrypt method
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
looks like this (Don't worry, this is just non-sensitive demo data):
b"{'iv': 'Ld2pjRJqoKcWnW1P3tiO9Q==', 'value': 'M9QeHtbybeUxAVuIRcQ3bA==', 'mac': 'xxxxxx......'}"
, which basically looks like a byte-string JSON. The testing encryption key is base64:69GnnXWsW1qnW+soLXOVd8Mi4AdXKBFfkw88/7F2kSg=.
I know I can turn it into a dictionary like this
import json
d = json.loads(enc)
How should I manipulate values from this dictionary to prepare it to be decrypted like other encrypted text this class can successfully decrypt?
Update:
Based on comments I've tried to modify the method to look like this:
def decrypt(self, encrypted):
enc = base64.b64decode(encrypted)
if b'{"iv":' == enc[:6]:
d = json.loads(enc)
iv = base64.b64decode(d['iv'])
val = base64.b64decode(d['value'])
else:
iv = enc[:AES.block_size]
val = enc[AES.block_size:]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(val)).decode('utf-8')
This still does not work. It doesn't crash, but I'm getting a blank string back ('') and I know that's not what was encrypted. The answer should be 'Demo'
The code in the "Update" section of the question will work without any changes. You just need to make sure to remove the "base64:" prefix in the encryption key provided. Once that is removed, it will work as expected.
I created a database using django and created a html form to store the data into the database.
Now, I want to encrypt using pycryptodome and store the ciphertext into the database.
And I want the decrypted plaintext when I display the data from the database.
I have tried some basic encryption examples and algorithms from pycrytodome documentation.
Now, I want to encrypt and store the ciphertext into the database.
#This is the function where I want to encrypt the data in the get_password variable and store it
def add(request):
get_name = request.POST["add_name"]
get_password = request.POST["add_password"]
print(type(get_name),type(get_password))
s = Student(student_name=get_name,student_password=get_password)
context = { "astudent":s }
s.save()
return render(request,"sms_2/add.html",context)
#This is the example I have tried from pycryptodome documentation.
from Crypto.Cipher import AES
key=b"Sixteen byte key"
cipher=AES.new(key,AES.MODE_EAX)
data=b"This is a secret##!!"
nonce = cipher.nonce
ciphertext, tag = cipher.encrypt_and_digest(data)
print(nonce)
print(key)
print(cipher)
print(ciphertext)
print(tag)
cipher1 = AES.new(key, AES.MODE_EAX, nonce=nonce)
plaintext = cipher1.decrypt(ciphertext)
try:
cipher1.verify(tag)
print("The message is authentic:", plaintext)
except ValueError:
print("Key incorrect or message corrupted")
I want to encrypt the plaintext I enter into the database using the html form and I want to ciphertext to be stored into the database.
I want help with that.
I'm using python to encrypt and decrypt files. When file encrypted, then try to decrypt like this:
from Crypto.Cipher import AES
from Crypto import Random
def decrypt(in_file, out_file, pwd, key_len=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_keyiv(pwd, salt, key_len, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
try:
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024*bs))
if len(next_chunk) == 0:
padding_len = ord(chunk[-1])
chunk = chunk[:-padding_len]
finished = True
out_file.write(chunk)
return True, None
except Exception as e:
return False, e
But if the password input error, the decrypt still decrypt in_file and write to out_file and no exception throw.
How to check the password error during decrypt?
AES by itself cannot check if the key is "correct". It is simply a pure function that transforms some bytes to some other bytes.
To achieve what you want, you need to implement it yourself. One way to do is to add a fixed header (like 16 bytes of zero) to the plaintext before encryption. Upon decryption, check and discard the said header if it matches or raise an error if the header mismatches.
A side note: you are doing encryption without any authentication, which is probably insecure.
Edit
First of all, you should add authentication. Encryption without authentication easily leads to many security flaws, many not obvious to the untrained. Especially since you are using AES in CBC mode, you may open yourself to padding oracle attacks without authentication.
When you do authenticated encryption the right way (encrypt-then-mac), you will get an authentication error if the user input the wrong password. If you want to further distinguish a wrong password from tampered data, you have to devise your own method, like prepending a ciphertext of magic number.
I got a problem with aes in python 2.7
import pyelliptic
iv = pyelliptic.Cipher.gen_IV('aes-256-cfb')
ctx = pyelliptic.Cipher("secretkey", iv, 1, ciphername='aes-256-cfb')
ciphertext = ctx.update('test1')
ciphertext += ctx.final()
ctx2 = pyelliptic.Cipher("secretkey", iv, 0, ciphername='aes-256-cfb')
Now I don't know how to send this msg to server, and decrypt it on server, because I don't know the IV and my server can't decrypt it. The server has the secret key.
The IV does not need to be kept secret, but it needs to unique (random) for every encrypt operation with the same key.
Many implementations just add the IV bytes to the front of the ciphertext. You have to know how long the IV is for your implementation so that you can slice it off before decrypting.
# encrypt
ciphertext = iv + ciphertext
# decrypt
blocksize = pyelliptic.Cipher.get_blocksize('aes-256-cfb')
iv = ciphertext[0:blocksize]
ciphertext = ciphertext[blocksize:]
From the code it is apparent that the IV is generated in the same size as the cipher blocksize, so it is safe to slice a block from the ciphertext to get the IV.