【发布时间】:2015-07-09 08:25:28
【问题描述】:
加密字符串 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,但返回的数据不正确,而不是“测试 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);
}
【问题讨论】:
-
这是最小的工作示例吗?如果是这样,哎呀!
-
PKCS#8 是“私钥信息语法规范”。虽然它支持加密的私钥,但你不应该直接用它加密字符串。相反,请使用 PKCS#1(1.5 填充或 OAEP,后者更好)或使用 PKCS#7/CMS 进行加密。
-
亲爱的 Maarten Bodewes,我使用的是 PKCS#1,但 Android 显然不支持它,因此建议我更改为 PKCS#8。
-
OAEP 不仅更好; 1.5 绝对可怕。
标签: php android encryption rsa phpseclib