Encrypting java JWEObject with RSA in python - 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.

Related

ValueError: Ciphertext with incorrect length (not 256 bytes) using Python

I want to decode RSA key in python, see below my code :
def decode():
encoded_password = request.args['password']
rsa_key = RSA.importKey(open('Python/auth/private.txt', "rb").read())
cipher = PKCS1_v1_5.new(rsa_key)
raw_cipher_data = b64decode(encoded_password)
# Decrypt the data
decrypted_password = cipher.decrypt(raw_cipher_data, "ERROR")
return jsonify({'status': 'success', 'message': decrypted_password.decode('utf-8')})
My RSA Private key file looks like this :
-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3r6bHdnLyOlnA
3SL9Q1emrIfq/GUz1L9nlUEraWAF/ZSjwAjzxISNW2sP7Utoz6rUCxGPhwUAYaqx
D5J4iNZ4Uh4v7q8/XIyzfj0kT2/+sEznFLLTMbfIMV/gn8dbzAWDfMjA4itv3FCp
...
sEcPKDP+Cfu+9qjCGZ8+cTa7ilT9r/yvUus5bwW+DpfeMAVMgLahPB0sHPR2jkAK
v/dTPZz+XPN8k4kbO/wQlY1NCavHjwOIiwd/Z1d0Fv+K9Pmrwi2xO3icegEj93dG
F7c3bSZfWisoMQm+K2VCMZY=
-----END RSA PRIVATE KEY-----
I managed to import the private RSA key, but I get an error when I try to decode.
ValueError: Ciphertext with incorrect length (not 256 bytes)
I can't find answers to my problem.
Maybe your encryption part is not coherent with the decryption one :
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from base64 import b64encode
from base64 import b64decode
def decode():
rsa_key = RSA.importKey(open('Python/auth/private.txt', "rb").read())
cipher = PKCS1_v1_5.new(rsa_key)
global encoded_password
# Decrypt the data
decrypted_password = cipher.decrypt(encoded_password, "ERROR")
password = b64decode(decrypted_password)
return jsonify({'status': 'success', 'message': decrypted_password.decode('utf-8')})
def encode():
password = b"stackoverflow"
rsa_key=RSA.generate(2048)
with open('Python/auth/private.txt', "wb") as file:
file.write(rsa_key.export_key('PEM'))
cipher = PKCS1_v1_5.new(rsa_key)
raw_cipher_data = b64encode(password)
print(raw_cipher_data)
# Decrypt the data
global encoded_password
encoded_password = cipher.encrypt(raw_cipher_data)
return jsonify({'status': 'success', 'message': encoded_password.decode('utf-8')})
encode()
decode()
The RSA key looks like :
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAmnDiQmYr3oHiVaEtRcD2wARtFS2FeHCX6CMreiyXy7bIAtMY
KvO2EMBv4jjqpZQ3IT8qFKYxSANgC/t6/PX8ZshjERhngTs/C39logjgf7NOabWh
pfjKVJ+XMGBCR943YpTR2YBCqyztbZUGTO1EL4dFrEYYEPBIlvy2AXXOYwWj78Bv
VrgQlhqZk7WeEmZ1t6ezGvsw18R72ITU1wzSekLiMCJhNttELIHbuWQZw48wVlXd
5B/803zHKPivtKsVaqrfBwv+UONozlXhpBlz6FYjA/tnC2xuk5WpSBc2jQfyeNho
pRtA1WK2YMp2IMbp1nVeajlPENCEE3w7joIyGwIDAQABAoIBABWVgNbvyYzDe+V6
F5t/4DGuIgKg2zOduhUxfnIziOlpu6KYFvDAdmmj7gm16PvyxBEA72QgZT3KaaId
vNldQC+rLMaRsbwqvWEMJnY9UFwjK+pz0x92LuzMZbMB7kbLWdPiDEFIqXNGiE4q
0s0YfHw5/F+bEjeieo/4ydpYDlvh5nG0y9Sdl6FxufAhx/JXuNNq+qO5qEEMQwYU
hnf31Sw7twEJGvOaKsCAcPG9F64JGo/QUx/VJIA97EoHvWl5USRxqUiOkyWpeeHs
iSYBOA4zAF68uEakax7jD2xc/GxCwBJGjrI9K3A6+NI9wfu6uJxIL8unfR+KwCSn
0k6446UCgYEAvZnOj7eY5t3q94TxxH3HHc61jBDLn7Mq/N0YdxocJp84zXLFtH6x
bgBVoqqllF3rp77f2HLnVHzacW+Tvv8GVxhZ1o48v8WK/5Ej3Sgh+6dsa99e24SQ
z7sGpYinATwqhWnwzd/G0ZHDTNX3vScOq/rNfnyRi6qu9+TIEbqCqC0CgYEA0Ibo
IJDxoBP2387jYB/Z8gqv6xq0kqyrdOa0yv2c7JFS8yCXXZ1cTy/x/YJrZR2eCeQs
YQfN95u12j3lUlCVWVb3ZmM6CAyEpUYqPZ4sCtJdKChLM0+I0yfIbfXiEIIJ0yjM
Al56IAYNEYydSPE9jLW/P4lHRrssQbKr3Py/qGcCgYB0XpKJYwZVrJ8qjE5Xa1tq
0BRdg3F282DPEmSRtVTR36fdcTQnNBtyiIIG9PXrujmJG34IO34APSFVvkXQVHZv
vmJlbaebjINjmJGKi7dP3dKN4us1kIfQ99l8gAMAnwz6FavWsCI3Pl/AKROE5RP8
OlMl7w7lyjzZqXGib/cBgQKBgEw8hjEhzLTRl7hLUyWZf3zWG2rA4LOfHTAoCIEO
J4j1uHXavHwlQ9JPnREp1UmqglTrbq4qxEp6Swn3BxgJDhETkm+EZ3r52KTz+g18
/m0Wa6h60sN3mHZaXRSWiIewgxcIG03ibJO4op5/4iEA0ZfX+ouoDL73Pz7lq5+n
aAqRAoGAFbytz4N8vCftiznwzTED5l2qqO3PASU1YctcZWVh7zDK8bQ6sXI9Abmz
8Q8kXPNFFLs+LeMaiId8KK6KtCMv8nu1GvjC1OuzMTwbASTDyy6FFdxOTB+JfEX9
XCftkh3k7F7OtIKarGVnSXbwH+NIPbV25DBRvW2tqCyztH8j4Bc=
-----END RSA PRIVATE KEY-----
So it is bigger than yours.
NB : I strongly recommend you to use PKCS OAEP #1 due to security reasons (https://pycryptodome.readthedocs.io/en/latest/src/cipher/pkcs1_v1_5.html).
As the error message says, the ciphertext you are trying to decrypt is of the incorrect length. Looking at the section for RSA decryption in RFC 3447, the ciphertext must be "an octet string of length k, where k is the length in octets of the RSA modulus n". In your case, it must be 256 bytes.
As #JoelCrypto says, this might be due to a discrepancy between your encryption and decryption code. Or it could be an extra byte or two that you added to the ciphertext by mistake. (Maybe an extra newline crept in somewhere?) Also, I totally agree with Joel about using PKCS#1 OAEP instead of PKCS#1 v1.5, as the latter is vulnerable to timing attacks.

