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.
Related
I want to achieve the same result in Dart and in Python in encryption using AES.
My problem is that my Python version only outputs the first two bytes successfully and nothing else.
In doing so, I (must) start in Dart.
As seen in the code, I generate an IV and a key, which is passed as a parameter.
List<String> aesEncryption(List<int> key, String message) {
final iv =
enc.IV.fromLength(16); // IV generation
final aesKey = enc.Key(Uint8List.fromList(
key)); // transform the key into the right format
enc.Encrypter encrypter =
enc.Encrypter(enc.AES(aesKey, mode: enc.AESMode.ctr));
final encryptedMessage = encrypter.encrypt(message, iv: iv);
// Here I send my encrypted message and IV to my Flask app, to use the same IV and to
// compare the results with eacht other
return [encryptedMessage.base64, iv.base64];
}
Then, eventually, I just encrypt the message "test" with the following key:
The sha256 Hash of the string "low;high"
I then send this tuple to my Flask app and receive the data as follows:
#app.route("/test", methods=["POST"])
def test():
values = request.get_json()
iv = base64.b64decode(values["iv"]) # my IV which was sent of the Dart App
sig = values["signature"] # just the AES encryption value of the Dart App
key = values["key"] # the string "low;high"
aes_key = hashlib.sha256(key.encode('utf-8')).digest() # as mentioned before, I have
# to hash the string "low;high" first and I will use this as my aes key
# I tested if the computed hash values are the same in python and dart, and yes it is so
# I create a Counter with my IV, I do not completely understand this part, maybe here is my mistake somewhere.
counter = Counter.new(128, initial_value=int.from_bytes(iv, byteorder='big'))
cipher = AES.new(aes_key, AES.MODE_CTR, counter=counter)
test = "test".encode('utf-8')
# The output of this print is: b'\xd52\xf8m\x0b\xcb"\xa7\x0e\xf7\xab\x96\x0f\xfc\x9f\n'
print(base64.b64decode(sig))
encrypted = cipher.encrypt(test)
# The output of this print is: b'\xd52\xf8m'
print(encrypted)
return "", 200
As you can see only the first two bytes are output of the python AES encryption and unfortunately, I don't understand why. I hope someone can help me with this because this implementation is essential for me.
Thank you in advance for every helpful answer!
Kind regards,
Bagheera
I was able to find the error again pretty quickly.
The problem is that automatic PKCS7 padding is used in Dart and not in Python.
So the solution (just for the matter that the encryption values are the same) is the following in Dart:
enc.Encrypter encrypter =
enc.Encrypter(enc.AES(aesKey, mode: enc.AESMode.ctr, padding: null));
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.
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))
I have written some code using the pycrypt library and I must be doing something wrong, but I can't figure out what it is that I am doing wrong. I can (nearly) decrypt messages with the wrong initialization vector even though I believe I am following their examples.
from Crypto.Cipher import AES
import os
from string import ascii_letters
key, iv = os.urandom(32), os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = ascii_letters
plaintext += ' ' * (-len(plaintext) % 16) # Padding
ciphertext = cipher.encrypt(plaintext)
cipher = AES.new(key, AES.MODE_CBC, os.urandom(16))
text = cipher.decrypt(ciphertext)
# text[16:] = b'qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ '
Now aside from the first 16 bytes of the decrypted text, you have everything decrypted correctly with a totally random choice of IV. Can someone help me figure out where I am going wrong?
That's normal for cipher block chaining. In CBC decryption, the IV is only necessary to reconstruct the first block of plaintext. The computation of other blocks of plaintext doesn't actually involve the IV. Here's a diagram (source: Wikimedia user WhiteTimberwolf, public domain):
Based on the Golang documentation on CFB decryption I wrote a minimal working example to decrypt a string that was encrypted with AES CFB and then base 64 encoded in python3.
The golang decryption works fine when the message was encrypted within Golang (with the encryption function from the Golang doc example).
However when I encrypt the message in a python script using the python crypto package, I am unable to decrypt it in the golang script successfully. I don't get the right bytes back.
$ python3 stack.py
Going to encrypt and base64 "This is not encrypted" result:
b'jf9A5LCxKWPuNb1XiH+G3APAgR//'
Now going to call the Golang script:
b'Hello from Golang, going to decrypt: jf9A5LCxKWPuNb1XiH+G3APAgR//
Result: Tl!\xca/\xf1\xc0\xb2\xd01Y\x02V\xec\xdf\xecy\xd38&\xd9\n'
Blocksize is 16 by default for both AES implementations.
So the question: What is going wrong?
Golang script:
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"os"
)
func main() {
key := []byte("TfvY7I358yospfWKcoviZizOShpm5hyH")
iv := []byte("mb13KcoviZizvYhp")
payload_python := os.Args[1]
fmt.Println("Hello from Golang, going to decrypt: "+payload_python+" Result: "+string(decrypt(key, payload_python, iv)))
}
func decrypt(key []byte, cryptoText string, iv []byte) []byte {
ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText) //decode base64 coding
//prepare decryption based on key and iv
block, _ := aes.NewCipher(key)
stream := cipher.NewCFBDecrypter(block, iv)
//decrypt
stream.XORKeyStream(ciphertext, ciphertext)
return ciphertext
}
Python script:
#!/usr/bin/env python3
import base64
from Crypto.Cipher import AES
from subprocess import check_output
original_message = 'This is not encrypted'
key = 'TfvY7I358yospfWKcoviZizOShpm5hyH'
iv = 'mb13KcoviZizvYhp'
#prepare encryption
cfb_cipher_encrypt = AES.new(key, AES.MODE_CFB, iv)
#encrypt and base64 encode
encryptedpayload = base64.b64encode(cfb_cipher_encrypt.encrypt(original_message))
print('Going to encrypt and base64 "{}" result:\n{}\n'.format(original_message,encryptedpayload))
print('Now going to call the Golang script:')
print(check_output('go run stack.go {}'.format(encryptedpayload.decode()),shell=True))
Try encrypting from Python like this.
The result can then be unencrypted from Go successfully.
#!/usr/bin/env python3
import base64
from Crypto.Cipher import AES
MODE = AES.MODE_CFB
BLOCK_SIZE = 16
SEGMENT_SIZE = 128
def _pad_string(value):
length = len(value)
pad_size = BLOCK_SIZE - (length % BLOCK_SIZE)
return value.ljust(length + pad_size, '\x00')
def encrypt(key, iv, plaintext):
aes = AES.new(key, MODE, iv, segment_size=SEGMENT_SIZE)
plaintext = _pad_string(plaintext)
encrypted_text = aes.encrypt(plaintext)
return encrypted_text
key = 'TfvY7I358yospfWKcoviZizOShpm5hyH'
iv = 'mb13KcoviZizvYhp'
original_message = 'This is not encrypted'
encryptedpayload = base64.b64encode(encrypt(key, iv, original_message))
print('Going to encrypt and base64 "{}" result:\n{}\n'.format(original_message,encryptedpayload))
Source: http://chase-seibert.github.io/blog/2016/01/29/cryptojs-pycrypto-ios-aes256.html