尝试使用 AES 加密和解密字符串时出现 IllegalBlockSizeException
Posted
技术标签:
【中文标题】尝试使用 AES 加密和解密字符串时出现 IllegalBlockSizeException【英文标题】:IllegalBlockSizeException when trying to encrypt and decrypt a string with AES 【发布时间】:2015-08-03 17:04:21 【问题描述】:我有一个硬编码的密钥,我想在将字符串存储到SharedPreferences
之前对其进行加密。这是我到目前为止的代码:
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;
它似乎在decrypt
方法的末尾捕获了一个异常:
javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
有人能指出我正确的方向吗?我感觉我在实例化 IvParameterSpec
时做错了什么。
【问题讨论】:
您的密钥只有 16 个字节长,所以您说的是 AES-128 而不是 AES-256。 @ArtjomB。我应该将其更改为 32 字节以使其成为 AES-256 吗?很抱歉我对此一无所知,这是我第一次处理加密问题 是的,如果您将密钥大小增加到 24 或 32 字节,将自动使用 AES-192 或 AES-256。 @ArtjomB.,我现在正与java.security.InvalidAlgorithmParameterException: expected IV length of 16
崩溃。我对此进行了研究,看来我必须下载 jar 并将它们作为库导入? ***.com/a/6729947/1367392 没有那个就没有办法用 AES-256 加密吗?谢谢
IV 的大小必须与块长度相同,对于 AES(-128、-192 和 -256)为 16 字节。
【参考方案1】:
当您使用 AES 加密字符串时,您会返回一个字节数组。尝试将这些字节直接转换为字符串 (new String(cipher.doFinal(plaintextBytes))
) 会导致各种问题。如果您要求加密方法的输出为字符串,则使用Base64
而不是尝试直接转换。在您的解密方法中,在解密字节数组之前将Base64
字符串转换回字节数组。
另外,不要使用getBytes()
,因为输出取决于系统默认值。使用getBytes("utf-8")
或其他。这消除了歧义。
【讨论】:
Q: Base64 encoder and decoder 为我工作!谢谢!【参考方案2】:以防万一有人感兴趣(或懒得做研究),这是我在接受的答案和 cmets 的帮助下汇总的AES-256
加密和解密的结果代码:
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;
【讨论】:
对不起,为什么我们有一行“new SecureRandom().nextBytes(iv);” ?没有使用返回值吧? @zkvarz: 方法操作分配的字节数组 --> 没有返回值 @Oleksiy 谢谢,它救了我的命,这是你答案的 kotlin 版本gist.github.com/kasim1011/a5a9644a60c33a4df3c29f4b34cf93a4 我正在使用 SecureRandom 生成的 SecretKey,大小 128,encodedToString(),然后使用 Base64.NO_WRAP 解码,Cypher 使用 AES/CBC/PKCS5Padding,IvParameterSpec 长度 16 和 SecureRandom.getInstance(" SHA1PRNG") ...,我找不到任何关于出了什么问题的答案。【参考方案3】:它是 Artjom B 答案的扩展,为我工作。
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;
【讨论】:
你刚刚救了我的命!【参考方案4】:@Oleksiy 答案的 Kotlin 版本。
<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
【讨论】:
以上是关于尝试使用 AES 加密和解密字符串时出现 IllegalBlockSizeException的主要内容,如果未能解决你的问题,请参考以下文章