MarcForn
MarcForn

Reputation: 3409

Encrypt/Decrypt String Kotlin

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

Answers (6)

H.Fa8
H.Fa8

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

Bolt UIX
Bolt UIX

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

Irving Kennedy
Irving Kennedy

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

Kasım &#214;zdemir
Kasım &#214;zdemir

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

MarcForn
MarcForn

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

Maarten Bodewes
Maarten Bodewes

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:

  • don't use SecureRandom to derive a key, try and lookup PBKDF2;
  • explicitly use a mode of operation such as "AES/GCM/NoPadding";
  • use a unique IV, and a random IV if you decide to use CBC (usually insecure);
  • don't use toByteArray without explicitly selecting a character encoding for the message.

Upvotes: 7

Related Questions