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