Search code examples
javapythoncryptographypublic-key-encryptionpycrypto

Cannot decrypt blob send by Java server in python client


I have the following workflow.

  1. Use openssl to generate rsa keypair
  2. Post public key to java server
  3. Server constructs in memory public key
  4. Server encrypts text with public key and sends blob back
  5. Python decrypts blob with private key

Generate public/private keypair:

openssl genrsa -out privkey.pem 1024 # generate private key

openssl rsa -in privkey.pem -pubout > pubkey.pem # derive public key

openssl pkcs8 -topk8 -in privkey.pem -outform PEM -inform PEM -nocrypt -out   privkey.pkcs8 # convert private key to PKCS8 format

Python code

import requests
import base64
from Crypto.PublicKey import RSA

privkey_content = open('./keys/privkey.pkcs8', 'rb')
pubkey_content = open('./keys/pubkey.pem', 'rb')
priv_key = RSA.importKey(privkey_content.read())
pub_key = RSA.importKey(pubkey_content.read())
pubkey_der_content = pub_key.exportKey(format='DER')
data = {'key' : base64.b64encode(pubkey_der_content)}
resp = requests.post('http://localhost:9000/crypt/', data=data)
decoded = priv_key.decrypt(base64.b64decode(resp.text))
with open('/tmp/decoded.txt', 'wb') as f:
    f.write(decoded)

Java Server code:

package com.hs.works;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.jetty.http.HttpStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;

import javax.crypto.Cipher;

public class CryptServlet extends HttpServlet {

    byte[] encrypt(byte[] pubKey, String text) {
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKey k = (RSAPublicKey) keyFactory.generatePublic(keySpec);
            final Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, k);
            return cipher.doFinal(text.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String key = req.getParameter("key");
        byte[] decoded_bytes = Base64.decodeBase64(key.getBytes());
        byte[] encoded = encrypt(decoded_bytes, "SECRET_TEXT");
        resp.setStatus(HttpStatus.OK_200);
        resp.getWriter().write(new String(Base64.encodeBase64(encoded)));
    }
}

The output:

>>>f = open('/tmp/decoded.txt', 'rb')
>>>c = f.read()
>>> c
'\x02bt\x1bq\xb1\x9bc\xef\xe30Q,o)<\xe8!z3B\xb7\xcf\xecWfN\x0f\x92~q\xb2\xcb\xa7\x15?%\xd1\xbf0$A,\x1ap?bZ$\xca6\xeet \xfc\x8d\x11\xee\n\x1b \xd3\xba\xaf\t\xf4&\x01\xa3\xb9H~\xd5o\x0c\xb3c\xa8\xdd\x9f4*\x86\x92\xe0\xcb\xb4\x1d\xcb\x9889\x856\xf9\xff\x9cGf\xf9\xa4\xc7\xadR\xb5\xcaU\xfc\xd355\xae\x87\x80\x1e\x00SECRET_TEXT'
>>>
>>> c[len(c) - 11:]
'SECRET_TEXT'

On printing the output on the python side, I see SECRET_TEXT in the output but there is also a lot of gibberish along with it.

I am not sure it's related to padding or some other issue.


Solution

  • I was not specifying the correct cipher.

    Made following change in the server code:

    final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
    

    and following changes in the python client code:

    from Crypto.Cipher import PKCS1_OAEP
    rsa_key = RSA.generate(2048)
    cipher = PKCS1_OAEP.new(rsa_key)
    pubkey = rsa_key.publickey()
    data = {'key' : base64.b64encode(pubkey.exportKey(format='DER'))}
    resp = requests.post('http://localhost:9000/crypt/', data=data)
    decoded = cipher.decrypt(base64.b64decode(resp.text))