【问题标题】:Android Device Decryption of PHP RSA encrypted string fails with incorrect decrypted resultPHP RSA加密字符串的Android设备解密失败,解密结果不正确
【发布时间】:2015-07-09 08:25:28
【问题描述】:

加密字符串 PHP-serverside 的代码。 我使用 PKCS8 而不是 PKCS1,这样我就可以在 Android 端解密。

加密代码如下: 我使用 phpseclib。

include('libs/PHPSecLib/Crypt/RSA.php');

...省略了查找存储在数据库中的公钥和私钥的代码...

$rsa = new Crypt_RSA();
$rsa->loadKey($row['pref_pub_key']); // public key stored in MySQL BLOB.

$plaintext = 'Testing 123';

$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS8); //USE PKCS8
$ciphertext = $rsa->encrypt($plaintext);

echo $ciphertext;

$rsa->loadKey($row['pref_priv_key']); // private key stored in MySQL BLOB
echo $rsa->decrypt($ciphertext);

$query = "UPDATE preferences SET pref_license = ?;          

    //execute query to store the encrypted text in pref_license BLOB field.
    try {
        $stmt   = $db->prepare($query);
        $stmt->bindParam(1,$ciphertext);
        $stmt->bindParam(2,$ref);

        $db->errorInfo();

        $result = $stmt->execute();
    }
    catch (PDOException $ex) {
        $response["success"] = 0;
        $response["message"] = "Database Error. Couldn't update Pref post with License!" . $ex->getMessage();
        echo $response["message"];
        die(json_encode($response));
    }

我基本上对字符串进行加密,并将其存储在一个 BLOB 字段中,以供以后参考。

我通过以下方式生成Private Public KEYPAIR并存储在BLOB中,并将私钥发送到Android设备:

include('libs/PHPSecLib/Crypt/RSA.php');

$rsa = new Crypt_RSA();

$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS8);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS8);

extract($rsa->createKey()); 

//echo $privatekey . '<br/><br/>' . $publickey; //I can see that it worked!

if (!empty($_POST)) {
    //initial update query to store the keys in BLOBS on SERVER MYSQL

    $query = "UPDATE  preferences SET pref_priv_key = ?, pref_pub_key = ?
    WHERE pref_device_serial = ?";


    //execute query
    try {
        $stmt   = $db->prepare($query);
        $stmt->bindParam(1,$privatekey);
        $stmt->bindParam(2,$publickey);
        $stmt->bindParam(3,$_POST['p_device_serial']);


        $db->errorInfo();

        $result = $stmt->execute();
    }
    catch (PDOException $ex) {
        $response["success"] = 0;
        $response["message"] = "Database Error. Couldn't update Pref post!" . $ex->getMessage();
        die(json_encode($response));
    }
}

//then I send the $privatekey to the Android device and save it there.
//to then later decrypt the serverside encrypted string.

$response["success"] = 1;
$response["pk"] = $privatekey;
$response["message"] = "Key Pair successfully generated.";    

    echo json_encode($response);

