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

时间:2015-04-29 20:41:04

标签: php android encryption rsa phpseclib

加密字符串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,但返回不正确的数据,而不是'Testing 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); 

}

1 个答案:

答案 0 :(得分:0)

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