普通网友 2015-04-29 20:41
浏览 137

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

}
  • 写回答

1条回答

  • douxianji6181 2015-04-30 19:10
    关注

    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!!!!

    评论

报告相同问题?

悬赏问题

  • ¥15 LiBeAs的带隙等于0.997eV,计算阴离子的N和P
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 来真人,不要ai!matlab有关常微分方程的问题求解决,
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?
  • ¥100 求三轴之间相互配合画圆以及直线的算法