AES解密报javax.crypto.BadPaddingException错误求助
问题分析与解决方案
问题根源
你遇到的javax.crypto.BadPaddingException,核心原因是代码中使用了静态的Cipher实例,而Cipher类本身并非线程安全。
加密程序是单线程调用加密+解密验证,不会触发线程安全问题;但如果解密程序是多线程环境(比如Web应用、多线程任务),多个线程共用同一个静态Cipher实例时,会导致init()和doFinal()操作交叉执行,破坏Cipher的内部状态,最终解密出错误的字节数据,引发填充验证失败。
修复方案
方案1:每次加密/解密创建新的Cipher实例
去掉静态的aes变量,在加密和解密方法内直接创建Cipher实例,确保每个操作都使用独立的实例,从根本上避免线程安全问题:
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Base64; public class EncryptionUtility { private static byte[] key_data = { (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f, (byte) 0x10 }; private static byte[] iv_data = { (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f, (byte) 0x10 }; private EncryptionUtility() { } public static String encrypt(String target) throws GeneralSecurityException { Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey key = new SecretKeySpec(key_data, "AES"); IvParameterSpec iv = new IvParameterSpec(iv_data); aes.init(Cipher.ENCRYPT_MODE, key, iv); return base64Encode(aes.doFinal(target.getBytes(StandardCharsets.UTF_8))); } private static String base64Encode(byte[] encrypted) { return Base64.getEncoder().encodeToString(encrypted); } public static String decrypt(String encrypted) throws GeneralSecurityException, IOException { Cipher aes = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKey key = new SecretKeySpec(key_data, "AES"); IvParameterSpec iv = new IvParameterSpec(iv_data); aes.init(Cipher.DECRYPT_MODE, key, iv); return new String(aes.doFinal(base64Decode(encrypted)), StandardCharsets.UTF_8); } private static byte[] base64Decode(String property) throws IOException { return Base64.getDecoder().decode(property); } }
方案2:使用ThreadLocal存储线程专属的Cipher实例
如果担心频繁创建Cipher实例的性能开销,可以用ThreadLocal为每个线程分配独立的Cipher实例,既保证线程安全,又减少实例创建次数:
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Base64; public class EncryptionUtility { private static byte[] key_data = { (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f, (byte) 0x10 }; private static byte[] iv_data = { (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f, (byte) 0x10 }; private static final ThreadLocal<Cipher> AES_CIPHER = ThreadLocal.withInitial(() -> { try { return Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw new RuntimeException("Failed to initialize AES cipher", e); } }); private EncryptionUtility() { } public static String encrypt(String target) throws GeneralSecurityException { Cipher aes = AES_CIPHER.get(); SecretKey key = new SecretKeySpec(key_data, "AES"); IvParameterSpec iv = new IvParameterSpec(iv_data); aes.init(Cipher.ENCRYPT_MODE, key, iv); return base64Encode(aes.doFinal(target.getBytes(StandardCharsets.UTF_8))); } private static String base64Encode(byte[] encrypted) { return Base64.getEncoder().encodeToString(encrypted); } public static String decrypt(String encrypted) throws GeneralSecurityException, IOException { Cipher aes = AES_CIPHER.get(); SecretKey key = new SecretKeySpec(key_data, "AES"); IvParameterSpec iv = new IvParameterSpec(iv_data); aes.init(Cipher.DECRYPT_MODE, key, iv); return new String(aes.doFinal(base64Decode(encrypted)), StandardCharsets.UTF_8); } private static byte[] base64Decode(String property) throws IOException { return Base64.getDecoder().decode(property); } }
额外排查点
- 确保待解密字符串无隐藏字符:比如前后空格、换行符,可先调用
encrypted.trim()再尝试解密(注意Base64字符串本身不应包含这些字符)。 - 统一算法名称大小写:将
SecretKeySpec构造函数中的"aes"改为"AES",避免部分JCE提供者对大小写敏感的问题。
内容的提问来源于stack exchange,提问作者Bill
相关产品推荐
相关产品推荐

