Use case
While working on an android app that retrieves it’s data from a PHP-API I needed to build a both-sided en- and decryption layer to secure the requests and responses properly.
I decided to use an AES encryption with IvParameterSpec, SecretKeySpec and “AES/CBC/PKCS5Padding” on Java side, and mcrypt_module “rijndael-128″ on PHP side.
Implementations
PHP
<? class ApiCrypter { private $iv = 'fdsfds85435nfdfs'; #Same as in JAVA private $key = '89432hjfsd891787'; #Same as in JAVA public function __construct() { } public function encrypt($str) { $str = $this->pkcs5_pad($str); $iv = $this->iv; $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv); mcrypt_generic_init($td, $this->key, $iv); $encrypted = mcrypt_generic($td, $str); mcrypt_generic_deinit($td); mcrypt_module_close($td); return bin2hex($encrypted); } public function decrypt($code) { $code = $this->hex2bin($code); $iv = $this->iv; $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv); mcrypt_generic_init($td, $this->key, $iv); $decrypted = mdecrypt_generic($td, $code); mcrypt_generic_deinit($td); mcrypt_module_close($td); $ut = utf8_encode(trim($decrypted)); return $this->pkcs5_unpad($ut); } protected function hex2bin($hexdata) { $bindata = ''; for ($i = 0; $i < strlen($hexdata); $i += 2) { $bindata .= chr(hexdec(substr($hexdata, $i, 2))); } return $bindata; } protected function pkcs5_pad ($text) { $blocksize = 16; $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } protected function pkcs5_unpad($text) { $pad = ord($text{strlen($text)-1}); if ($pad > strlen($text)) { return false; } if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) { return false; } return substr($text, 0, -1 * $pad); } } ?>
JAVA
package com.cwilldev.crypt; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class ApiCrypter { private String iv = "myuniqueivparam"; private String secretkey = "mysecretkey"; private IvParameterSpec ivspec; private SecretKeySpec keyspec; private Cipher cipher; public ApiCrypter() { ivspec = new IvParameterSpec(iv.getBytes()); keyspec = new SecretKeySpec(secretkey.getBytes(), "AES"); try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } } public byte[] encrypt(String text) throws Exception { if(text == null || text.length() == 0) { throw new Exception("Empty string"); } byte[] encrypted = null; try { cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); encrypted = cipher.doFinal(text.getBytes("UTF-8")); } catch (Exception e) { throw new Exception("[encrypt] " + e.getMessage()); } return encrypted; } public byte[] decrypt(String code) throws Exception { if(code == null || code.length() == 0) { throw new Exception("Empty string"); } byte[] decrypted = null; try { cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); decrypted = cipher.doFinal(hexToBytes(code)); } catch (Exception e) { throw new Exception("[decrypt] " + e.getMessage()); } return decrypted; } public static String bytesToHex(byte[] data) { if (data==null) { return null; } int len = data.length; String str = ""; for (int i=0; i<len; i++) { if ((data[i]&0xFF)<16) { str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF); } else { str = str + java.lang.Integer.toHexString(data[i]&0xFF); } } return str; } public static byte[] hexToBytes(String str) { if (str==null) { return null; } else if (str.length() < 2) { return null; } else { int len = str.length() / 2; byte[] buffer = new byte[len]; for (int i=0; i<len; i++) { buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16); } return buffer; } } }
Encryption
Java
String encryptedRequest = ApiCrypter.bytesToHex(this.apiCrypter.encrypt(jsonParams.toString()));
PHP
$encrypted = $ApiCrypter->encrypt($str);
Decryption
Java
String res = new String( this.apiCrypter.decrypt( text ), "UTF-8" ); res = URLDecoder.decode(res,"UTF-8");
PHP
$decrypted = $ApiCrypter->decrypt($str);
Hire Professional Valet Parking Services in Dubai for Your Business, Hotels, Shopping Malls, Or Special Event Now! We ensure safe & Secure Parking.
A stroke happens when the blood supply to part of the brain is break or reduced, preventing brain cells from getting nutrients and oxygen. Normally, that loss of function causes in to difficulty to move an arm or leg (called paralysis). There might be loss of sensation or peculiar sensations in the affected areas.
https://sehagarden.com/our-specialities/ayurveda-treatment-for-stroke/
seriously its very helpfull https://bit.ly/3rqoAMc
Thank you for sharing this knowledgeable blog
https://piofront.com/
https://airmasteremirates.com/
https://airmastertape.com/
https://awesomedrive.ae/
https://brazilfloors.com/
Looking to it nearer, the byte exhibit contains exactly 0 esteems. I had no issues with it when utilizing the string at all in android, however when I put away the information in a SharedPreferences record, the XML got tainted as a result of it.
I have tried both versions of code (your as well as serpro’s). Both codes work great. But there is a potential issue with both the codes. If a UTF-8 string is encrypted on android then it will not decrypted by the php version correctly. For example, I encrypted “اسداسداسداسد” in android and then decrypted it using php. Instead of producing “اسداسداسداسد”, it produced “اسداسداسداسد”. Is there any way to resolve this problem?
My Code bat its not work
import android.content.Context;
import android.telephony.TelephonyManager;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
Context mContext;
TelephonyManager mtelemamanger;
static char[] HEX_CHARS = {’0′,’1′,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’a',’b',’c',’d',’e',’f'};
public String deviceid = mtelemamanger.getSimSerialNumber();
//public String deviceid = “894310210159403811111456457657845786485458″; ///READ THE Simcard Serialnumber and put it as deviceid
public String iv = new String (deviceid.substring(0, 16));
private static IvParameterSpec ivspec;
private static SecretKeySpec keyspec;
private static Cipher cipher;
private String SecretKey = “0123456789abcdef”;//Dummy secretKey (CHANGE IT!)
public MCrypt () {
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), “AES”);
try {
cipher = Cipher.getInstance(“AES/CBC/NoPadding”);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public MCrypt (Context context)//this is a constructor
{
mContext = context;
mtelemamanger = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception(“Empty string”);
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception(“[encrypt] ” + e.getMessage());
}
return encrypted;
}
public static byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception(“Empty string”);
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
//Remove trailing zeroes
if( decrypted.length > 0)
{
int trim = 0;
for( int i = decrypted.length – 1; i >= 0; i– ) if( decrypted[i] == 0 ) trim++;
if( trim > 0 )
{
byte[] newArray = new byte[decrypted.length - trim];
System.arraycopy(decrypted, 0, newArray, 0, decrypted.length – trim);
decrypted = newArray;
}
}
} catch (Exception e)
{
throw new Exception(“[decrypt] ” + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] buf)
{
char[] chars = new char[2 * buf.length];
for (int i = 0; i >> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = 0;
int size = 16;
int x = source.length() % size;
int padLength = size – x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}
i need help how to get on Android Device with getSimSerialNumber(); the Simcard serial number and use it as Secret key
ApiCrypter apiCrypter = new ApiCrypter();
String encryptedRequest = null;
try {
encryptedRequest = ApiCrypter.bytesToHex(apiCrypter.encrypt(“Text to Encrypt”));
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(this, encryptedRequest, Toast.LENGTH_LONG).show();
######################################################
i get null?!
help
i get null string ?!
A big thanks to Goran for his extra code, I had the problem that he stated, added his code and everything is working again.
Big Thanks
This git hub code is updated?I hope!!!!is it?
Hi Java folks,
the PHP script has an critical issue. The pkcs5_unpad must return $text in case the input string is not using padding.
The JAVA chipper does not apply a padding, when the input string is an exact multiple of the block size (this is what padding is for; it’s only used when size mismatches). The pkcs5_unpad() should return $text in this case.
protected function pkcs5_unpad($text) {
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) {
return $text;
}
if (strspn($text, chr($pad), strlen($text) – $pad) != $pad) {
return $text;
}
return substr($text, 0, -1 * $pad);
}
Happy Coding!
on PHP side
in some casess unpadd function pkcs5_unpad($text) will return false and because of that function decrypt($code) will fail, return false(bool) beside decrypted data
so i replace nex line in decrypt($code):
return $this->pkcs5_unpad($ut);
with:
$unpadded = $this->pkcs5_unpad($ut);
if($unpadded){
return $unpadded;
}else{
return $ut;
}
and now everything work, so if you have problem in some cases(rare) try this fix…
Hi Goran,
I appreciate your input. But may you tell me what exactly “in some cases” mean?
So I can update my code accordingly.
Cheers
This is a great posting and works /great/! Is this open-source? Thanks!
That’s the purpose of posting it online, isn’t it? :-)
Please refer to https://github.com/SeRPRo/Android-PHP-Encrypt-Decrypt since Sergio pointed out that he created the basis for this solution.
While I see modifications, it couldn’t hurt you to specify that you copied/modified code for other places. Like my github repository: https://github.com/SeRPRo/Android-PHP-Encrypt-Decrypt/blob/master/Java/src/com/serpro/library/String/MCrypt.java You also left the same tags I put in the Exceptions.. :)
Anyway.. I hope your code works well too
Hello Sergio,
I am sorry to don’t crosslink to your solution. Usually I am benefiting people I am using code of (as you can see in mostly all of my other posts). In this case I missed it – probably because I did not find all sources I have used as base. So thanks for your work and sharing!
47 protected function pkcs5_unpad($text) {
PHP Quellcode
48 $pad = ord($text{strlen($text)-1});
49 if ($pad > strlen($text)) {
50 return false;
51 }
kann mir bitte jemand erklären warum hier false zurückgeben wird ?
müsste da nicht der $text zurückgebenen werden ?
Hi Stefan,
as Goran mentioned just now you are not the only guy facing this issue.
Stay tuned for Goran’s reply.
Hi Stefan,
du hast recht. Der Code muss wie folgt lauten:
if ($pad > strlen($text)) {
return $text;
}
To the author of this website: Don’t wait for Goran’s reply. You can reproduce this issue, when passing a short encrypted string to decrypt() (on php side). Bye the way, nice work.
It works perfectly on android and PHP server side. Thanks for your gentle sharing. Thanks a lot!
Gracias me sirvio bastante
manito arriba
Works. Thanks.