2015-04-29 69 views
0

加密字符串PHP代碼的服務器端。 我使用PKCS8而不是PKCS1,這樣我就可以在Android端解密。Android的PHP設備解密RSA加密字符串失敗,解密結果不正確

加密的代碼如下: 我使用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字段,供日後參考。

我產生了私有公有密鑰對,並存儲在以下列方式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字段中的專用密鑰,並嘗試解密字符串:

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我保存許可證到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); 

} 
+1

這是最低工作的例子嗎?如果是這樣,那麼! – KevinDTimm

+1

[PKCS#8](https://tools.ietf.org/html/rfc5208)是「私鑰信息語法規範」。雖然它支持加密私鑰,但不應該直接使用它加密字符串。相反,使用PKCS#1(1.5填充或OAEP,這更好)或使用PKCS#7/CMS進行加密。 –

+0

親愛的Maarten Bodewes,我使用的是PKCS#1,但Android並不明顯支持它,因此我被建議更改爲PKCS#8。 –

回答

0

行,所以拜Maarten Bodewes,我又回到了PKCS#1在服務器端,然後couldn沒有按照預期在Android上工作。我發現SpongyCastle library,並能夠從PrivateKey中提取Modulus和privateExponent,然後我能夠成功解密加密的字符串。謝謝MAARTEN !!!!

+0

[PKCS#1不安全](https://stackoverflow.com/a/34088308/2224584)。 –