How to use public key to encrypt data and use base64 encode it in python?

I want to implement the following function using python, it uses public key to encrypt the password and then using base64 to encode it. I have searched many answers but I didn't find a solution, I think probably I didn't understand them. Would you please give me some advice?
// # java code
public static String encryptRSA(String password, String publicKey) {
try {
byte[] decoded = Base64.decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(decoded));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encode(cipher.doFinal(password.getBytes("UTF-8")));
return outStr;
} catch (Exception e) {
}
return null;
}
I use the following code, but it raises error M2Crypto.RSA.RSAError.
# python code
import base64
import M2Crypto
pub = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClnY+3rAf/az9t2bxC80TObpZ2ZCH1xSjPt9QtXl6k6UtD7mQcI2CWSwnDgKJr2m2WnM1kR6X+oOL61lXO0gSuuD8tWOx/knZA2VaSTLdsHBDLOX3e6Fo/O3CtoLVwO5FYFBIFHXMoikPkR8tFIOLWsX0y3slLQQShwSJAHytP4QIDAQAB"
password = 123
def public_encrypt(public_key, password):
rsa_pub = M2Crypto.RSA.load_key_string(public_key.encode('utf-8'))
ctxt_pub = rsa_pub.public_encrypt(password.encode(), M2Crypto.RSA.pkcs1_padding)
ctxt64_pub = base64.b64encode(ctxt_pub).decode()
return ctxt64_pub
res = public_encrypt(pub, password)
print('res:', res)
The full error message is: M2Crypto.RSA.RSAError: no start line, indicating that the key is imported incorrectly.
load_key_string() requires a PEM-encoded private key in PKCS#1 or PKCS#8 format, but no public key. A PEM encoded public key in X.509/SPKI format can be imported with load_pub_key_bio().
From the posted X.509/SPKI key a PEM encoded key can be created by adding a newline after every 64 characters and adding a corresponding header and footer:
import M2Crypto
x509 = '''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClnY+3rAf/az9t2bxC80TObpZ2
ZCH1xSjPt9QtXl6k6UtD7mQcI2CWSwnDgKJr2m2WnM1kR6X+oOL61lXO0gSuuD8t
WOx/knZA2VaSTLdsHBDLOX3e6Fo/O3CtoLVwO5FYFBIFHXMoikPkR8tFIOLWsX0y
3slLQQShwSJAHytP4QIDAQAB
-----END PUBLIC KEY-----'''
bio = M2Crypto.BIO.MemoryBuffer(x509)
rsa_pub = M2Crypto.RSA.load_pub_key_bio(bio)
Another issue is that there is no encode() defined for an integer, but presumably password is supposed to be a string and this is just a copy/paste error.
With these changes the code works.
Note that a 1024 bits RSA key (like yours) is insecure nowadays, instead the length should be >= 2048 bits.

