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

javax.crypto.BadPaddingException解密错误:许可证解密失败求助

解密许可证时出现javax.crypto.BadPaddingException的问题排查

生成并加密许可证文件后,尝试解密时触发javax.crypto.BadPaddingException错误,已尝试调整填充类型但未解决问题,相关代码如下:

static ObjectMapper objectMapper=new ObjectMapper();

public static void main(String[] args) throws Exception {
    KeyPairGenerator keyPairGenerator  = KeyPairGenerator.getInstance ("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair pair = keyPairGenerator.generateKeyPair();
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, 1);
    LicenseImportDTO licenseData = LicenseImportDTO.builder()
            .licenseKey ("123DS92-SAKH-SKH-22")
            .contact("WORLD A.Ş.")
            .customerName(DigestUtils.md5Hex("WORLD"))
            .cluster(false)
            .numberOfNodes(0)
            .numberOfSites(2)
            .duration(12)
            .licenseType(LicenseType.BASE)
            .deviceSize(DeviceSize.SMALL).build();

    String encrpyLicense = encryptExportLicenceDTO(licenseData,pair.getPublic());
    Thread.sleep(2000);
    readLicenseData(encrpyLicense,pair.getPrivate());
}

public static String encryptExportLicenceDTO(LicenseImportDTO licenceDTO, PublicKey publicKey) throws NoSuchAlgorithmException, NoSuchPaddingException, JsonProcessingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
    KeyGenerator keyGen = KeyGenerator.getInstance ("AES");
    keyGen.init(128);
    SecretKey secretKey = keyGen.generateKey();
    Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
    byte[] encryptedBytes = aesCipher.doFinal(objectMapper.writeValueAsString(licenceDTO).getBytes());
    Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
    byte[] encryptedSymmetricKey = rsaCipher.doFinal(secretKey.getEncoded());
    byte[] combined = new byte[encryptedBytes.length + encryptedSymmetricKey.length];
    System.arraycopy(encryptedSymmetricKey, 0, combined, 0, encryptedSymmetricKey.length);
    System.arraycopy(encryptedBytes, 0, combined, encryptedSymmetricKey.length, encryptedBytes.length);
    return Base64.getEncoder().encodeToString(combined);
}

private static LicenseImportDTO readLicenseData(String  encodedLicense , PrivateKey privateKey) throws Exception {
    byte[] combined = Base64.getDecoder().decode(encodedLicense);
    byte[] encryptedSymmetricKey = new byte[128];
    byte[] encryptedBytes = new byte[combined.length - 128];
    System.arraycopy(combined, 0, encryptedSymmetricKey, 0, 128);
    System.arraycopy(combined, 128, encryptedBytes, 0, encryptedBytes.length);
    Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
    byte[] symmetricKeyBytes = rsaCipher.doFinal(encryptedSymmetricKey);
    SecretKey secretKey = new SecretKeySpec(symmetricKeyBytes, 0, symmetricKeyBytes.length, "AES");
    Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    aesCipher.init(Cipher.DECRYPT_MODE, secretKey);
    byte[] decryptedBytes = aesCipher.doFinal(encryptedBytes);
    String decryptedJson = new String(decryptedBytes, StandardCharsets.UTF_8);
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(decryptedJson, LicenseImportDTO.class);
}

问题根源

  1. AES CBC模式未处理初始化向量(IV)
    AES/CBC模式要求加密和解密使用相同的IV。当前加密代码中调用aesCipher.init(Cipher.ENCRYPT_MODE, secretKey)时,Cipher会自动生成随机IV,但该IV并未被保存到加密结果中,解密时无法获取正确的IV,导致解密失败触发BadPaddingException。

  2. RSA加密密钥长度硬编码错误
    2048位RSA加密后的对称密钥长度为256字节(2048位=256字节),但解密代码中硬编码用128字节来拆分加密后的对称密钥,导致拆分出的密钥数据不完整,解密对称密钥时出错,进而引发后续AES解密失败。

修复后的代码

修改加密方法,保存IV和正确拼接数据

public static String encryptExportLicenceDTO(LicenseImportDTO licenceDTO, PublicKey publicKey) throws Exception {
    KeyGenerator keyGen = KeyGenerator.getInstance("AES");
    keyGen.init(128);
    SecretKey secretKey = keyGen.generateKey();
    
    Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
    // 获取自动生成的IV
    byte[] iv = aesCipher.getIV();
    
    byte[] licenseBytes = objectMapper.writeValueAsString(licenceDTO).getBytes(StandardCharsets.UTF_8);
    byte[] encryptedBytes = aesCipher.doFinal(licenseBytes);
    
    Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
    byte[] encryptedSymmetricKey = rsaCipher.doFinal(secretKey.getEncoded());
    
    // 拼接顺序:IV(16字节) + 加密后的对称密钥(256字节) + 加密的许可证数据
    byte[] combined = new byte[iv.length + encryptedSymmetricKey.length + encryptedBytes.length];
    System.arraycopy(iv, 0, combined, 0, iv.length);
    System.arraycopy(encryptedSymmetricKey, 0, combined, iv.length, encryptedSymmetricKey.length);
    System.arraycopy(encryptedBytes, 0, combined, iv.length + encryptedSymmetricKey.length, encryptedBytes.length);
    
    return Base64.getEncoder().encodeToString(combined);
}

修改解密方法,正确拆分数据并使用IV解密

private static LicenseImportDTO readLicenseData(String encodedLicense, PrivateKey privateKey) throws Exception {
    byte[] combined = Base64.getDecoder().decode(encodedLicense);
    
    // 拆分数据:IV固定16字节,2048位RSA加密密钥长度256字节
    byte[] iv = new byte[16];
    byte[] encryptedSymmetricKey = new byte[256];
    byte[] encryptedBytes = new byte[combined.length - iv.length - encryptedSymmetricKey.length];
    
    System.arraycopy(combined, 0, iv, 0, iv.length);
    System.arraycopy(combined, iv.length, encryptedSymmetricKey, 0, encryptedSymmetricKey.length);
    System.arraycopy(combined, iv.length + encryptedSymmetricKey.length, encryptedBytes, 0, encryptedBytes.length);
    
    // 解密对称密钥
    Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
    byte[] symmetricKeyBytes = rsaCipher.doFinal(encryptedSymmetricKey);
    SecretKey secretKey = new SecretKeySpec(symmetricKeyBytes, 0, symmetricKeyBytes.length, "AES");
    
    // 使用IV初始化AES解密器
    Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    aesCipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
    
    byte[] decryptedBytes = aesCipher.doFinal(encryptedBytes);
    String decryptedJson = new String(decryptedBytes, StandardCharsets.UTF_8);
    
    return objectMapper.readValue(decryptedJson, LicenseImportDTO.class);
}

额外优化

  • 移除readLicenseData中重复实例化的ObjectMapper,直接使用类中的静态实例objectMapper,避免冗余对象创建。

内容的提问来源于stack exchange,提问作者Hanbey Toktaş

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:06:00