簡體   English   中英

AES-128加密不適用於Java <1.7

[英]AES-128 Encryption not working on Java < 1.7

我已經在學校任務中​​搗亂了3天,終於在今天完成了它,沒有錯誤且工作正常! 除了,我在Java 1.7上測試它,學校服務器(教授將編譯它)運行1.6。 所以,我在1.6上測試了我的代碼,希望覆蓋我的所有基礎,並在解密時得到BadPaddingException

[編輯] 警告:此代碼不遵循常見的安全實踐,不應在生產代碼中使用。

最初,我有這個,在1.7上工作正常(對不起,很多代碼..所有相關..):

public static String aes128(String key, String data, final int direction) {
    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES");
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    }

    try {
        if (direction == ENCRYPT) {
            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

但是,我的BadPaddingException catch塊在解密時執行:

javax.crypto.BadPaddingException: Given final block not properly padded
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
        at javax.crypto.Cipher.doFinal(DashoA13*..)
        at CipherUtils.aes128(CipherUtils.java:112)
        at CipherUtils.decryptFile(CipherUtils.java:44)
        at decryptFile.main(decryptFile.java:21)

這就是我試圖解決的問題(基本上,我自己添加了所有填充/取消填充,並使用NoPadding ):

public static String aes128(String key, String data, final int direction) {
    // PADCHAR = (char)0x10 as String
    while (key.length() % 16 > 0)
        key = key + PADCHAR; // Added this loop

    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");
    AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    } catch (InvalidAlgorithmParameterException e) {
        return null; // Added this catch{}
    }

    try {
        if (direction == ENCRYPT) {
            while (data.length() % 16 > 0)
                data = data + PADCHAR; // Added this loop

            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

使用它時,我只是亂搞亂哄哄:

Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)

值得注意的是1.6和1.7產生不同的加密字符串。

例如,在1.7上,使用key hi加密xy (包括SHA-1哈希)會產生:

XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=

在1.6,同樣的事情產生:

nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=

我沒想到任務需要這么長時間,所以我的時間已經用完了,今晚確實需要完成。 如果那時候沒有答案,我只會給老師留言。 它似乎是在1.7中修復的一些問題...雖然希望可以通過我的代碼中的正確添加/修復來解決。

非常感謝每個人的時間!

首先:

對於幾乎所有系統,兩次加密相同的明文應該總是 (即非常非常高的概率)產生不同的密文。

傳統的例子是,它允許CPA對手通過兩個查詢區分E(“黎明時的攻擊”)和E(“黃昏時的攻擊”)。 (有一些系統需要確定性加密,但正確的方法是“合成IV”或密碼模式,如CMC和EME。)

最終,問題是SecureRandom()不是用於密鑰派生的。

  • 如果輸入“key”是密碼,那么你應該使用類似PBKDF2(或scrypt()bcrypt() )的東西。
    • 此外,您應該使用顯式字符集,例如String.getBytes("UTF-8")
  • 如果輸入“key”是鍵,則最常見的字符串表示形式是hexdump。 Java不包括unhexing功能,但有幾個在這里
    • 如果輸入是“主密鑰”並且您想要派生子密鑰,那么您應該使用其他數據對其進行哈希處理 如果子鍵始終相同,則沒有多大意義。

額外的挑剔:

  • 您的代碼容易受到填充oracle攻擊; 你真的應該在對數據做任何事情之前驗證MAC(或者更好,使用經過驗證的加密模式)。
  • 在第二個列表中,您明確重用了IV。 壞! 假設使用CBC模式,使用的IV應該是不可預測的; SecureRandom在這里很有用。

我一直在看,我不得不同意NullUserException。 問題是使用SecureRandom 這意味着你永遠不會真正知道你的密鑰是什么,因此它不一定是相同的密鑰。

encKey來自SecureRandom,它由提供的密鑰播種。 因此,如果鍵是相同的,種子是相同的,所以隨機應該是相同的...

...當然,除非Oracle(或其他提供商)更改版本之間的實現。

好的,添加我研究的更多信息。 我認為這個答案最有幫助

從用戶獲取密碼和明文,並將它們轉換為字節數組。
生成安全的隨機鹽。
將salt附加到密碼並計算其加密哈希值。 重復多次。
使用生成的哈希作為初始化向量和/或密鑰加密明文。
保存鹽和生成的密文。

對我來說,聽起來像SecureRandom只使用一次來生成salt但是必須使用密碼文本保存salt以撤消加密過程。 額外的安全性來自步驟的重復和變化(默默無聞)。

注意:我無法找到任何共識,即這些步驟是最佳做法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM