Reputation: 3409
I've created this two extensions in Kotlin to Encrypt/Decrypt strings:
fun String.encrypt(seed : String): String {
val keyGenerator = KeyGenerator.getInstance("AES")
val secureRandom = SecureRandom.getInstance("SHA1PRNG")
secureRandom.setSeed(seed.toByteArray())
keyGenerator.init(128, secureRandom)
val skey = keyGenerator.generateKey()
val rawKey : ByteArray = skey.encoded
val skeySpec = SecretKeySpec(rawKey, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, skeySpec)
val byteArray = cipher.doFinal(this.toByteArray())
return byteArray.toString()
}
fun String.decrypt(seed : String): String {
val keyGenerator = KeyGenerator.getInstance("AES")
val secureRandom = SecureRandom.getInstance("SHA1PRNG")
secureRandom.setSeed(seed.toByteArray())
keyGenerator.init(128, secureRandom)
val skey = keyGenerator.generateKey()
val rawKey : ByteArray = skey.encoded
val skeySpec = SecretKeySpec(rawKey, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, skeySpec)
val byteArray = cipher.doFinal(this.toByteArray())
return byteArray.toString()
}
for some reason I'm getting the following exception:
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
What I'm doing wrong?
Upvotes: 8
Views: 43063
Reputation: 318
You can Encrypt and Decrypt in Kotlin like this
first you need:
val SECRET_KEY = "secretKey"
val SECRET_IV = "secretIV"
after that
Encrypt:
private fun String.encryptCBC(): String {
val iv = IvParameterSpec(SECRET_IV.toByteArray())
val keySpec = SecretKeySpec(SECRET_KEY.toByteArray(), "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv)
val crypted = cipher.doFinal(this.toByteArray())
val encodedByte = Base64.encode(crypted, Base64.DEFAULT)
return String(encodedByte)
}
and Decrypt:
private fun String.decryptCBC(): String {
val decodedByte: ByteArray = Base64.decode(this, Base64.DEFAULT)
val iv = IvParameterSpec(SECRET_IV.toByteArray())
val keySpec = SecretKeySpec(SECRET_KEY.toByteArray(), "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv)
val output = cipher.doFinal(decodedByte)
return String(output)
}
after that you can use it like this:
val strEncrypt = edt.text.toString().encryptCBC()
val strDecrypted = strEncrypt?.decryptCBC()
Upvotes: 3
Reputation: 7022
The easiest way of implementing AES Encryption and Decryption in Android is to copy this class in your projects.
Encrypt Strings
Please copy the AESUtils class in your project first and then you can use it like this.
String encrypted = "";
String sourceStr = "This is any source string";
try {
encrypted = AESUtils.encrypt(sourceStr);
Log.d("TEST", "encrypted:" + encrypted);
} catch (Exception e) {
e.printStackTrace();
}
Decrypt Strings
String encrypted = "ANY_ENCRYPTED_STRING_HERE";
String decrypted = "";
try {
decrypted = AESUtils.decrypt(encrypted);
Log.d("TEST", "decrypted:" + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
AESUtils Class
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AESUtils
{
private static final byte[] keyValue =
new byte[]{'c', 'o', 'd', 'i', 'n', 'g', 'a', 'f', 'f', 'a', 'i', 'r', 's', 'c', 'o', 'm'};
public static String encrypt(String cleartext)
throws Exception {
byte[] rawKey = getRawKey();
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String encrypted)
throws Exception {
byte[] enc = toByte(encrypted);
byte[] result = decrypt(enc);
return new String(result);
}
private static byte[] getRawKey() throws Exception {
SecretKey key = new SecretKeySpec(keyValue, "AES");
byte[] raw = key.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKey skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] encrypted)
throws Exception {
SecretKey skeySpec = new SecretKeySpec(keyValue, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
Upvotes: 4
Reputation: 29
Following MarcForn guide I reduce it like this this:
const val encryptionKey = "ENCRYPTION_KEY"
fun String.cipherEncrypt(encryptionKey: String): String? {
try {
val secretKeySpec = SecretKeySpec(encryptionKey.toByteArray(), "AES")
val iv = encryptionKey.toByteArray()
val ivParameterSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
val encryptedValue = cipher.doFinal(this.toByteArray())
return Base64.encodeToString(encryptedValue, Base64.DEFAULT)
} catch (e: Exception) {
e.message?.let{ Log.e("encryptor", it) }
}
return null
}
fun String.cipherDecrypt(encryptionKey: String): String? {
try {
val secretKeySpec = SecretKeySpec(encryptionKey.toByteArray(), "AES")
val iv = encryptionKey.toByteArray()
val ivParameterSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
val decodedValue = Base64.decode(this, Base64.DEFAULT)
val decryptedValue = cipher.doFinal(decodedValue)
return String(decryptedValue)
} catch (e: Exception) {
e.message?.let{ Log.e("decryptor", it) }
}
return null
}
Upvotes: 0
Reputation: 5644
AES Encryption / Decryption using base64 key, salt and iv (Initialization Vector).
object AESEncyption {
const val secretKey = "tK5UTui+DPh8lIlBxya5XVsmeDCoUl6vHhdIESMB6sQ="
const val salt = "QWlGNHNhMTJTQWZ2bGhpV3U=" // base64 decode => AiF4sa12SAfvlhiWu
const val iv = "bVQzNFNhRkQ1Njc4UUFaWA==" // base64 decode => mT34SaFD5678QAZX
fun encrypt(strToEncrypt: String) : String?
{
try
{
val ivParameterSpec = IvParameterSpec(Base64.decode(iv, Base64.DEFAULT))
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec = PBEKeySpec(secretKey.toCharArray(), Base64.decode(salt, Base64.DEFAULT), 10000, 256)
val tmp = factory.generateSecret(spec)
val secretKey = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
return Base64.encodeToString(cipher.doFinal(strToEncrypt.toByteArray(Charsets.UTF_8)), Base64.DEFAULT)
}
catch (e: Exception)
{
println("Error while encrypting: $e")
}
return null
}
fun decrypt(strToDecrypt : String) : String? {
try
{
val ivParameterSpec = IvParameterSpec(Base64.decode(iv, Base64.DEFAULT))
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec = PBEKeySpec(secretKey.toCharArray(), Base64.decode(salt, Base64.DEFAULT), 10000, 256)
val tmp = factory.generateSecret(spec);
val secretKey = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
return String(cipher.doFinal(Base64.decode(strToDecrypt, Base64.DEFAULT)))
}
catch (e : Exception) {
println("Error while decrypting: $e");
}
return null
}
}
iOS swift
Upvotes: 20
Reputation: 3409
Following Maarten Bodews guides I fix the issues as:
fun String.encrypt(password: String): String {
val secretKeySpec = SecretKeySpec(password.toByteArray(), "AES")
val iv = ByteArray(16)
val charArray = password.toCharArray()
for (i in 0 until charArray.size){
iv[i] = charArray[i].toByte()
}
val ivParameterSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
val encryptedValue = cipher.doFinal(this.toByteArray())
return Base64.encodeToString(encryptedValue, Base64.DEFAULT)
}
fun String.decrypt(password: String): String {
val secretKeySpec = SecretKeySpec(password.toByteArray(), "AES")
val iv = ByteArray(16)
val charArray = password.toCharArray()
for (i in 0 until charArray.size){
iv[i] = charArray[i].toByte()
}
val ivParameterSpec = IvParameterSpec(iv)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
val decryptedByteValue = cipher.doFinal(Base64.decode(this, Base64.DEFAULT))
return String(decryptedByteValue)
}
Upvotes: 9
Reputation: 94018
To encode your ciphertext use base 64 or hexadecimals. The Java API contains a Base64 class, so you're probably best off using that.
byte[]#toString
doesn't do what you expect it to do; it simply returns a representation of the byte array reference, not the contents of the byte array.
Besides that:
"AES/GCM/NoPadding"
;toByteArray
without explicitly selecting a character encoding for the message.Upvotes: 7