1. 引言
Java中的Cipher
类是提供加密解密功能的核心类。本文将探讨使用该类时可能遇到的常见异常,并提供解决方案。
2. NoSuchAlgorithmException:找不到支持X算法的提供者
当使用虚构算法获取Cipher
实例时:
Cipher.getInstance("ABC");
会抛出如下异常:
java.security.NoSuchAlgorithmException: Cannot find any provider supporting ABC
at javax.crypto.Cipher.getInstance(Cipher.java:543)
核心原因
- ✅ 算法名称无效:传入的算法转换字符串必须符合官方文档的规范
- ✅ 拼写错误:即使查阅文档后仍报错,需检查算法名称拼写
- ✅ 模式/填充参数错误:可指定算法模式和填充方式,但需确保组合有效
有效与无效示例对比
Cipher.getInstance("AES/ABC"); // ❌ 无效模式
Cipher.getInstance("AES/CBC/ABC"); // ❌ 无效填充
Cipher.getInstance("AES/CBC/PKCS5Padding"); // ✅ 有效组合
默认值警告
- 算法模式默认为ECB(存在安全风险)
- 填充方式默认为NoPadding
- ⚠️ 生产环境应显式指定安全模式(如CBC)
3. IllegalBlockSizeException:输入长度不是X字节的倍数
3.1 解密时的异常
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherTextBytes);
当密文长度不符合要求时:
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1109)
根本原因
- AES是分组密码,固定处理16字节块
- ❌ 最后一个分组长度不足16字节
- ⚠️ 通常表明原始数据加密过程有误
处理建议
- 必须捕获此异常(编译器强制要求)
- 注意:约1/16的错误密文会因巧合长度而绕过此异常
3.2 加密时的异常
String plainText = "https://www.baeldung.com/";
byte[] plainTextBytes = plainText.getBytes();
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainTextBytes);
当明文长度不是16字节倍数时抛出相同异常。
解决方案
- ✅ 启用填充:使用
PKCS5Padding
等填充方案Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // ✅ 启用填充
- ⚠️ 加解密必须使用相同填充方式
3.3 其他排查技巧
- 检查Java文档确认填充方案有效性
- 验证Java版本与文档匹配(不同版本支持方案可能不同)
4. BadPaddingException:最终块填充不正确
4.1 填充方式不匹配
// 加密使用ISO10126Padding
Cipher cipher = Cipher.getInstance("AES/ECB/ISO10126Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherTextBytes = cipher.doFinal(plainTextBytes);
// 解密使用PKCS5Padding
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, encryptionKey);
return cipher.doFinal(cipherTextBytes); // ❌ 抛出异常
异常信息:
javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
4.2 密钥不匹配(最常见原因)
SecretKey encryptionKey = CryptoUtils.getKeyForText("BaeldungIsASuperCoolSite");
SecretKey differentKey = CryptoUtils.getKeyForText("ThisGivesUsAnAlternative");
// 加密
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
byte[] cipherTextBytes = cipher.doFinal(plainTextBytes);
// 解密使用不同密钥
cipher.init(Cipher.DECRYPT_MODE, differentKey);
return cipher.doFinal(cipherTextBytes); // ❌ 抛出异常
4.3 算法/模式不匹配
// 加密使用CBC模式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherTextBytes = cipher.doFinal(plainTextBytes);
// 解密使用ECB模式
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherTextBytes); // ❌ 抛出异常
统一排查思路
✅ 确保解密时使用的:
- 密钥与加密时完全相同
- 算法名称/模式/填充方式完全匹配
5. InvalidKeyException
5.1 参数缺失(缺少IV)
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, encryptionKey); // ❌ 缺少IV参数
cipher.doFinal(cipherTextBytes);
异常信息:
java.security.InvalidKeyException: Parameters missing
解决方案
- ✅ 添加初始化向量(IV): ```java byte[] ivBytes = new byte[]{'B', 'a', 'e', 'l', 'd', 'u', 'n', 'g', 'I', 's', 'G', 'r', 'e', 'a', 't', '!'}; IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, encryptionKey, ivParameterSpec); // ✅ 添加IV
- ⚠️ IV长度必须为16字节(AES要求)
- ⚠️ 加解密必须使用相同IV
### 5.2 密钥长度无效
```java
java.security.InvalidKeyException: Invalid AES key length: X bytes
解决方案
- ✅ 确保密钥长度为16字节(128位)
- ⚠️ Java默认仅支持128位加密
6. 总结
本文系统分析了Java加密API中的常见异常:
- NoSuchAlgorithmException:算法名称/模式/填充无效
- IllegalBlockSizeException:数据长度不符合分组要求
- BadPaddingException:通常由密钥/算法不匹配引起
- InvalidKeyException:密钥长度错误或缺少必要参数
关键排查原则:
- ✅ 严格匹配加解密参数(密钥/算法/模式/填充/IV)
- ⚠️ 异常信息可能具有误导性(如填充问题实际是密钥错误)
- 📖 始终参考官方文档验证参数有效性
完整示例代码可在GitHub项目中获取。