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); }
问题根源
AES CBC模式未处理初始化向量(IV)
AES/CBC模式要求加密和解密使用相同的IV。当前加密代码中调用aesCipher.init(Cipher.ENCRYPT_MODE, secretKey)时,Cipher会自动生成随机IV,但该IV并未被保存到加密结果中,解密时无法获取正确的IV,导致解密失败触发BadPaddingException。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ş
相关产品推荐
相关产品推荐

