I am trying to learn AES by testing my code against https://aesencryption.net. I previously had an error in Base64.encodeBase64String and also Base64.decodeBase64 // encode/decode Base64. So I manipulated Base64 somehow to resolve the error. Now in my app the text is encrypted and decrypted properly, I think. But when I try to encrypt or decrypt the same text server-side (at aesencryption.net), the site is not able to decrypt my encrypted string. Please help.
Following is my code :
public class MainActivity extends AppCompatActivity {
static final String TAG = "SymmetricAlgorithmAES";
private static SecretKeySpec secretKey ;
private static byte[] key ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Original text
// {"type":"Success","httpCode":"200","code":"200","message":{"pin":"11111"},"extra":""}
String theTestText = "hi";
TextView tvorig = (TextView)findViewById(R.id.tvorig);
tvorig.setText("\n[ORIGINAL]:\n" + theTestText + "\n");
final String strPssword = "android";
setKey(strPssword);
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE,secretKey);
encodedBytes = c.doFinal(theTestText.getBytes());
} catch (Exception e) {
Log.e(TAG, "AES encryption error");
}
TextView tvencoded = (TextView)findViewById(R.id.tvencoded);
tvencoded.setText("[ENCODED]:\n" +
Base64.encodeToString(encodedBytes, Base64.DEFAULT) + "\n");
Log.d(TAG, Base64.encodeToString(encodedBytes, Base64.DEFAULT));
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, secretKey);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e(TAG, "AES decryption error");
}
TextView tvdecoded = (TextView)findViewById(R.id.tvdecoded);
tvdecoded.setText("[DECODED]:\n" + new String(decodedBytes) + "\n");
}
public static void setKey(String myKey){
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
System.out.println(key.length);
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
System.out.println(key.length);
System.out.println(new String(key,"UTF-8"));
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
Thanks in advance.
I do something like this which actually works ;)
public class AESCrypter {
private final Cipher cipher;
private final SecretKeySpec key;
private AlgorithmParameterSpec spec;
public AESCrypter(String password) throws Exception
{
// hash password with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(password.getBytes("UTF-8"));
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
key = new SecretKeySpec(keyBytes, "AES");
spec = getIV();
}
public AlgorithmParameterSpec getIV()
{
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
IvParameterSpec ivParameterSpec;
ivParameterSpec = new IvParameterSpec(iv);
return ivParameterSpec;
}
public String encrypt(String plainText) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8");
return encryptedText;
}
public String decrypt(String cryptedText) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(bytes);
String decryptedText = new String(decrypted, "UTF-8");
return decryptedText;
}
}
Call this class like this :
try {
AESCrypter _crypt = new AESCrypter("password");
String output = "";
String plainText = "top secret message";
output = _crypt.encrypt(plainText); //encrypt
System.out.println("encrypted text=" + output);
output = _crypt.decrypt(output); //decrypt
System.out.println("decrypted text=" + output);
} catch (Exception e) {
e.printStackTrace();
}
And for iPhone (Code is here) : https://github.com/Gurpartap/AESCrypt-ObjC
Hope this code works for you too :)
The PHP code uses CBC mode by default while your Java code does not specify the same, and leaves it to the underlying implementation. If I recall correctly its probably ECB/PKCS5Padding. Your PHP implementation is using 'CBC' with no padding (mcrypt doesn't support padding by default but you can do so manually).
Its very important be be very specific about mode, padding, and charset when working with different platforms else you will run into different defaults.
Try initializing your cipher as follows: Cipher.getInstance("AES/CBC/NoPadding");
fun encrypt(encrypted: String): String? {
try {
val secretKey = "secretKey" //The secret key, 32 bytes string.
val ivKey = "ivKey" // The initialization vector, 16 bytes string.
val iv = IvParameterSpec(hashIV.toByteArray(Charsets.UTF_8))
val skySpec = SecretKeySpec(hashKey.toByteArray(Charsets.UTF_8), "AES-256-CBC")
val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, skySpec, iv)
val original: ByteArray = cipher.doFinal(encrypted.toByteArray())
return Base64.encode(
Base64.encodeToString(original, Base64.NO_WRAP).toByteArray(charset("UTF-8")),
Base64.NO_WRAP
).decodeToString()
} catch (ex: Exception) {
ex.printStackTrace()
}
return null
}
fun decrypt(encrypted: String?): String? {
try {
val secretKey = "secretKey" //The secret key, 32 bytes string.
val ivKey = "ivKey" // The initialization vector, 16 bytes string.
val iv = IvParameterSpec(hashIV.toByteArray(Charsets.UTF_8))
val skySpec = SecretKeySpec(hashKey.toByteArray(Charsets.UTF_8), algorithm)
val cipher: Cipher = Cipher.getInstance(transformation)
cipher.init(Cipher.DECRYPT_MODE, skySpec, iv)
val original: ByteArray = cipher.doFinal(
Base64.decode(
Base64.decode(encrypted, Base64.NO_WRAP),
Base64.NO_WRAP
)
)
return original.decodeToString()
} catch (ex: Exception) {
ex.printStackTrace()
}
return null
}
openssl_encrypt
Related
I have an Android application (Built over JAVA) to be converted to Flutter. There is an encryption function and a decryption function in JAVA which I am looking to convert to DART.
However, it seems like something is missing in the conversion and therefore, it is not encrypting starting few characters in the string and therefore, when decrypting back, it does not bring the correct data back.
Below is the JAVA code in use.
public static String encrypt(Context ctx, String requestString) {
String encryptString = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = 'SOME16CHARSTRING'.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.out.println("Length - " + len);
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(requestString.getBytes("UTF-8"));
encryptString = Base64.encodeToString(encrypted, Base64.NO_WRAP);
} catch (Exception e) {
GlobalMethods.SendErrorReport(ctx, e);
e.printStackTrace();
}
return encryptString;
}
And the converted DART code is as below.
String encryptAES(String plainTextString) {
try {
final key = Key.fromUtf8('SOME16CHARSTRING');
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final encrypted = encrypter.encrypt(plainTextString, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
return encrypted.base64;
} catch (e) {
print("Error while encoding String : $e");
return null;
}
}
Anything which I can correct here ?
Thanks in Advance.
I have encrypted the string in php. Decrypted successfully from php and node.js. In addition, it must be decrypted by java.
Help me an example of decrypting from java?
PHP Encrypt code
/* encrypt */
$encryption_method = 'aes-256-cbc';
$secretHash = "d95acd54c6a821ff32c52825b931c194";
$iv_size = openssl_cipher_iv_length($encryption_method);
$iv = openssl_random_pseudo_bytes($iv_size);
//encrypt
$encryptedMessage = openssl_encrypt($new_token, $encryption_method, $secretHash, 0, $iv);
//Concatenate iv with data
$ciphertext = bin2hex($iv).$encryptedMessage;
/* decrypt the cipher */
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessageWithIv, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessageWithIv, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
Below is the encryption and decryption process for a string using AES algorithm.
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
In case init vector is not known, try using below code segment.
public byte[] decrypt(String encryptedString) throws DataLengthException, InvalidCipherTextException {
byte[] input = encryptedString.getBytes("UTF-8");
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(new AESEngine());
SecureRandom random = new SecureRandom();;
KeyParameter key = new KeyParameter("$secretHash".getBytes());// your key string
BlockCipherPadding blockCipherPadding = new PKCS7Padding();;
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(cbcBlockCipher, blockCipherPadding);
int blockSize = cbcBlockCipher.getBlockSize(); // Make sure this block size is same as that used while encrypting the string.
int inputOffset = 0;
int inputLength = input.length;
int outputOffset = 0;
byte[] initializationVector = new byte[blockSize];
System.arraycopy(input, 0, initializationVector, 0, blockSize);
inputOffset += blockSize;
inputLength -= blockSize;
pbbc.init(encrypt, new ParametersWithIV(key, initializationVector));
byte[] output = new byte[pbbc.getOutputSize(inputLength) + outputOffset];
int outputLength = outputOffset + pbbc.processBytes(input, inputOffset, inputLength, output, outputOffset);
outputLength += pbbc.doFinal(output, outputLength);
return Arrays.copyOf(output, outputLength);
}
Just in case it helps someone in the future: encryption with AES/CBC/PKCS5PADDING along with the generation of a dynamic IV that is appended to the final ciphertext in Java can be done through the following code:
Encryption (JAVA)
public String encryptPlainText(String plainText) {
String cipherText = "";
try {
String keyString = "examplesecretkeyexamplesecretkey";
//Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] v = new byte[16];
new SecureRandom().nextBytes(v);
IvParameterSpec iv = new IvParameterSpec(v);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, iv);
byte[] cipherTextByteArray = cipher.doFinal(plainText.getBytes());
//appending iv to ciphertext without any additional libraries to handle the concatenation of the two byte arrays
byte[] ivWithCipherTextByteArray = new byte[v.length + cipherTextByteArray.length];
System.arraycopy(v, 0, ivWithCipherTextByteArray, 0, v.length);
System.arraycopy(cipherTextByteArray, 0, ivWithCipherTextByteArray, v.length, cipherTextByteArray.length);
cipherText = new String(Base64.getEncoder().encode(ivWithCipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return cipherText;
}
Decryption of the cipherText obtained with the code above can be implemented in the following way:
Decryption (JAVA)
public static String decryptCipherText(String cipherText) {
String plainText="";
try {
String keyString = "examplesecretkeyexamplesecretkey";
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] cipherTextByteArray = Base64.getDecoder().decode(cipherText);
//initialize the IvParameterSpec with the first 16 bytes of the cipherText
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(cipherTextByteArray, 0, 16));
//cipherText to decrypt is now the original one with the first 16 bytes removed (the IV used above)
cipherTextByteArray = Arrays.copyOfRange(cipherTextByteArray, 16, cipherTextByteArray.length);
cipher.init(Cipher.DECRYPT_MODE, keyspec, iv);
plainText = new String(cipher.doFinal(cipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return plainText;
}
Hi I have java code which decrypt the ciphertext encrypted using CryptoJS library(AES).
Now i wanted to write the javacode which will encrypt the plaintext again.
Please find the below code.
try {
String secret = "René Über";
String cipherText="U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
byte[] cipherData = Base64.decode(cipherText, Base64.DEFAULT);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes("utf-8"), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData,"utf-8");
System.out.println("Decrypted "+decryptedText);
//Here I get right plain text as
//System.out: Decrypted The quick brown fox jumps over the lazy dog.
Cipher abc=Cipher.getInstance("AES/CBC/PKCS5Padding");
abc.init(Cipher.ENCRYPT_MODE,key,iv);
byte[] encryptedData=abc.doFinal(decryptedData);
String str=Base64.encodeToString(encryptedData,Base64.DEFAULT);
System.out.println("encrypted "+str);
//Here i want the encrypted text as
// encrypted U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hy//aQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=
//but i receive
//System.out: encrypted IZ5IDQruC+Cz0pd5krBsIM0KzbM+j4FeO8pgusm60wr6HFPCX+HJpAs5oPssshGjYjl/J5Ew+//eui
}catch (Exception e)
{}
When I decrypt the code I get correct Plain Text but when I again encrypt the plain text I didnt get the encrypted text as previous.
Please Help.
GenerateKeyAndIV function code:-
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte)0);
}
}
Your ciphertext has "Salted__<8 byte salt>" at the beginning, which you skip when decrypting. You need to prefix the same in your encryption mode if you want to create OpenSSL compatible ciphertext.
Your encryption code ciphertext seems correct when you view it in a base64 to hex decoder, e.g. the one provided here. However, because each character only contains 64 bits and since the bytes have shifted 16 places (which is not divisible by 3), it just seams that your entire ciphertext is incorrect, while it is just missing 16 bytes at the front.
Here posting my working code for android I have used crypto for decryption on the server. Below code is using AES Algorithm
private static final String key = "aesExamplekey";
private static final String initVector = "exampleintvec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
// byte[] finalCiphertext = new byte[encrypted.length+2*16];
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}`
Server side code asp.net
public string DecryptStringAES(string cipherText)
{
// var keybytes = Encoding.UTF8.GetBytes("7061737323313233");
// var iv = Encoding.UTF8.GetBytes("7061737323313233");
var keybytes = Encoding.UTF8.GetBytes("aesExamplekey");
var iv = Encoding.UTF8.GetBytes("exampleintvec");
var encrypted = Convert.FromBase64String(cipherText);
var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);
return string.Format(decriptedFromJavascript);
}
I am trying to decrypt a file using the following code:
Uri targURI = Uri.parse("content://xxxx/yyy.txt");
try {
InputStream content = getContentResolver().openInputStream(targURI);
BufferedReader reader1 = new BufferedReader(new InputStreamReader(content));
String line1;
String text = "";
while ((line1 = reader1.readLine()) != null) {
text+=line1;
}
Log.i("FILE ENCRYPTED", text);
String DECRYPTED = "";
DECRYPTED = decrypt(text);
Log.i("FILE DECRYPTED:", DECRYPTED);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public String decrypt(String paramString) throws Exception {
String md5_pin1 = "";
String md5_pin = MD5(md5_pin1);
SecretKeySpec keySpec = new SecretKeySpec(md5_pin.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] paramString1 = Base64.decode(paramString.getBytes(), 0);
byte[] paramstring2 = cipher.doFinal(paramString1);
String decoded = new String(paramstring2, "UTF-8");
return decoded;
}
#NonNull
public static String MD5(String paramString) throws Exception {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(paramString.getBytes());
byte messageDigest[] = digest.digest();
StringBuffer hexString = new StringBuffer();
int i=0;
while( i < messageDigest.length) {
String str = Integer.toHexString( messageDigest[i] & 0xFF );
if (str.length() == 1) {
hexString.append("0");
}
hexString.append(str);
i += 1;
}
return hexString.toString();
}
as it is showed the file is accessed using a content provider, and stored in a String variable, and actually the correct string value is stored (encrypted data).
The way to decrypt it is to get a seed (empty space in this case), and then use MD5 digest, then use that value to encrypt/decrypt the cleartext.
However whenever the code reaches: String decoded = new String(paramstring2, "UTF-8"); the error message: pad block corrupted is thrown.
Any ideas what I am doing wrong?
Avoid using default padding for cipher as it may be different on different environment.
Try the following code:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Also use the same padding for encrypting the file.
You can use the following methods:
private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParams);
return cipher.doFinal(data);
}
private static byte[] decrypt(byte[] encrypted, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ivByte = new byte[cipher.getBlockSize()];
IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivParamsSpec);
return cipher.doFinal(encrypted);
}
This was not a problem related to coding.
Please disregard this question
I have a hardcoded key with which I want to encrypt a string before storing it in SharedPreferences. This is the code I have so far:
public class TokenEncryptor {
private final static String TOKEN_KEY = "91a29fa7w46d8x41";
public static String encrypt(String plain) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(plain.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
SecretKeySpec newKey = new SecretKeySpec(TOKEN_KEY.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return new String(cipher.doFinal(encoded.getBytes()));
} catch (Exception e) {
Ln.e(e);
return null;
}
}
}
It seems to be catching an exception at the end of decrypt method:
javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
Can someone point me in the right direction? I have a feeling I'm doing something wrong instantiating IvParameterSpec.
When you encrypt a string with AES, you get an array of bytes back. Trying to convert those bytes directly to a string (new String(cipher.doFinal(plaintextBytes))) will cause all sorts of problems. If you require the output from your encryption method to be a string, then use Base64 rather than attempting a direct conversion. In your decryption method, convert the Base64 string back into a byte array before decrypting the byte array.
Also, do not use getBytes() since the output depends on the system defaults. Use getBytes("utf-8") or whatever. That eliminates ambiguity.
Just in case anyone is interested (or feels too lazy to do their research), here is the the result code for AES-256 encryption and decryption that I put together, with help from the accepted answer and comments:
public class TokenEncryptor {
private final static String TOKEN_KEY = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw";
public static String encrypt(String plain) {
try {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(plain.getBytes("utf-8"));
byte[] ivAndCipherText = getCombinedArray(iv, cipherText);
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP);
} catch (Exception e) {
Ln.e(e);
return null;
}
}
public static String decrypt(String encoded) {
try {
byte[] ivAndCipherText = Base64.decode(encoded, Base64.NO_WRAP);
byte[] iv = Arrays.copyOfRange(ivAndCipherText, 0, 16);
byte[] cipherText = Arrays.copyOfRange(ivAndCipherText, 16, ivAndCipherText.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(TOKEN_KEY.getBytes("utf-8"), "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), "utf-8");
} catch (Exception e) {
Ln.e(e);
return null;
}
}
private static byte[] getCombinedArray(byte[] one, byte[] two) {
byte[] combined = new byte[one.length + two.length];
for (int i = 0; i < combined.length; ++i) {
combined[i] = i < one.length ? one[i] : two[i - one.length];
}
return combined;
}
}
It's an extension of Artjom B answer and working for me.
public String encryptMsg(String message, SecretKey secret)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes("UTF-8"));
return Base64.encodeToString(cipherText, Base64.NO_WRAP);
}
public String decryptMsg(String cipherText, SecretKey secret)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
byte[] decode = Base64.decode(cipherText, Base64.NO_WRAP);
String decryptString = new String(cipher.doFinal(decode), "UTF-8");
return decryptString;
}
Kotlin version of #Oleksiy 's answer.
<script src="https://gist.github.com/kasim1011/a5a9644a60c33a4df3c29f4b34cf93a4.js"></script>
import android.util.Base64
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
private const val algorithm = "AES"
private const val tokenKey = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw"
private const val padding = "AES/CBC/PKCS5Padding"
private const val ivSize = 16
fun String.encryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivByteArray = ByteArray(ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)
val cipherText = cipher.doFinal(toByteArray(Charsets.UTF_8))
val ivAndCipherText = getCombinedArray(ivByteArray, cipherText)
return Base64.encodeToString(ivAndCipherText, Base64.NO_WRAP)
}
fun String.decryptAES(): String {
val tokenBytes = tokenKey.toByteArray(Charsets.UTF_8)
val secretKey = SecretKeySpec(tokenBytes, algorithm)
val ivAndCipherText = Base64.decode(this, Base64.NO_WRAP)
val cipherText = Arrays.copyOfRange(ivAndCipherText, ivSize, ivAndCipherText.size)
val ivByteArray = Arrays.copyOfRange(ivAndCipherText, 0, ivSize)
val iv = IvParameterSpec(ivByteArray)
val cipher = Cipher.getInstance(padding)
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv)
return cipher.doFinal(cipherText).toString(Charsets.UTF_8)
}
private fun getCombinedArray(one: ByteArray, two: ByteArray): ByteArray {
val combined = ByteArray(one.size + two.size)
for (i in combined.indices) {
combined[i] = if (i < one.size) one[i] else two[i - one.size]
}
return combined
}