在 Android 设备上,我使用 ans AsyncTask 请求加密字符串,然后从本地 sqlite blob 字段中读取 PrivateKey,并尝试解密字符串:

 class GetLicense extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(LicenseActivity.this);
            pDialog.setMessage("Retrieving License Data...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(true); 
            pDialog.show();
        }

        @Override
        protected String doInBackground(String... args) {
            int success;
            String elicense;

            try {
                getPref = LicenseActivity.this.openOrCreateDatabase("aaa.db", Context.MODE_PRIVATE, null);

                Cursor c = getPref.rawQuery("SELECT * FROM preferences", null);


                Log.d("request!", "starting");
                if (c != null) {
                    if (c.moveToFirst()) {
                        do {

                            String Preferences_Id = c.getString(c.getColumnIndex(SupaAttendDb.KEY_ROWID));
                            String Preferences_UUID = c.getString(c.getColumnIndex(SupaAttendDb.KEY_Preferences_PREFUUID));
                            String Preferences_Device_Serial = c.getString(c.getColumnIndex(SupaAttendDb.KEY_Preferences_PREFDEVICESERIAL));
                            sPK = c.getString(c.getColumnIndex(SupaAttendDb.KEY_Preferences_PREFPK));

                            // Building Parameters

                            List<NameValuePair> params = new ArrayList<NameValuePair>();
                            params.add(new BasicNameValuePair("p_uuid", Preferences_UUID));

                            try {
                                //Get Encrypted License from server
                                JSONObject json = jsonParser.makeHttpRequest(
                                        GET_LICENSE_URL, "POST", params);

                                // full json response

                                // json success element
                                success = json.getInt(TAG_SUCCESS);
                                if (success == 1) {
                                    sData = json.getString(TAG_LICENSE);

..然后我将许可证保存到 Android SQLite。不需要代码... 在 onPostExecute 中,我格式化 PrivateKey,然后尝试解密 sData,但返回的数据不正确,而不是“测试 123”。

        protected void onPostExecute(String file_url) {

            pDialog.dismiss(); 
            String privKeyPEM = sPK.replace("-----BEGIN PRIVATE KEY-----\r\n", "");
            privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
            byte[] b = Base64.decode(privKeyPEM,Base64.DEFAULT);

            KeyFactory keyFactory = null;
            try {
                keyFactory = KeyFactory.getInstance("RSA");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }

            EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(b); //This decodes properly without any exceptions.
            PrivateKey privateKey2 = null;
            try {
                privateKey2 = keyFactory.generatePrivate(privateKeySpec);
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            } 
            byte[] decryptedData = null;
            Cipher cipher = null;
            try {
                cipher = Cipher.getInstance("RSA");
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } 
            try {
                cipher.init(Cipher.DECRYPT_MODE,privateKey2);
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }
            byte[] sD = Base64.decode(sData, Base64.DEFAULT);// Here I try to get the encrypted string retrieved from server into a byte[].
            try {
                decryptedData = cipher.doFinal(sD); // no errors, but I get the incorrect unencrypted string.
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            }
            if (decryptedData != null){
                String decrypted = new String(decryptedData);
                //decryptedData = Base64.encode(decryptedData,Base64.DEFAULT);
                Toast.makeText(LicenseActivity.this, decrypted, Toast.LENGTH_LONG).show();
            }

        }
}

我意识到我只是在最后一段代码中做了一些愚蠢的事情,我尝试解码加密字符串,然后解密它。 希望你能指出我正确的方向,抱歉啰嗦了。

哦,是的,在你问之前,我使用以下 PHP 从服务器检索许可证:

require("config.inc.php");

if (!empty($_POST)) {

//initial query
$query = "SELECT pref_uuid, pref_license, pref_device_serial FROM preferences WHERE pref_uuid = :p_uuid"; 

    $query_params = array(':p_uuid' => $_POST['p_uuid']); 

    //execute query
    try {
        $stmt = $db->prepare($query);

        $db->errorInfo();

        $result = $stmt->execute($query_params);
    }
    catch (PDOException $ex) {
        $response["success"] = 0;
        $response["message"] = "Database Error. Couldn't retrieve License details!" . $ex->getMessage();
        die(json_encode($response));
    }

    if(!$result) {
        $response["success"] = 0;
        $response["message"] = "Database Error. Couldn't return Licenses!" . $ex->getMessage();
        die(json_encode($response));
    }


    $row = $stmt->fetch(PDO::FETCH_ASSOC);

        $response["license"] = base64_encode($row['pref_license']); // I encode it here before I send the encrypted string off to android device.
        $response["message"] = "License Record successfully retrieved";
        $response["success"] = 1; 

echo json_encode($response); 

}

【问题讨论】:

  • 这是最小的工作示例吗?如果是这样,哎呀!
  • PKCS#8 是“私钥信息语法规范”。虽然它支持加密的私钥,但你不应该直接用它加密字符串。相反,请使用 PKCS#1(1.5 填充或 OAEP,后者更好)或使用 PKCS#7/CMS 进行加密。
  • 亲爱的 Maarten Bodewes,我使用的是 PKCS#1,但 Android 显然不支持它,因此建议我更改为 PKCS#8。
  • OAEP 不仅更好; 1.5 绝对可怕。

标签: php android encryption rsa phpseclib


【解决方案1】:

好的,多亏了Maarten Bodewes,我回到服务器端的PKCS#1,然后无法按预期在Android端使用它。我发现了SpongyCastle library,并且能够从 PrivateKey 中提取 Modulus 和 privateExponent,然后我能够成功地解密加密的字符串。谢谢马丁!!!!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多