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

AES加密输出长度不固定致数据库字段截断问题求助

解决AES/CBC加密密文长度不一致导致数据库字段截断问题

问题根源

你遇到的Data truncation: Data too long for column异常本质上有两个核心原因:

  1. AES块加密的特性:AES是块大小为16字节的块加密算法,PKCS5Padding会自动将明文填充到16字节的整数倍。加密后的二进制密文长度是ceil(明文字节数 / 16) * 16,转成十六进制(Hex)字符串后,每个字节对应2个字符,因此密文Hex长度是密文字节数的2倍。比如:
    • 短明文(如"test",4字节)会被填充到16字节,Hex密文长度为32字符
    • 较长明文(如"this is test",12字节)同样填充到16字节,Hex密文长度也是32字符(你提供的示例密文可能是笔误,正常情况下同块数的明文加密后Hex长度一致)
    • 如果明文超过16字节(比如17字节),会被填充到32字节,Hex密文长度变为64字符
  2. 数据库字段长度不足:你的firstName字段定义为varchar(25),但哪怕最短的Hex密文都需要32字符,远远超过字段限制,必然触发截断异常。

修复步骤

1. 调整数据库字段长度(或类型)

根据firstName的最大明文长度计算所需的最大密文长度:

  • 假设firstName最多25个UTF-8字符,每个字符最多占3字节,总字节数为25*3=75字节
  • 填充后密文字节数为ceil(75/16)*16=80字节
  • 转Hex后字符串长度为80*2=160字符

因此,建议将字段修改为:

ALTER TABLE user MODIFY COLUMN firstName VARCHAR(160);

或者更高效的方式,直接存储二进制密文(节省空间):

ALTER TABLE user MODIFY COLUMN firstName VARBINARY(80);

(如果用二进制存储,加密时直接存cipherBytes,解密时读取字节数组即可,无需Hex转换)

2. 修复加密代码中的安全隐患(重要!)

你的代码中IV(初始化向量)使用了全0的字节数组,这在CBC模式下是严重的安全问题——相同的明文会生成相同的密文,容易被破解。必须生成随机唯一的IV,并将IV与密文一起存储:

修改后的加密方法

public String encrypt(String plainText) {
    byte[] cipherBytes = null;
    log.info("Started encryption...");
    if (plainText != null && !plainText.isEmpty()) {
        if (cipher != null && key != null) {
            try {
                // 生成随机IV(AES块大小为16字节)
                byte[] ivByte = new byte[cipher.getBlockSize()];
                new SecureRandom().nextBytes(ivByte);
                IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
                
                cipher.init(Cipher.ENCRYPT_MODE, key, ivParamsSpec);
                cipherBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
                
                // 将IV和密文拼接(IV在前,密文在后),再转Hex
                byte[] combined = ByteBuffer.allocate(ivByte.length + cipherBytes.length)
                                            .put(ivByte)
                                            .put(cipherBytes)
                                            .array();
                plainText = Hex.encodeHexString(combined);
                
                log.info("Completed encryption.");
                log.info("Encrypted data (Hex): " + plainText);
            } catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException | InvalidAlgorithmParameterException e) {
                log.error("Encryption failed : " + e.getMessage());
                e.printStackTrace();
                throw new RuntimeException("Encryption failed : " + e.getMessage());
            }
        } else {
            log.error("Encryption failed, cipher, key is null.");
            throw new RuntimeException("Encryption failed, cipher, key is null.");
        }
    }
    return plainText;
}

修改后的解密方法

public String decrypt(String cipherHexString) throws AuthorizationException {
    log.info("Started decryption...");
    String resource = "kk";
    String resourceCatagory = "kk tables";
    String accessType = "write";
    try {
        if (decryptionAuthorizer.authorize(resource, resourceCatagory, accessType)) {
            if (cipherHexString != null && !cipherHexString.isEmpty()) {
                if (cipher != null && key != null) {
                    try {
                        byte[] combined = Hex.decodeHex(cipherHexString.toCharArray());
                        // 分离IV和密文(前16字节是IV)
                        byte[] ivByte = Arrays.copyOfRange(combined, 0, cipher.getBlockSize());
                        byte[] cipherBytes = Arrays.copyOfRange(combined, cipher.getBlockSize(), combined.length);
                        
                        IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
                        cipher.init(Cipher.DECRYPT_MODE, key, ivParamsSpec);
                        byte[] plainTextBytes = cipher.doFinal(cipherBytes);
                        
                        String plainText = new String(plainTextBytes, StandardCharsets.UTF_8);
                        log.info("Completed decryption.");
                        return plainText;
                    } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | DecoderException e) {
                        log.error("Decryption failed : " + e.getMessage());
                        e.printStackTrace();
                        throw new RuntimeException("Decryption failed : " + e.getMessage());
                    }
                } else {
                    log.error("Decryption failed, cipher, key is null.");
                    throw new RuntimeException("Decryption failed, cipher, key is null.");
                }
            }
        }
    } catch (AuthorizationException e) {
        throw new AuthorizationException("User not authorized to decrypt data.");
    }
    return cipherHexString;
}

3. 其他优化建议

  • 统一使用StandardCharsets.UTF_8代替硬编码的"UTF8",避免编码异常
  • 移除加密方法中new String(cipherBytes, "UTF8")的日志输出,因为二进制密文转UTF-8字符串会产生乱码,没有实际意义

内容的提问来源于stack exchange,提问作者Niranga Sandaruwan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:23:09