Java项目KeyGenerator不可用求助:AES算法配置报错
AES密钥生成与加密报错问题排查与解决
问题概述
开发小型项目时遇到KeyGenerator相关问题,尝试多种算法和密钥长度均未解决,具体报错:
- 仅配置算法为
AES时,提示:ECB Mode can not use IV - 配置算法为
AES/CBC/PKCS5PADDING时,提示:KeyGenerator not available
项目原代码
@SpringBootApplication public class SslServerApplication { private static final String ALGORITHM = "AES/CBC/PKCS5PADDING"; private static final int KEY_SIZE = 192; public static void main(String[] args) throws Exception { SecretKey key = generateKey(); String message = "Hello, Professor Conlan!"; byte[] encrypted = encrypt(message, key); byte[] hash = hash(encrypted); System.out.println("Hash: " + bytesToHex(hash)); boolean verified = verifyChecksum(hash, encrypted, key); System.out.println("Checksum verified: " + verified); } //generates secret key private static SecretKey generateKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM); keyGenerator.init(KEY_SIZE); return keyGenerator.generateKey(); } //encrypts secret key key static byte[] encrypt(String message, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] iv = new byte[cipher.getBlockSize()]; SecureRandom random = new SecureRandom(); random.nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); return cipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); } static byte[] hash(byte[] message) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); return md.digest(message); } private static boolean verifyChecksum(byte[] checksum, byte[] message, SecretKey key) throws Exception { byte[] hash = hash(decrypt(message, key)); return MessageDigest.isEqual(hash, checksum); } //decrypts the encrypted secret key static byte[] decrypt(byte[] message, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] iv = Arrays.copyOfRange(message, 0, cipher.getBlockSize()); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); return cipher.doFinal(Arrays.copyOfRange(message, cipher.getBlockSize(), message.length)); } static String bytesToHex(byte[] bytes) { StringBuilder result = new StringBuilder(); for (byte b : bytes) { result.append(String.format("%02x", b)); } return result.toString(); } }
项目POM文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.snhu</groupId> <artifactId>ssl-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ssl-server</name> <description>ssl-server skeleton for CS-305</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>5.3.0</version> <configuration> <outputDirectory default-value="C:\Users\Shawn\Documents\M7 Folder"/> </configuration> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
问题原因与修复方案
1. KeyGenerator初始化错误
KeyGenerator.getInstance()仅需传入基础算法名称(如AES),不能包含模式和填充方式。原代码传入AES/CBC/PKCS5PADDING,导致找不到对应密钥生成器,这是第二个报错的直接原因。
2. ECB模式不支持IV的问题
仅指定AES时,Java默认采用ECB模式,该模式无需IV向量,但加密代码中传入了IvParameterSpec,触发第一个报错。必须使用CBC/GCM等安全模式,并正确处理IV。
3. 加密逻辑的隐藏问题
原加密方法生成IV后未将其与密文拼接返回,解密时却尝试从密文开头截取IV,导致解密失败。需将IV放在密文头部一起输出。
修复后的完整代码
@SpringBootApplication public class SslServerApplication { private static final String ALGORITHM = "AES/CBC/PKCS5PADDING"; private static final int KEY_SIZE = 192; public static void main(String[] args) throws Exception { SecretKey key = generateKey(); String message = "Hello, Professor Conlan!"; byte[] encrypted = encrypt(message, key); // 仅对密文部分哈希,排除IV byte[] hash = hash(Arrays.copyOfRange(encrypted, 16, encrypted.length)); System.out.println("Hash: " + bytesToHex(hash)); boolean verified = verifyChecksum(hash, encrypted, key); System.out.println("Checksum verified: " + verified); } // 生成密钥:仅传入基础算法名称"AES" private static SecretKey generateKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(KEY_SIZE); return keyGenerator.generateKey(); } // 加密:将IV与密文拼接后返回 static byte[] encrypt(String message, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] iv = new byte[cipher.getBlockSize()]; SecureRandom random = new SecureRandom(); random.nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); byte[] cipherText = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); // IV在前,密文在后,组合成最终加密数据 byte[] result = new byte[iv.length + cipherText.length]; System.arraycopy(iv, 0, result, 0, iv.length); System.arraycopy(cipherText, 0, result, iv.length, cipherText.length); return result; } static byte[] hash(byte[] message) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); return md.digest(message); } private static boolean verifyChecksum(byte[] checksum, byte[] encryptedData, SecretKey key) throws Exception { byte[] decrypted = decrypt(encryptedData, key); byte[] hash = hash(decrypted); return MessageDigest.isEqual(hash, checksum); } // 解密:从加密数据头部提取IV static byte[] decrypt(byte[] encryptedData, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); int blockSize = cipher.getBlockSize(); byte[] iv = Arrays.copyOfRange(encryptedData, 0, blockSize); byte[] cipherText = Arrays.copyOfRange(encryptedData, blockSize, encryptedData.length); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); return cipher.doFinal(cipherText); } static String bytesToHex(byte[] bytes) { StringBuilder result = new StringBuilder(); for (byte b : bytes) { result.append(String.format("%02x", b)); } return result.toString(); } }
额外注意事项
- Java 8默认仅支持128位AES密钥,使用192/256位密钥需安装JCE无限制权限策略文件,否则会抛出密钥长度不支持异常。
- 优先推荐使用AES/GCM模式,该模式同时提供加密和认证功能,安全性优于CBC模式。
内容的提问来源于stack exchange,提问作者Shawn
相关产品推荐
相关产品推荐

