PHP RSA加密字符串的Android设备解密失败,解密结果不正确

The code that encrypts a string PHP-serverside. I use the PKCS8 and not PKCS1 so that I can unencrypt on Android side.

The code for the Encryption follows: I use the phpseclib.

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

...code to lookup public and private keys stored in database omitted...

$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));
    }

I basically encrypt the string, and store it in a BLOB field, for later reference.

I generate the Private Public KEYPAIR and store in BLOB in the following manner, and send the privatekey to the Android device:

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);

On the Android device, I use ans AsyncTask to request the Encrypted String and then read the PrivateKey from the local sqlite blob field, and try and decrypt the string:

 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);

..then I save the the license to Android SQLite. code not required ... In the onPostExecute, I format the PrivateKey and then try to decrypt the sData, but get the incorrect data back, and not 'Testing 123'.

        protected void onPostExecute(String file_url) {

            pDialog.dismiss(); 
            String privKeyPEM = sPK.replace("-----BEGIN PRIVATE KEY-----
", "");
            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();
            }

        }
}

I realise I'm just doing something stupid in this last bit of code where I try and decode the Encrypted string, and then decrypt it. Hope you can point me in the correct direction, and sorry for being long winded.

Oh and yes, before you ask, I retrieve the license from the server with the following 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); 

}
dongyinting3179
dongyinting3179 OAEP不仅仅是更好的;1.5绝对可怕。
4 年多之前 回复
dongyou2635
dongyou2635 亲爱的MaartenBodewes,我使用的是PKCS#1,但Android显然不支持它,因此我被建议改为PKCS#8。
大约 5 年之前 回复
doudong4532
doudong4532 PKCS#8是“私钥信息语法规范”。虽然它支持加密的私钥,但您不应该使用它直接加密字符串。相反,使用PKCS#1(1.5填充或OAEP,这是更好的)或使用PKCS#7/CMS加密。
大约 5 年之前 回复
douxie1894
douxie1894 这是最小的工作示例吗?如果是的话,哎呀!
大约 5 年之前 回复

1个回答



好的,感谢 Maarten Bodewes ,我回到服务器端的PKCS#1,然后无法按预期在Android端使用它。 我发现了 SpongyCastle库,并且能够从PrivateKey中提取模数和privateExponent,然后我就是 能够成功解密加密的字符串。 感谢MAARTEN !!!! </ p>
</ div>

展开原文

原文

OK so thanks to Maarten Bodewes , I went back to PKCS#1 on the serverside, then couldn't work with it on Android side as expected. I discovered SpongyCastle library, and was able to extract the Modulus and privateExponent from the PrivateKey, then I was able to successfully unencrypt the encrypted string. THANKS MAARTEN!!!!

douqiao5543
douqiao5543 PKCS#1不安全。
4 年多之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问