1

I want to encrypt and decrypt plaintext to AES algorithm. I got key and iv comes from API. I tried a lot of things but didn't work. What is wrong?

https://github.com/simbiose/Encryption

https://github.com/scottyab/AESCrypt-Android

public class CryptoHandler {

    private static CryptoHandler instance = null;

    public static CryptoHandler getInstance() {

        if (instance == null) {
            instance = new CryptoHandler();
        }
        return instance;
    }

    public String encrypt(String message, String key, String IV) throws NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException,
            BadPaddingException, InvalidKeyException,
            UnsupportedEncodingException, InvalidAlgorithmParameterException {

        byte[] srcBuff = message.getBytes("UTF8");
        //here using substring because AES takes only 16 or 24 or 32 byte of key
        SecretKeySpec skeySpec = new
                SecretKeySpec(key.substring(0,32).getBytes(), "AES");
        IvParameterSpec ivSpec = new
                IvParameterSpec(IV.substring(0,16).getBytes());
        Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
        byte[] dstBuff = ecipher.doFinal(srcBuff);
        String base64 = Base64.encodeToString(dstBuff, Base64.DEFAULT);
        return base64;
    }

    public String decrypt(String encrypted, String key, String IV) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException,
            InvalidAlgorithmParameterException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException {

        SecretKeySpec skeySpec = new
                SecretKeySpec(key.substring(0,32).getBytes(), "AES");
        IvParameterSpec ivSpec = new
                IvParameterSpec(IV.substring(0,16).getBytes());
        Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        ecipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
        byte[] raw = Base64.decode(encrypted.getBytes(), 0, 16, Base64.DEFAULT);
        byte[] originalBytes = ecipher.doFinal(raw);
        String original = new String(originalBytes, "UTF8");
        return original;
    }

}

Example API response data:

{
  "key": "QaDtfPpeMW0VgMMd4XF88K6KkIPe5ZG0sitpyhuJf/E=",
  "iv": "ccp2YePjewVL9X+vCms5BQ==",
  "string": "5c2c82a6-66da-41f9-b20d-5d4ffd0c505a",
}
6
  • What encryption method the API uses, what encoding it employs? Commented Apr 10, 2021 at 10:55
  • It comes with base64 code for key and iv. It is in Key and IV size AES256. Encryption and decryption is performed with ECB/CBC block encryption in PKCS7 padding type. Commented Apr 10, 2021 at 10:57
  • Well, ECB and CBC are different modes of operations, which one?. Once can distinguish ECB from CBC with the lack of IV. The standard IV size is always 128-bit for AES - that is the block size-. Commented Apr 10, 2021 at 11:19
  • I don't know. I have a documentation and it's writing you should use key and iv to comes from API to encrypt string with use key and iv with AES algorithm. Commented Apr 10, 2021 at 11:38
  • That key and IV look base 64 encoded, and I don't see you decode anything. You may have to actually learn the stuff instead of just trying things. Commented Apr 10, 2021 at 12:55

3 Answers 3

2

Yunus, I faced with same problem, late but if someone other faces again, I'm sharing my solution. Key and IVkey already converted to base64 and ready to decode, so no need substr process. When you make substr you lose part of whole key.

for keys below I read from shared prefs.

    const val AES_KEY = "AES_KEY"
    const val AES_IV = "AES_IV"

sharedprefs returns keys as same as your keys (as encoded to base64 and as a string -not bytearray-)

{
  "key": "QaDtfPpeMW0VgMMd4XF88K6KkIPe5ZG0sitpyhuJf/E=",
  "iv": "ccp2YePjewVL9X+vCms5BQ==",
  "string": "5c2c82a6-66da-41f9-b20d-5d4ffd0c505a",
}

then just encode:

fun encrypt(message: String): String? {
    try {
        val srcBuff = message.toByteArray(charset("UTF8"))
        val skeySpec = SecretKeySpec(Base64.decode(pref.getString(Constraints.AES_KEY, null)!!, Base64.DEFAULT), "AES")
        val ivSpec = IvParameterSpec(Base64.decode(pref.getString(Constraints.AES_IV, null)!!, Base64.DEFAULT))
        val ecipher: Cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
        ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec)
        val dstBuff: ByteArray = ecipher.doFinal(srcBuff)
        return Base64.encodeToString(dstBuff, Base64.DEFAULT)
    } catch (ex: Exception) {
        context.longToast(ex.localizedMessage)
    }
    return null
}

