Python-rsa: can encrypt cannot decrypt - python

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.

Related

Encrypting java JWEObject with RSA in python

I asked a while ago how to do something similar: Decrypting and encrypting java JWEObject with algorithm RSA-OAEP-256 on python
Now I have a different encryption key and that code is not working for me anymore.
I need to be able to encrypt my data: {"value": "Object Encryption"} with JWE using RSA.
I have this key id: a4d4039e-a8c7-4d06-98c8-2bda90ab169c
and this encryption key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9JJaeFiDdB+dGvi3jUzKUMU73kG6vvc/P+jwZXRKKpJSwf8PU4SapMyFPFFoHwca6Z8vZogF4ghEJ18JipNyF3BLnfCt1EHuZ15FG1Aywvpi+xw7F0UoJ9DWItBM1SodKXIh1be44/9SiLrpcyROKId349zWMOl3IVVxekLPKWTHsy2Iowp7JsjNEK3t9RdV+PAtUzp1ACyqHD/MDYSmpJuEOR9AbmBayaFIWVP+52q1ir7ea88zocmklDg0SGjiRNXq1tUAljWezpKstKQNz/IZN1kMLQ8SknrlpZL0vjjAnHFlgtLfcwPbESt76surRshfGwwfx8T9AOfXMgELNQIDAQAB
and I should get this:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiYTRkNDAzOWUtYThjNy00ZDA2LTk4YzgtMmJkYTkwYWIxNjljIn0.2hGqQVSbgZ9-9Hiz8VZizORpWRR2yioHb8vK6R9tQCpxr0jxBGehNL0K36XfJWJC5KxcxDdD9byeI_YTtB_hYTgsuMTHS5p-4aJ4nLk43Ya5yR8p8nn4s11wbkfSj0jbqSFb_1IOCMgX0Xu8lmnVe7Tjc4vACwBoaM6VpudEsLHpyQ9OxNaa1apbRp-BX3DEVM3l7ltHhMIh_DCRWbC4-LbS51L4RqLWxmihqRA97FYX4HX38Vbt3O__2tq5KfSjq78UEOffEFe_CRg8mXZ1CHgyH4YPMNmjS-jAI4m07Coja4zLXgv7ctFaFQePISLaZLgdp3a0a-Sht5cwwZfAhg.mc7_YA9mg3l7VV5B.ZOnYjkiXx1YSxDIILjcHUXluwW8jqsSO5NuIkto.9KtJGJRS9QevrqZPYYlcTQ
That's the java code I'm trying to rewrite in python:
private RSAPublicKey getObjectEncryptionKey()
throws NoSuchAlgorithmException, InvalidKeySpecException {
logger.debug("Getting object encryption key");
if (Objects.isNull(objectEncryptionKey)) {
objectEncryptionKey = getActiveKey(Algorithm.RSA);
}
byte[] encryptionKey = base64Decode(String.valueOf(objectEncryptionKey.getEncryptionKeyValue()).getBytes());
KeyFactory keyFactory = getInstance(Algorithm.RSA.name());
return (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(encryptionKey));
}
public String encryptObject(Object object) {
logger.debug("Encrypting object with keyId: {}", getObjectEncryptionKeyId());
JsonWebEncryption encryptedObject = getJWEObject(object);
try {
return encryptedObject.getCompactSerialization();
} catch (JoseException e) {
throw new CryptoException("Could not encrypt object/event", e);
}
}
private JsonWebEncryption getJWEObject(Object object) {
JsonWebEncryption jwe = new JsonWebEncryption();
try {
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
jwe.setKey(getObjectEncryptionKey());
jwe.setKeyIdHeaderValue(getObjectEncryptionKeyId());
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new CryptoException("Could not create JsonWebEncryption", e);
}
}
How is it different from my previous question and what is the correct way to do it in python?
I tried doing something like that:
def grouper(iterable, n, fillvalue=''):
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
def decryption_key_to_pem(decryption_key: str) -> bytes:
pem = ['-----BEGIN PRIVATE KEY-----']
for group in grouper(decryption_key, 64):
pem.append(''.join(group))
pem.append('-----END PRIVATE KEY-----')
return str.encode('\n'.join(pem))
jwk.JWK.from_pem(decryption_key_to_pem(key))
but I get this exception:
ValueError: ('Could not deserialize key data. The data may be in an incorrect format, it may be encrypted with an unsupported algorithm, or it may be an unsupported key type (e.g. EC curves with explicit parameters).', [_OpenSSLErrorWithText(code=503841036, lib=60, reason=524556, reason_text=b'error:1E08010C:DECODER routines::unsupported'), _OpenSSLErrorWithText(code=109052072, lib=13, reason=168, reason_text=b'error:068000A8:asn1 encoding routines::wrong tag'), _OpenSSLErrorWithText(code=109576458, lib=13, reason=524554, reason_text=b'error:0688010A:asn1 encoding routines::nested asn1 error'), _OpenSSLErrorWithText(code=109576458, lib=13, reason=524554, reason_text=b'error:0688010A:asn1 encoding routines::nested asn1 error')])
Also tried something like:
def get_jwe_key(data, encryption_key, encryption_key_id):
jwe = jwcrypto.jwe.JWE()
jwe.plaintext = json.dumps(data).encode('utf-8')
jwe.alg = 'RSA-OAEP-256'
jwe.enc = 'A256GCM'
jwe.recipient = encryption_key
jwe.header = {'kid': encryption_key_id}
return jwe
jwe_key = get_jwe_key(decrypted_data, key, key_id)
jwe_key.serialize()
and I get: jwcrypto.common.InvalidJWEOperation: No available ciphertext
In the JWCrypto documentation you can find examples for the encryption with JWCrypto. You basically only need to insert your values:
from jwcrypto import jwk, jwe
import json
spki_pem = '''-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9JJaeFiDdB+dGvi3jUzK
UMU73kG6vvc/P+jwZXRKKpJSwf8PU4SapMyFPFFoHwca6Z8vZogF4ghEJ18JipNy
F3BLnfCt1EHuZ15FG1Aywvpi+xw7F0UoJ9DWItBM1SodKXIh1be44/9SiLrpcyRO
KId349zWMOl3IVVxekLPKWTHsy2Iowp7JsjNEK3t9RdV+PAtUzp1ACyqHD/MDYSm
pJuEOR9AbmBayaFIWVP+52q1ir7ea88zocmklDg0SGjiRNXq1tUAljWezpKstKQN
z/IZN1kMLQ8SknrlpZL0vjjAnHFlgtLfcwPbESt76surRshfGwwfx8T9AOfXMgEL
NQIDAQAB
-----END PUBLIC KEY-----'''
data = {"value": "Object Encryption"}
public_key = jwk.JWK.from_pem(spki_pem.encode('utf-8'))
payload = json.dumps(data).encode('utf-8')
protected_header = {
"alg": "RSA-OAEP-256",
"enc": "A256GCM",
"kid": "a4d4039e-a8c7-4d06-98c8-2bda90ab169c",
}
jwetoken = jwe.JWE(payload, recipient=public_key, protected=protected_header)
jewcompact = jwetoken.serialize(True)
print(jewcompact)
Possible output:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiYTRkNDAzOWUtYThjNy00ZDA2LTk4YzgtMmJkYTkwYWIxNjljIn0.DstnMY6WkdCGA1NC0S4JIU3CNVFuUNQSghQyqkh8RpmUyBvnEelvkmWTTf3AApj4jNflnYL_bp8vbeu8PO6CyF1Pi_gUZ1vE1PHHcLD8VZ2eMiIdG08Qq9L7uDlqTIaM6qX0n_uctsm4Y0rflWXrSbr5iwXHxgOQ5XDgLCgg870tObS3RbK2RzrjYRnQs-_hK4R8LRJgCEKeV__fzmU6nx5jA4qXXs_U3y9Uxs3_4OE-xSelPT_yY5xCMs8fAHvaua92mrRCMSu9cp9iAYW8qu3bYdUFjnWqifOQIUB2HljqMH85tCxS02tBuVPs52b8pgNUckqa_v43BvxTbnwuJg.RWtp5j38l8mz_qoJ.2VBsxt1zyk5rAmSvg3k0eMLzIAPo9ttn-7dLB2nP.k4k9ZnCRRA1im2sbvMLlbQ
An encrypted token consists of 5 parts, the header, the encrypted key, the IV/nonce, the actual ciphertext and the tag, each separated by . and Base64url encoded. Since AES generates a different key and IV/nonce for each encryption and RSA encryption is non-deterministic, a different encrypted token is generated for each encryption except for the first part. So you should not expect that the code generates the same encrypted token that you posted.
The generated token can be decrypted using the private key associated with the posted public key and the decryption code of the linked post. This is also the right way for a test.
Edit:
Regarding the first part of your question from the comment: If the header
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiYTRkNDAzOWUtYThjNy00ZDA2LTk4YzgtMmJkYTkwYWIxNjljIn0
of the encrypted token is Base64url encoded, you get:
{"alg":"RSA-OAEP-256","enc":"A256GCM","kid":"a4d4039e-a8c7-4d06-98c8-2bda90ab169c"}
Here A256GCM as enc means that the plaintext is encrypted with AES-256 in GCM mode (symmetric encryption), with a random key being generated for the AES encryption.
RSA-OAEP-256 as alg means that this AES key is encrypted with RSA and with OAEP/SHA256 as padding (asymmetric encryption).
Bear in mind that RSA encryption always uses the public key (of the recipient).
So both cryptography types (symmetric and asymmetric) are involved, which is also called hybrid encryption.
The private RSA key does not come into play at all during encryption, but only during decryption, as you can verify in the linked post. There, the encrypted token is decrypted with the private RSA key (which must be associated with the public key that was used for encryption), meaning more precisely that the symmetric AES key is decrypted with RSA (using the private RSA key), which is then used to decrypt the actual ciphertext.
Regarding the second part: The format and type of the key can be determined with an ASN.1 parser, e.g. https://lapo.it/asn1js/.
The key you posted is an ASN.1/DER encoded public RSA key in X.509/SPKI format that is Base64 encoded. A public RSA key essentially contains modulus and public exponent. A private key also contains the private fields and also uses other formats.

