2

I have a PHP script that I have been using do decrypt a session key that I encrypted from iOS. The encryption is done on the client using a 1024-bit public key. Decryption on the server side is done with the corresponding private key. Now I'm trying to write an encryption method for Android. Unfortunately, the decryption continues to fail, and I can't see what is wrong.

Here is the Android code:

public String encryptSessionKeyWithPublicKey(String pemString, byte[] sessionKey) {
    try {
        PublicKey publicKey = getPublicKeyFromPemFormat(pemString);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherData = cipher.doFinal(sessionKey);
        return Base64.encodeToString(cipherData, Base64.DEFAULT);
    } catch (IOException ioException) {
        Log.e(TAG, "ioException");
    } catch (NoSuchAlgorithmException exNoSuchAlg) {
        Log.e(TAG, "NoSuchAlgorithmException");
    } catch (InvalidKeySpecException exInvalidKeySpec) {
        Log.e(TAG, "InvalidKeySpecException");
    } catch (NoSuchPaddingException exNoSuchPadding) {
        Log.e(TAG, "NoSuchPaddingException");
    } catch (InvalidKeyException exInvalidKey) {
        Log.e(TAG, "InvalidKeyException");
    } catch (IllegalBlockSizeException exIllBlockSize) {
        Log.e(TAG, "IllegalBlockSizeException");
    } catch (BadPaddingException exBadPadding) {
        Log.e(TAG, "BadPaddingException");
    }

    return null;
}

private PublicKey getPublicKeyFromPemFormat(String PEMString)
        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
    AssetManager assetManager = context.getAssets();
    InputStream inputStream = assetManager.open(PEMString);
    BufferedReader pemReader = new BufferedReader(new InputStreamReader(inputStream));

    StringBuffer content = new StringBuffer();
    String line = null;
    while ((line = pemReader.readLine()) != null) {
        if (line.indexOf("-----BEGIN PUBLIC KEY-----") != -1) {
            while ((line = pemReader.readLine()) != null) {
                if (line.indexOf("-----END PUBLIC KEY") != -1) {
                    break;
                }
                content.append(line.trim());
            }
            break;
        }
    }
    if (line == null) {
        throw new IOException("PUBLIC KEY not found");
    }

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decode(content.toString(), Base64.DEFAULT)));

}

The PHP script is fairly simple:

<?php

$passphrase = 'xxxxxxxx'; // just for testing - load from file later
$decrypted_session_key = 'unavailable';

$encrypted_session_key = base64_decode($_POST['encrypted_session_key']);

$fp = fopen('private128.pem', 'r');
if ($fp == null) { 
    $arr = array('response' => 'failure', 'message' => 'private key not readable');
    echo json_encode($arr);
    die();
}

$priv_key = fread($fp, 8192);
fclose($fp);

$res = openssl_get_privatekey($priv_key, $passphrase);
$decr_result = openssl_private_decrypt($encrypted_session_key, $decrypted_session_key, $res, OPENSSL_PKCS1_OAEP_PADDING);

if (!$decr_result) {
    $arr = array('response' => 'failure', 'message' => $decr_result);
    echo json_encode($arr);
    die();
}

// write the decrypted string to a file
$session_key_file = fopen("session_key", "w") or die("Unable to open file!");
fwrite($session_key_file, $decrypted_session_key);
fclose($session_key_file);

$arr = array('response' => 'success', 'message' => 'server confirms receipt of session key');
echo json_encode($arr);

?>

All I am trying to encrypt are 16 randomly generated bytes.

The PHP output is: {"response":"failure","message":false} which means that openssl_private_decrypt line isn't getting a correct decryption result.

Since my PHP script works with my iOS code, I do not want to change it unless absolutely necessary. Can anyone see what I should do to my Java code to align it with what is happening on the PHP side?

5
  • generally speaking, you should be just doing $priv_key = file_get_contents(...). consider what'd happen if your key file ended up being more than 8k long - you'd be loading a partial key. Commented Feb 18, 2015 at 17:10
  • Good point. I know that the private key file is only 1751 bytes. Commented Feb 18, 2015 at 17:15
  • How long is the message? Commented Feb 18, 2015 at 17:32
  • I'm just encrypting 16 randomly generated bytes of data (128 bits). Commented Feb 18, 2015 at 17:55
  • Don't use PKCS1. Commented Mar 6, 2016 at 19:05

1 Answer 1

5

Your PHP function has OPENSSL_PKCS1_OAEP_PADDING but your java function is using RSA/ECB/PKCS1PADDING

  1. Change your PHP decryption to OPENSSL_PKCS1_PADDING which seems to match your java encryption.

OR

  1. switch your java encryption to RSA/ECB/OAEPWithSHA-1AndMGF1Padding
Sign up to request clarification or add additional context in comments.

3 Comments

I knew that the padding would come into this somewhere. Thanks very much!
Please change "javascript" -> "java". solution 2 was helpful! Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.