Which Python JOSE library supports nested JWT (signed+encrypted)?

I looked at python-jose and jose but neither seem to support encrypting a signed JWT. For example, "jose" library supports signing and encrypting separately, without nesting them.
Am I missing something, like perhaps it's fairly easy to nest JWTs outside the library? If so, please share tips on achieving this so the result is well formatted.
jwcrypto supports nested JWS and JWE.
To sign and then encrypt:
# Load your RSA pub and private keys
pubKey = jwk.JWK().from_pyca(serializedPublicKey)
privateKey = jwk.JWK().from_pyca(serializedPrivateKey)
# your JWT claims go here
claims = {
# JWT claims in JSON format
}
# sign the JWT
# specify algorithm needed for JWS
header = {
u'alg' : 'RS256',
'customSigHeader':'customHeaderContent'
}
# generate JWT
T = jwt.JWT(header, claims)
# sign the JWT with a private key
T.make_signed_token(privateKey)
# serialize it
signed_token = T.serialize(compact=True)
# JWE algorithm in the header
eprot = {
'alg': "RSA-OAEP",
'enc': "A128CBC-HS256",
'customEncHeader':'customHeaderContent'
}
E = jwe.JWE(signed_token, json_encode(eprot))
# encrypt with a public key
E.add_recipient(pubKey)#
# serialize it
encrypted_signed_token = E.serialize(compact=True)
To decrypt and verify signature:
#Decrypt and Verify signature
E = jwe.JWE()
# deserialize and decrypt
E.deserialize(encrypted_signed_token, key=privateKey)
raw_payload = E.payload
# verify signature
S = jws.JWS()
S.deserialize(raw_payload, key=pubKey)
final_payload = S.payload

how to encrypt a data string with RSA private key in python [duplicate]

