In my Android app I am communicating with a web service the data sent and responded are encrypted with AES encryption.
So what I do is the following. I'm sending a base64 encoded AES encrypted JSON String to share.php
Share.php will then decrypt this string and insert it into the database. After that the PHP will encrypt en encode the response.
My Android application then needs to decode en decrypt this message.
But the decryption of the PHP response is not going very well.
This is my AES.java:
public class AES {
private final String characterEncoding = "UTF-8";
private final String cipherTransformation = "AES/ECB/PKCS5Padding";
private final String aesEncryptionAlgorithm = "AES";
public byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
//IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
//cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy);
System.out.println("Do final: "+cipherText);
cipherText = cipher.doFinal(cipherText);
return cipherText;
}
public byte[] encrypt(byte[] plainText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
//IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
//cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
plainText = cipher.doFinal(plainText);
return plainText;
}
private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= key.getBytes(characterEncoding);
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
/// <summary>
/// Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
/// </summary>
/// <param name="plainText">Plain text to encrypt</param>
/// <param name="key">Secret key</param>
/// <returns>Base64 encoded string</returns>
public String encrypt(String plainText, String key) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
byte[] plainTextbytes = plainText.getBytes(characterEncoding);
byte[] keyBytes = getKeyBytes(key);
//return Base64.encodeToString(encrypt(plainTextbytes,keyBytes, keyBytes), Base64.DEFAULT);
return Base64.encodeToString(encrypt(plainTextbytes,keyBytes, new byte[0]), Base64.DEFAULT);
}
/// <summary>
/// Decrypts a base64 encoded string using the given key (AES 128bit key and a Chain Block Cipher)
/// </summary>
/// <param name="encryptedText">Base64 Encoded String</param>
/// <param name="key">Secret Key</param>
/// <returns>Decrypted String</returns>
public String decrypt(String encryptedText, String key) throws KeyException, GeneralSecurityException, GeneralSecurityException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException{
byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT);
byte[] keyBytes = getKeyBytes(key);
//return new String(decrypt(cipheredBytes, keyBytes, keyBytes), characterEncoding);
return new String(decrypt(cipheredBytes, keyBytes, new byte[0]), characterEncoding);
}
}
And this is the code to encode en encrypt the response in PHP:
function mc_encrypt($encrypt, $mc_key) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
$passcrypt = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $mc_key, trim($encrypt), MCRYPT_MODE_ECB, $iv));
$encode = base64_encode($passcrypt);
return $encode;
}
function mc_decrypt($decrypt, $mc_key) {
$decoded = base64_decode($decrypt);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $mc_key, trim($decoded), MCRYPT_MODE_ECB, $iv));
return $decrypted;
}
I'm guessing that the settings of the PHP encryption do not match the settings for the Java part. Can
I'm getting the following error:
03-12 13:44:09.661: W/System.err(15717): javax.crypto.BadPaddingException: pad block corrupted
I suggest you take a look at http://phpaes.com/. It's a free AES encryption library implemented purely in PHP; it's fast and very very simple to use.
At the very least, it allows you get one step closer to isolating the true source of the issue.
This might not be the answer you're looking for - but is there a specific reason you're manually encrypting this data instead of using SSL/HTTPS?
In most cases HTTPS will be easier to implement and more secure than manually implementing a symmetric cipher.
Related
I try to decrypt to json response in Android from node js server and it does not decrypt it correctly. But it decrypted correctly a string or JSONArray. When i decrypt json object in node js from android, it decrypts correctly. Can you help me to understand what is wrong?
Node js
function encryptDataTestWork(text, masterkey) {
// body...
// random initialization vector
const iv = crypto.randomBytes(16);
// random salt
const salt = crypto.randomBytes(64);
// derive encryption key: 32 byte key length
// in assumption the masterkey is a cryptographic and NOT a password there is no need for
// a large number of iterations. It may can replaced by HKDF
// the value of 2145 is randomly chosen!
const key = crypto.pbkdf2Sync(masterkey, salt, 2145, 32, 'sha512');
// AES 256 GCM Mode
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
// encrypt the given text
const encrypted = Buffer.concat([
cipher.update(text, 'utf8'),
cipher.final()
]);
// extract the auth tag
const tag = cipher.getAuthTag();
// generate output
return Buffer.concat([salt, iv, tag, encrypted]).toString('base64');
};
let item = JSON.stringify({name:'Selemani',lastName:'Jimmy'});
let generatedKey = 'SEXFgk1kQ3BcDzrH';
let encryptedMobile = encryptDataTestWork(item, generatedKey);
console.log('data->[encrypted] -> '+encryptedMobile);
res.send({tag: 'success', data: encryptedMobile, key: generatedKey});
And Android code
private final static int GCM_TAG_LENGTH = 16;
public static String decryptHelper(String algorithm, byte[] cipherText, byte[] tag,SecretKey key,
GCMParameterSpec gcmParameterSpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
cipher.update(cipherText);
byte[] plainText = cipher.doFinal(tag);
return new String(plainText, StandardCharsets.UTF_8);
}
public static String decrypt(String encData, String masterKey) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException {
byte[] cipherText;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cipherText = Base64.getDecoder().decode(encData.getBytes(StandardCharsets.UTF_8));
}else{
cipherText = android.util.Base64.decode(encData.getBytes(StandardCharsets.UTF_8), android.util.Base64.DEFAULT);
}
byte[] salt = Arrays.copyOfRange(cipherText, 0, 64);
byte[] iv = Arrays.copyOfRange(cipherText, 64, 80);
byte[] tag = Arrays.copyOfRange(cipherText, 80, 96);
byte[] ciphertext = Arrays.copyOfRange(cipherText, 96, cipherText.length);
SecretKey key = getKeyFromPassword(masterKey, salt);
return decryptHelper("AES/GCM/NoPadding", ciphertext, tag, key, new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv));
}
Here is what i see in logs
Data Response: {"tag":"success","data":"yMiFnU7JNhCtFY1buCO5kGgj8rOm3L+iAr9JxBwziMiPes4mHctcvX+V2crOncOQeLKikGo+RY82mOrKu4DWhhmejdI+/UDHWYqs2k54w4SDED8IeJzZ+1kM5mgB+WMMWI6zb5+9r6TAb6ThBFoE6Ot3dajtvVUe6LwGGiUugViNCGtu","key":"SEXFgk1kQ3BcDzrH"}
2022-04-30 12:29:46.298 28249-28281/com.hello.app E/HttpConnexion: n(JSON_OBJECT=key): SEXFgk1kQ3BcDzrH
2022-04-30 12:29:46.337 28249-28281/com.hello.app E/HttpConnexion: Decrypted: "lastName":"Jimmy"}// decrypted text i get. It should be {name:'Selemani',lastName:'Jimmy'}
2022-04-30 12:29:46.338 28249-28281/com.hello.app W/System.err: org.json.JSONException: Value lastName of type java.lang.String cannot be converted to JSONObject
If i send a string like "Hello World", it correctly decrypts.
I did what #Topaco said and it worked!
public static String decryptHelper(String algorithm, byte[] cipherText, byte[] tag,SecretKey key,
GCMParameterSpec gcmParameterSpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
byte[] plain = cipher.update(cipherText);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(plain);
byte[] plainText = cipher.doFinal(tag);
outputStream.write(plainText);
return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
}
Thank you! #Topaco You saved me!
I'm trying to encrypt and decrypt some text using AES algorithm like :
private static final String ALGORITHM = "AES";
private static final String MODE = "AES/CBC/PKCS7Padding";
public String encrypt(String value, String key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(key.getBytes()));
byte[] values = cipher.doFinal(value.getBytes());
return Base64.encodeToString(values, Base64.URL_SAFE);
}
public String decrypt(String value, String key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
byte[] values = Base64.decode(value, Base64.DEFAULT);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(key.getBytes()));
return new String(cipher.doFinal(values));
}
The encrypting code works perfectly fine but when I'm trying to decrypt the encrypted text it returns a bad base64 error.
What I tried to do before:
I already tried to change Base64.DEFAULT to Base64.URL_SAFE but it gave me the same error.
Also, I tried to replace "/" and "+" with "_" and "-" and it doesn't work either.
Heres the value I'm encrypted and trying to decrypt:
Ssg2w+dv7es7/wWAeAcoAOSVnYKsoLlefbmS8tYr+jc=
Thanks for helping...
everyone.
I am trying to encrypt and decrypt a string with an AES symmetric key, generated with a password. My current code for generating this follows below:
public class AESUtils {
public static SecretKey getKeyFromPassword(String password, String salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
return new SecretKeySpec(factory.generateSecret(spec)
.getEncoded(), "AES");
}
public static IvParameterSpec generateIv() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
public static String encryptPasswordBased(String plainText, SecretKey key, IvParameterSpec iv)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes()));
}
public static String decryptPasswordBased(String cipherText, SecretKey key, IvParameterSpec iv)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
}
}
The code to generate the encrypted string:
AESUtils.encryptPasswordBased(string_content_plain, AESUtils.getKeyFromPassword("password", "salt"), AESUtils.generateIv());
The code to generate the decrypted string:
AESUtils.decryptPasswordBased(string_content_encrypted, AESUtils.getKeyFromPassword("password", "salt"), AESUtils.generateIv());
The encryptPasswordBased works fine, but when I use the decryptPasswordBased, it always raises javax.crypto.BadPaddingException with the message: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
What can I do? The generated key is always the same. I have compared it with both byte arrays.
Thanks
As #Topaco said, the solution was to use the same IV when decrypting the content. The generation of a new one would make it impossible to decrypt.
I'm trying to encrypt data with RSA public key in Java and decrypt it in Crypto++. It results in an error:
"RSA/EME-PKCS1-v1_5: ciphertext length of 24 doesn't match the required length of 128 for this key"
What am I doing wrong?
Java:
String cipher = Encryption.encryptStrRSA(txt, pubKeyk);
public static String encryptStrRSA(String str, PublicKey pubKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encryptedAesKey = cipher.doFinal(str.getBytes());
String cipherStr = new String(encryptedAesKey);
System.out.println(cipherStr);
return cipherStr;
}
public static PublicKey strToPublicKey(String key64) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(key64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
public static String publicKeyToStr(PublicKey publ) throws GeneralSecurityException {
KeyFactory fact = KeyFactory.getInstance("RSA");
X509EncodedKeySpec spec = fact.getKeySpec(publ, X509EncodedKeySpec.class);
return Base64.getEncoder().encode(spec.getEncoded()).toString();
}
Crypto++:
using namespace CryptoPP;
RSAES_PKCS1v15_Decryptor priv(privString);
StringSource( cipher, cipherSize, true, new
Base64Decoder( new PK_DecryptorFilter(randPool, priv, new StringSink(sdata))));
It is dangerous to use String instances for keeping binary data -- you should use byte[] instead.
Additionally, in the java code there is no Base64 wrapping of the resulting ciphertext, but in the C++ code it is being unwrapped from Base64.
Modified your code to return byte[] and encode the result using Base64:
public static byte[] encryptRSA(String str, PublicKey pubKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(str.getBytes());
}
String cipher = Base64.getEncoder().encodeToString(Encryption.encryptRSA("0123456789ABCDEF", pubKeyk));
Then you can decrypt in Crypto++ the same way you did.
Good luck!
I found the following code on an encryption website ... it works as a Java client and a C# server. This code works great except there is one problem I cannot figure out. If I send small messages everything is fine ... the problem starts when I send more than 47 char to the server at this point it fails with the following error:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
Can someone look this over or possibly test it for themselves and find a solution … thanks.
Java Code:
public class Crypto {
private final String characterEncoding = "UTF-8";
private final String cipherTransformation = "AES/CBC/PKCS5Padding";
private final String aesEncryptionAlgorithm = "AES";
public byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);
cipherText = cipher.doFinal(cipherText);
return cipherText;
}
public byte[] encrypt(byte[] plainText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
plainText = cipher.doFinal(plainText);
return plainText;
}
private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= key.getBytes(characterEncoding);
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
/// <summary>
/// Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
/// </summary>
/// <param name="plainText">Plain text to encrypt</param>
/// <param name="key">Secret key</param>
/// <returns>Base64 encoded string</returns>
public String encrypt(String plainText, String key) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
byte[] plainTextbytes = plainText.getBytes(characterEncoding);
byte[] keyBytes = getKeyBytes(key);
return Base64.encodeToString(encrypt(plainTextbytes,keyBytes, keyBytes), Base64.DEFAULT);
}
/// <summary>
/// Decrypts a base64 encoded string using the given key (AES 128bit key and a Chain Block Cipher)
/// </summary>
/// <param name="encryptedText">Base64 Encoded String</param>
/// <param name="key">Secret Key</param>
/// <returns>Decrypted String</returns>
public String decrypt(String encryptedText, String key) throws KeyException, GeneralSecurityException, GeneralSecurityException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException{
byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT);
byte[] keyBytes = getKeyBytes(key);
return new String(decrypt(cipheredBytes, keyBytes, keyBytes), characterEncoding);
}
C# Code:
class Crypto
{
public static string currentPassword;
public RijndaelManaged GetRijndaelManaged(String secretKey)
{
var keyBytes = new byte[16];
var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
return new RijndaelManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
Key = keyBytes,
IV = keyBytes
};
}
public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
{
return rijndaelManaged.CreateEncryptor()
.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
}
public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged)
{
return rijndaelManaged.CreateDecryptor()
.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
}
/// <summary>
/// Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
/// </summary>
/// <param name="plainText">Plain text to encrypt</param>
/// <param name="key">Secret key</param>
/// <returns>Base64 encoded string</returns>
public String Encrypt(String plainText, String key)
{
var plainBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key)));
}
/// <summary>
/// Decrypts a base64 encoded string using the given key (AES 128bit key and a Chain Block Cipher)
/// </summary>
/// <param name="encryptedText">Base64 Encoded String</param>
/// <param name="key">Secret Key</param>
/// <returns>Decrypted String</returns>
public String Decrypt(String encryptedText, String key)
{
var encryptedBytes = Convert.FromBase64String(encryptedText);
return Encoding.UTF8.GetString(Decrypt(encryptedBytes, GetRijndaelManaged(key)));
}
}