or decode:

fun decrypt(encrypted: String): String? {
        try {
            val skeySpec = SecretKeySpec(Base64.decode(pref.getString(Constraints.AES_KEY, null)!!, Base64.DEFAULT), "AES")
            val ivSpec = IvParameterSpec(Base64.decode(pref.getString(Constraints.AES_IV, null)!!, Base64.DEFAULT))
            val ecipher: Cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
            ecipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec)
            val raw: ByteArray = Base64.decode(encrypted, Base64.DEFAULT)
            val originalBytes: ByteArray = ecipher.doFinal(raw)
            return String(originalBytes, Charset.forName("UTF-8"))
        } catch (ex: Exception) {
            context.longToast(ex.localizedMessage)
        }
        return null
    }
Sign up to request clarification or add additional context in comments.

1 Comment

i have a vulnerability problem with sonarqube for i use "AES/CBC/PKCS7Padding"
1

I don't see much wrong, other than the fact that it is incorrectly stringified (keys and IV's should contain random byte values, including those that are not part of the printable ASCII character set).

However, the most likely culprit is this line:

 byte[] raw = Base64.decode(encrypted.getBytes(), 0, 16, Base64.DEFAULT);

As PKCS#7 always unpads, using a single block may mean that unpadding fails if it is present in one of the following blocks instead. Just decode the entire base 64, not just 16 characters.

There is absolutely no reason whatsoever why CryptoHandler should have only one instance. Your current getInstace method is not thread safe either, so you may turn up with multiple handlers anyway. Just use normal classes, and think about what should be included in them.

This is mainly a so called "wrapper class". It does nothing useful. I would recommend writing cryptography related classes that are specific to a particular use case. Trust me when I say that you may end up rewriting everything later if you don't - I've been there.

9 Comments

How can i fix this?
Updated answer, just decode the entire base 64 encoded ciphertext.
I got error "javax.crypto.IllegalBlockSizeException: error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH" on line "byte[] originalBytes = ecipher.doFinal(raw);"
Remove .getBytes() and use the String version of decode just to be sure. Compare the base 64 output of encrypt with that of decrypt.
Same error i tried that byte[] raw = Base64.decode(encrypted, Base64.DEFAULT); byte[] raw = encrypted.getBytes("UTF8");
|
0

No need of any third party library for this.

private fun get16CharIv(): String {
    return UUID.randomUUID().toString().replace("-", "").substring(0, 16)
}

fun encryptTextWith16CharIv(textToEncrypt: String, key: String): String? {
    return try {
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        val secretKeyByte = key.toByteArray(Charsets.UTF_8)
        val keySpec = SecretKeySpec(secretKeyByte, "AES")
        val ivSpec = IvParameterSpec(get16CharIv().toByteArray(Charsets.UTF_8))
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
        val encoder = BASE64Encoder()
        encoder.encode(cipher.doFinal(textToEncrypt.toByteArray(Charsets.UTF_8)))
    } catch (e: Exception) {
        null
    }
}

fun decryptTextWith16CharIv(encodedTextToDecrypt: String, key: String): String? {
    return try {
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        val secretKeyByte = key.toByteArray(Charsets.UTF_8)
        val keySpec = SecretKeySpec(secretKeyByte, "AES")
        val ivSpec = IvParameterSpec(get16CharIv().toByteArray(Charsets.UTF_8))
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
        val decodedText = Base64.decode(
            encodedTextToDecrypt.toByteArray(Charsets.UTF_8), Base64.DEFAULT
        )
        String(cipher.doFinal(decodedText))
    } catch (e: Exception) {
        null
    }
}

//Another way to get IV
fun getIv(): ByteArray {
    if (IV == null) {
        val IV = ByteArray(32)
        val random: SecureRandom = SecureRandom()
        random.nextBytes(IV)
    }
    return IV!!
}

There is one more padding format called No padding. For that we have to use "AES/CBC/NoPadding"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.