首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

解決 javax.crypto.BadPaddingException:DES 金鑰生成陷阱與 SecretKeyFactory 正確用法

     編輯:Java 綜合教學

解決 javax.crypto.BadPaddingException:DES 金鑰生成陷阱與 SecretKeyFactory 正確用法

使用 SecureRandom 衍生 DES 金鑰在跨平台/多執行緒場景下極易導致 BadPaddingException: Given final block not properly padded。本文剖析根因,提供基於 SecretKeyFactory 的標準修復方案。

錯誤現象

javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    at javax.crypto.Cipher.doFinal(DashoA13*..)

加密正常,解密拋出。多執行緒並發測試時複現率極高。

問題根因:SecureRandom 實現差異

// 錯誤寫法:依賴 SecureRandom 衍生金鑰
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(new SecureRandom(strKey.getBytes())); // 種子來源不穩定
Key key = kg.generateKey();
  • SecureRandom 實現依賴 JCE Provider(SunJCE、BC 等),不同 JDK 版本/廠商種子擴展算法不同。
  • 相同種子可能產生不同金鑰,導致加密端與解密端金鑰不一致。
  • 多執行緒共享 KeyGenerator 實例時狀態競爭加劇問題。

正確方案:SecretKeyFactory + DESKeySpec

// 修復後的金鑰生成
private Key setKey(String strKey) {
    try {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
        DESKeySpec keySpec = new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8));
        return factory.generateSecret(keySpec); // 確定性生成,跨平台一致
    } catch (Exception e) {
        throw new RuntimeException("DES 金鑰生成失敗", e);
    }
}

優勢:

  1. 標準 PKCS#8 規範,確定性:相同密碼 → 相同金鑰。
  2. 無隨機數狀態,天然執行緒安全。
  3. 支援指定字元集(UTF-8),避免平台預設編碼差異。

其他常見導致 BadPadding 的原因

原因檢查方式
加密/解密金鑰不一致打印金鑰 getEncoded() 十六進制對比
填充模式不匹配統一指定 Cipher.getInstance("DES/ECB/PKCS5Padding")
資料截斷/損壞Base64 編碼/解碼是否完整、傳輸過程是否截斷
資料長度非 8 位元組倍數(無填充模式)必須使用 PKCS5Padding 或手動填充

完整修復版 DES 工具類片段

public class DesUtil {
    private static final String ALGO = "DES";
    private static final String TRANSFORMATION = "DES/ECB/PKCS5Padding";
    private final Cipher encryptCipher;
    private final Cipher decryptCipher;

    public DesUtil(String password) {
        try {
            Key key = generateKey(password);
            encryptCipher = Cipher.getInstance(TRANSFORMATION);
            encryptCipher.init(Cipher.ENCRYPT_MODE, key);
            decryptCipher = Cipher.getInstance(TRANSFORMATION);
            decryptCipher.init(Cipher.DECRYPT_MODE, key);
        } catch (Exception e) {
            throw new IllegalStateException("DES 初始化失敗", e);
        }
    }

    private Key generateKey(String pwd) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGO);
            DESKeySpec spec = new DESKeySpec(pwd.getBytes(StandardCharsets.UTF_8));
            return factory.generateSecret(spec);
        } catch (Exception e) {
            throw new IllegalArgumentException("無效密鑰", e);
        }
    }

    public String encrypt(String plain) {
        try {
            byte[] encrypted = encryptCipher.doFinal(plain.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            throw new RuntimeException("加密失敗", e);
        }
    }

    public String decrypt(String cipher) {
        try {
            byte[] decoded = Base64.getDecoder().decode(cipher);
            byte[] decrypted = decryptCipher.doFinal(decoded);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("解密失敗", e);
        }
    }
}

安全建議

  • DES 已不安全(56-bit 金鑰),新專案請使用 AES-256-GCM。
  • 金鑰管理請用專業 KMS/密碼保險箱,勿硬編碼。
  • 避免 ECB 模式,優先 CBC/GCM 並妥善管理 IV/Nonce。

掌握標準金鑰生成方式,徹底告別「莫名其妙」的解密失敗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved