Warm tip: This article is reproduced from serverfault.com, please click

Making RSA encryption compatible in Python (PyCrypto) and PHP (OpenSSL)

发布于 2016-03-03 18:11:22

I am migrating an entire PHP API, and while I used PyCrypto before, I am not sure how to translate the following encryption call, since I need the exact same result. The PHP call is:

define('KEY', "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC81t5iu5C0JxYq5/XNPiD5ol3Z
w8rw3LtFIUm7y3m8o8wv5qVnzGh6XwQ8LWypdkbBDKWZZrAUd3lybZOP7/82Nb1/
noYj8ixVRdbnYtbsSAbu9PxjB7a/7LCGKsugLkou74PJDadQweM88kzQOx/kzAyV
bS9gCCVUguHcq2vRRQIDAQAB
-----END PUBLIC KEY-----");
$cypher = "";
$result = openssl_public_encrypt($plain, $cypher, KEY, OPENSSL_PKCS1_PADDING);
echo bin2hex($cypher);

Assuming everything goes right, this prints the content from $cypher, passed to hexadecimal. For a sample input "azzzzzzzzzzzzdfdf" I get something like: "2281aeebc1166cdfb2f17a0a0775d927ca5a9ad999bae0e4954f58bd8082fdf7efe1fd284876530341f714456d7eb8cd44c57b20ab27029b84d5dc77a674bede3fe9065282931404286082e9df8607bdcff0818b90324dfee7d76b566d0f99bebc5cc913372c276ba373712128f1bcc226b59367cff93f7cdd6dbde25b366863".

I must assume this value as right, since the code was taken from an existing API I am migrating. However, trying the same with PyCrypto (yes, I am migrating the API to be available in Python), I use the following code:

def bin2hex(s):
    return "".join([hex(ord(c))[2:].zfill(2) for c in s])

KEY = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC81t5iu5C0JxYq5/XNPiD5ol3Z
w8rw3LtFIUm7y3m8o8wv5qVnzGh6XwQ8LWypdkbBDKWZZrAUd3lybZOP7/82Nb1/
noYj8ixVRdbnYtbsSAbu9PxjB7a/7LCGKsugLkou74PJDadQweM88kzQOx/kzAyV
bS9gCCVUguHcq2vRRQIDAQAB
-----END PUBLIC KEY-----"""

from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
encrypter = PKCS1_v1_5.new(RSA.importKey(KEY))
print bin2hex(encrypter.encrypt("azzzzzzzzzzzzdfdf"));

While I expect the same value be returned and printed, the value finally is "3dd94ffabd01bb0e94010c0fedbcd4eb648f12e5d9e6d934b77ae86f76681d8a1b790cad9fddf6e6720415b4d645e525c33c402fa9778739b8e461790387e9508f7158a5fdc5723f5fc26d166b11a00759f0e0ee3ba6719a2e7c6b918f66e1311d1fff878ee2ca8762e1d6120f1e9585a76cdc7719ca20129ae76182b4277170".

Using PKCS1_OAEP outputs "290f60f37088c2cb46ae9221b01ff46a463f270ef7cf70bbea49de0b5ae43aec34a0eb46e694cf22f689eb77e808c590fdc30eda09f9d3f3cb8c15e0505bf5a984c2a121bc9fa83c6b5ccf50235f072467b4ae9cdf0f0ee2e486626ffa62ad1aa715fbe29e8afe4ceab3ca5a5df4c1dc75d7f258285d7ff1f4f2b4dcb7a8413a".

It is easy to tell that I must fix my python code. How can I fix my python code so it returns the exact same result as the given PHP call?

Questioner
Luis Masuelli
Viewed
0
Artjom B. 2016-03-04 05:20:58

Your code is fine. PKCS#1 v1.5 padding in pyCrypto is randomized (source). So the encryption will always be different even if you use the same key and plaintext. This is a desirable property.

If you want to check compatibility between pyCrypto and PHP's OpenSSL extension, then you would need to encrypt in one, decrypt in the other and check that you got what you expected.


PKCS#1 v1.5 padding should not be used nowadays, because there are efficient attacks against it. OAEP is a much better alternative.