Python AES CTR Mode only encrypts first two bytes

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

AES won't decrypt properly

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

Python - Socket sends RSA public key wrong

I trying implement a simple messaging system with several security features. So I need to send client's RSA key's public key part to server. But I print key on both sides. They are not match.
My client code:
from Crypto.PublicKey import RSA
from Crypto import Random
random_for_key = Random.new().read
usr_key = RSA.generate(1024, random_for_key)
usr_pub_pt = usr_key.publickey()
print(usr_pub_pt)
usr_pub_pt = usr_key.publickey()
socket2.send(usr_pw_couple.encode())
socket2.send(usr_pub_pt.exportKey(format='PEM', passphrase=None, pkcs=1))
My server code:
r_usr, r_pw = (self.conn.recv(2048).decode().split(" "))
r_pk = RSA.importKey((self.conn.recv(2048).decode()), passphrase=None)
print(r_usr,r_pw)
print(r_pk)
Thank you all.

Decoding with Python's cryptography package

My server is in Python and client is Swift. I'm using cryptography package for Python and SwCrypt for Swift.
I create a public and private key pair
Server send public key to iOS
iOS creates a random AES key and encrypt it with the public key
let publicKeyDER = try SwKeyConvert.PublicKey.pemToPKCS1DER(publicPemReceivedFromServer)
let msg = "this_is_thirty_two_character_lon".data(using: .ascii)!
let random_aes_cypher = try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: msg, key: CC.generateRandom(32), iv: CC.generateRandom(16))
iOS sends the encrypted AES key back to server
let x = try CC.RSA.encrypt(random_aes_cypher, derKey: publicKeyDER, tag: Data(), padding: .oaep, digest: .sha1)
//Post x to server
Server decrypts the encrypted AES key with the private key
private_key.decrypt(encrypted_aes_key, oaep_padding)
On the last step, the decrypted message contains very weird character..Any idea why? It looks something like this on PyCharm: \�<�>Ddž䥋��wp+6'���W=��$�O�rܨosf�.C��qKT=_�{�B��pE#�-mn��t����Y^0���L�9f#�=O*��\���B��z�;��"�0��k&��z,��J�\�L
When I call len() on the decrypted message, I see that I get the correct number of characters back (32 in this case), but the letters are just weird like shown above.

Categories

Resources