I'm trying to configure asymmetric encryption between python and java / android. The use case is storing user passwords securely (using the public key), enabling re-authentication to the server (which has the private key).
I've got the crypto algorithms working separately in Python and Java, but can't get my Python side to decrypt ciphertext generated in Java. I think the two most likely issues are either
To create a complete example here i'll reproduce both private and public keys, and then will re-generate a new set for actual use.
Java imports
// Java imports
import android.util.Base64;
import android.util.Log;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
Java encryption code - I apologise for the code formatting / width...
public class RSAEncryption {
public void encrypt (String ... strings) throws Exception {
// Load in public key, remove escape characters, headers, footers:
String public_key_string = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6kTFJl+6jG2rfYlZxtFi\nzajOgvKgInJHOa4i3G5vB9c7f7kzsTMOmeg5YHn3LNndg4Wx4AyfN5fbcNGg+KmJ\nK91b2lkgFy7pVEhWfzK4/yqk0liG7MwuN0G8GqUjIqJOXPS6lXB9Zr3n9QyTkKGV\n9cnNVPV1CNuN3bOu0t8Mu3fvZ+z8edq/cfUpTXwDdfRmZ6WeWxxqogK2uCwmneEN\n8kqyWE4OxhyqLJMw9mCGHOqTVgJUnvjMBezywr6s3vIcs2Q7CnxQx/g/GTqhUxLS\nHDlyAFDbhU4BWkCHrCa/nTyIFgXC9X4YRpQd24xfGXJjB2qUVv2H0O5FoRJAEYvS\nfQIDAQAB\n-----END PUBLIC KEY-----\n";
String publicKeyContent = public_key_string.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
// Encrypt input using OAEP / SHA-256 /MGF1
byte[] input = strings[0].getBytes();
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] publicBytes = Base64.decode(publicKeyContent, Base64.DEFAULT);
X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(publicBytes);
PublicKey pubKey = keyFactory.generatePublic(keySpecX509);
cipher.init(Cipher.ENCRYPT_MODE, pubKey, new OAEPParameterSpec("SHA-256",
"MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
byte[] cipherText = cipher.doFinal(input);
return cipherText;
}
}
If i run those code with the input string = "test text", i get encrypted output of:
BoOymgUGnB/LIqYOhBmW60HCL4s6FQFlNpRmhqm63AGq7gud4g96og6f+5yHUVHktAT0pl93UpAz//aae4esTseR6/o7jD/NGS+gZlNRHsSuNNstVZ8wOp1S03YcHYEpXtTdElMiKso4cbZmO20SoUvyzjI5e7RM1aWKCebqCvG7RYneCgoMi611AY2cVSwMYOENcRwqXeB3sFMrjoNQ1TkkgHU5MSxDbVLbNO72AYYQi/TkwiQ57qnjgk3KHXeatJv/5hWqa5ooBCy6aLDFux6X2721wTVa2/RSiZKG7DJHObV5pQNw4zjGWc4d5IJ+P/o6GKHY4fTj8ujqS6A44Q==
I'm confident that this works, as I can decrypt it successfully with the private key (with another code block, not reproduced here).
This is then used as the input for the python code.
Python imports
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backendb
Python decryption code
PRIVATE_KEY_STRING='-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDqRMUmX7qMbat9\niVnG0WLNqM6C8qAickc5riLcbm8H1zt/uTOxMw6Z6Dlgefcs2d2DhbHgDJ83l9tw\n0aD4qYkr3VvaWSAXLulUSFZ/Mrj/KqTSWIbszC43QbwapSMiok5c9LqVcH1mvef1\nDJOQoZX1yc1U9XUI243ds67S3wy7d+9n7Px52r9x9SlNfAN19GZnpZ5bHGqiAra4\nLCad4Q3ySrJYTg7GHKoskzD2YIYc6pNWAlSe+MwF7PLCvqze8hyzZDsKfFDH+D8Z\nOqFTEtIcOXIAUNuFTgFaQIesJr+dPIgWBcL1fhhGlB3bjF8ZcmMHapRW/YfQ7kWh\nEkARi9J9AgMBAAECggEARuUi6JcFxGOYBzies51AEk7omBZGwcXlqh35rM26yhun\nhOKOMyzpWUg+vOSMGcWg1KGMD+qh8FgDb6Pw2++qdFzb5DsejAWFVR1DF+FIvOex\n03o48sZjohNBkqqw9FU788OYB4twV7xWywDQU2+jCyvT+McDcPfIefRbjrMzjjOM\nOhMY0F6OXeDpuZttgqZsP65aA7yaIfjuk5tuYbpZvNmV1DAlqfGmxt2PO1laobGQ\nS7Vx18zpWWdh/GgnbZDD3SG69AmAWIdOZNV6SuUC5lgOqw2O+8TB7MXFbiCy7v5J\n1tMap6OCRDm5jLsc6ZFP5Zs1s9/FnW9/jCxw4kFxPQKBgQD7DcVUaW7rKUmYORAA\nqY6uAIu+Lo+607G4Nva52Rotj0vdiIsAYxJPP2dxKTjL0GLbKIsOcmd2xD0e1/yu\nlAu6koCyC5cT5XwNJ388XDwtWPU2J7mC1TKq0TJUZ56uzdZxNKOWQ7Upq3901zbQ\nQhssjJpgX09CPzlfpAc9NHhchwKBgQDu4lc5HaJNsFeEMOuaUmTUu+W+veIZZaV3\n7yTnLxoJCyqu5DVKvhMwDZP3I2JuvHbCPf3AzshvNcbCiG/6u9CQSbv4wRZO6p8c\nlIAymy0neaq7+uxHdvem58A4xIlKhlnNpzIqE9fay7ejAklGPTFs/YdsyVXwADVr\n30cfOdO92wKBgCCrGhJx5c0UAk+cnUh4x+g8ifKlfG6DPY0LGe/1IELtcqHRMsVK\nHwfQ6FUBWDKtWy/Jhs7KdEwwHQP2dxsAiMYuajDA8VfVdN8BVL02A16jRMVXRfyQ\nYZd4wWPaV/vHLTBt+RuEk/5oIp3Bo5BWCdMyOKRxwo6MS5r2bTq5qS/hAoGAKezT\nfhSzXYsrcOndD7KSO7vWcImG2wo55ji0c1aS7S9miFdI+xss5uwbIe614dV1ylVy\n6ZnhF5OKlK25aXn4+rnWIaxRq/wFfNCbR0ZwwFLcIi3BtjEs+cAGvm/P4KJ/tFY5\nuaTN53qFejh2f7tRp10/nVogmQSQW6ROKS7O+K0CgYA03s/ajxaYTLzBHpIdqisl\nsGaen4IBibB3OZs/vplBZ0xG34B8+MC8qgvC6DFVR4Y3XMol8lOGiS6OQVvIRjnC\nhqd/QobpH0j5Wy2cM+6yXVvj95dwif7QjaqDydniRhGbxa6iJ8vTYqyl9yNjWwX/\n72JRzDNB+9De2LntVwzBwA==\n-----END PRIVATE KEY-----\n'
#Load the java output into a bytes object to decrypt
java_out = b'BoOymgUGnB/LIqYOhBmW60HCL4s6FQFlNpRmhqm63AGq7gud4g96og6f+5yHUVHktAT0pl93UpAz//aae4esTseR6/o7jD/NGS+gZlNRHsSuNNstVZ8wOp1S03YcHYEpXtTdElMiKso4cbZmO20SoUvyzjI5e7RM1aWKCebqCvG7RYneCgoMi611AY2cVSwMYOENcRwqXeB3sFMrjoNQ1TkkgHU5MSxDbVLbNO72AYYQi/TkwiQ57qnjgk3KHXeatJv/5hWqa5ooBCy6aLDFux6X2721wTVa2/RSiZKG7DJHObV5pQNw4zjGWc4d5IJ+P/o6GKHY4fTj8ujqS6A44Q=='
#Load the private key string into a functioning key object
private_key = serialization.load_pem_private_key(
PRIVATE_KEY_STRING.encode('UTF-8'),
password=None,
backend=default_backend()
)
#Decrypt the input
private_key.decrypt(
java_out,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
Using this same code (and another block to encrypt) I can successfully encrypt / decrpyt in Python. However, when using the output from Java as the input, this code returns the following error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-71-9af0738427f1> in <module>()
4 mgf=padding.MGF1(algorithm=hashes.SHA256()),
5 algorithm=hashes.SHA256(),
----> 6 label=None
7 )
8 )
C:\ProgramData\OWTools\miniconda\lib\site-packages\cryptography\hazmat\backends\openssl\rsa.py in decrypt(self, ciphertext, padding)
384 key_size_bytes = int(math.ceil(self.key_size / 8.0))
385 if key_size_bytes != len(ciphertext):
--> 386 raise ValueError("Ciphertext length must be equal to key size.")
387
388 return _enc_dec_rsa(self._backend, self, ciphertext, padding)
ValueError: Ciphertext length must be equal to key size.
One thought which might be useful is that in the python code to encrypt, the cipher text comes out in the format:
b'N\xf2\xa7\xfaW6[Z\x0c\x14\xe7\xc8\xcb\xed\xa2xM\xea\x83\xa3\xb7\x05few!\x0e\\zd\xfe g\xbbO1\xfe\n\xa0Q\xab\xb8e0\xe9R^\xc8\xbbU\xfbuxp\x00\xf7>\xf8\x14\x1c\xd7\xca}\xd3\n{\xc9\xa2^\x8c0.\x9a\\\xe2\x89\xfd\xbf\xabw\x03\xc4\xdc\xfdX\xcf\xcc\x01\xc7\xc8\\j\xa9\xd7U\xfbP\xc2$\xd9^\x9e1\x9d&\xf1\x1b\x08%\xb3kF`\x19\xeb\xc8\xf0\x9f\xb1\x13\xaa\xa1\xed+X\xdc\x10!Fw\xae\x93\xb0\x80\xf3}\x98nz^\x04\xfe\x08T\x98d>[\xa7\x92\x08\x8a\r\xbc\x85\xf8\xcb\xce\xcc\x94\x95V\xf0\xad\x8d\x94N\x0c\x03\xf0\xbcT\x06%\x0b\x06\xcb_\xcauY$\n\xbc\xea\xde\xe4G\x05F\xf5\xef\x10\x83w"f5*&?\\9\x08\xd3}\xf0X\xb7\x06F\x99\xe1\xf3\x02\x14a\x7f\x1a0,?{\x12^|\xba\xed\xf6s\xed\xe6@m\xc7ex\x86\xf1\xae\xeec=\x92\x7fZ\x19\xee\x0f\x86\xb8\xf5T_\x9f\x1c'
which is obviously quite different to the java output - though i'm not entirely sure why (or what that means!)
Any help much appreciated! Thanks
Based on the comment from Kelakela, i realised that the problem was that the output of the Java code is in Base64, while the python code requires Binary input.
The solution was as simple as:
from codecs import decode
ciphertext = base64.decode(java_out, 'base64')
and then running private_key.decrypt(ciphertext, ... )
on the output in binary format.
The rest of the code works fine for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With