I have pkcs8_rsa_private_key file which generate by openssl from a rsa_private_key.pem file.
I need make a signature by the private key in python, make the same signature with below java code.
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static String sign(String content, String privateKey) {
String charset = "utf-8";
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PKCS#8 defines a way to encode and transport secret keys and it is not specific to OpenSSL; PKCS#1 defines a way to use an RSA key (no matter how it was loaded into your application, with PKCS#8 or not) to carry out and verify digital signature on data.
The piece of code you have does three things:
It decodes Base64 into PKCS#8
It decodes PKCS#8 into the actual key in memory (mind you may need to provide a passphrase here)
It performs PKCS#1 v1.5 signature using SHA-1 using said key
It encodes the signature in Base64
The example for PKCS#1 v1.5 signing in the API of PyCrypto does exactly steps #2 and #3.
from Crypto.Signature import PKCS1_v1_5 as pk
from Crypto.Hash import SHA
class Crypt(object):
pkcs8_private_key = RSA.importKey(open('pkcs8_rsa_private_key.pem', 'r').read())
def rsa_sign(cls, des_reqdata):
"""
#:param reqdata: request reqData
"""
h = SHA.new(des_reqdata)
signer = pk.new(cls.pkcs8_private_key)
signature = signer.sign(h)
return base64.b64encode(signature)

Python pyCrypto RSA encrypt method gives same results using private or public key

I'm trying to understand the pyCrypto encrypt and decrypt methods for public and private keys, and I'm seeing something strange. Suppose I have a set of private and public keys, stored in files dummy_private.txt and dummy_public.txt.
I create a private key object and public key object like this:
private_key_file='dummy_private.txt'
f = open(private_key_file, 'r')
privateKey = RSA.importKey(f.read(),None)
f.close()
public_key_file='dummy_public.txt'
f = open(public_key_file, 'r')
publicKey = RSA.importKey(f.read(),None)
f.close()
Now suppose I want to encrypt some message. I can do it like this:
s='This is a super secret message'
sutf8=s.encode('utf8')
enc=publicKey.encrypt(sutf8,None)[0]
encb64=base64.encodestring(enc)
print "Public key Encoded message is %s" % (encb64,)
This makes sense because I am encrypting with the public key and I should be able to decrypt with the private key.
However, I can also encrypt the above using the private key, and it gives me the same result!
enc2=privateKey.encrypt(sutf8,None)[0]
encb642=base64.encodestring(enc2)
print "Private key Encoded message is %s" % (encb642,)
When I print out the base64 encoded version of the encrypted data, using either the private key or the public key, they are the same! Why is that?
And this raises the problem of digitally signing something with the private key. If I can sign something with the public key and get the same results, then how does signing verify that I am who I say I am? This must be some issue with the encrypt method that I don't understand. Can someone please explain?
Since encrypting with both the public key and private key gives the same results, it appears that decrypting with the private key can be done regardless of whether the encryption was done with the private key or the public key. I'm totally confused as to why one could encrypt with the private key and get a result that is the same as if it were done with the public key.
When you encrypt with a private key, pycrypto is actually using the public key (which can be generated from the private key).
Source: PyCrypto: Decrypt only with public key in file (no private+public key)
You'll find that pycrypto doesn't allow you to decrypt using the public key for good reason:
>>> publicKey.decrypt(enc2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/site-packages/pycrypto-2.6-py2.7-linux-x86_64.egg/Crypto/PublicKey/RSA.py", line 174, in decrypt
return pubkey.pubkey.decrypt(self, ciphertext)
File "/usr/local/lib/python2.7/site-packages/pycrypto-2.6-py2.7-linux-x86_64.egg/Crypto/PublicKey/pubkey.py", line 93, in decrypt
plaintext=self._decrypt(ciphertext)
File "/usr/local/lib/python2.7/site-packages/pycrypto-2.6-py2.7-linux-x86_64.egg/Crypto/PublicKey/RSA.py", line 239, in _decrypt
mp = self.key._decrypt(cp)
File "/usr/local/lib/python2.7/site-packages/pycrypto-2.6-py2.7-linux-x86_64.egg/Crypto/PublicKey/_slowmath.py", line 52, in _decrypt
raise TypeError("No private key")
TypeError: No private key
Mathematically, RSA makes it possible to encrypt with the private key and decrypt with the public key, but you're not supposed to do that. The public key is PUBLIC - it's something you would readily share and thus would be easily disseminated. There's no added value in that case compared to using a symmetric cipher and a shared key (see: https://crypto.stackexchange.com/questions/2123/rsa-encryption-with-private-key-and-decryption-with-a-public-key)
Conceptually, "encrypting" with the private key is more useful for signing a message whereas the "decryption" using the public key is used for verifying the message.
More background: exchange public/private key in PKCS#1 OAEP encryption/decryption

Categories

Resources