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?
Advertisement
Answer
Your PHP function has OPENSSL_PKCS1_OAEP_PADDING
but your java function is using RSA/ECB/PKCS1PADDING
- Change your PHP decryption to OPENSSL_PKCS1_PADDING which seems to match your java encryption.
OR
- switch your java encryption to
RSA/ECB/OAEPWithSHA-1AndMGF1Padding