普通网友 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 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型