You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:05:29