เนื่องจากบล็อกสุดท้ายไม่ได้รับการหุ้มอย่างถูกต้อง


115

ฉันกำลังพยายามใช้อัลกอริทึมการเข้ารหัสที่ใช้รหัสผ่าน แต่ฉันได้รับข้อยกเว้นนี้:

javax.crypto.BadPaddingException: เนื่องจากบล็อกสุดท้ายไม่ได้รับการหุ้มอย่างถูกต้อง

ปัญหาอาจเกิดจากอะไร?

นี่คือรหัสของฉัน:

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) { 
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(การทดสอบ JUnit)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}

คำตอบ:


197

หากคุณพยายามถอดรหัสข้อมูลที่มีเบาะ PKCS5 ด้วยคีย์ที่ไม่ถูกต้องจากนั้นจึงทำการแกะออก (ซึ่งทำโดยคลาส Cipher โดยอัตโนมัติ) คุณมักจะได้รับ BadPaddingException (ซึ่งอาจน้อยกว่า 255/256 เล็กน้อยประมาณ 99.61% ) เนื่องจากช่องว่างภายในมีโครงสร้างพิเศษซึ่งได้รับการตรวจสอบระหว่าง unpad และมีแป้นน้อยมากที่จะทำให้เกิดช่องว่างภายในที่ถูกต้อง

ดังนั้นหากคุณได้รับข้อยกเว้นนี้ให้จับและถือว่าเป็น "คีย์ผิด"

นอกจากนี้ยังสามารถเกิดขึ้นได้เมื่อคุณป้อนรหัสผ่านผิดซึ่งจะใช้เพื่อรับคีย์จากที่เก็บคีย์หรือซึ่งถูกแปลงเป็นคีย์โดยใช้ฟังก์ชันการสร้างคีย์

แน่นอนว่าช่องว่างภายในที่ไม่ดีอาจเกิดขึ้นได้หากข้อมูลของคุณเสียหายในการขนส่ง

ที่กล่าวว่ามีข้อสังเกตด้านความปลอดภัยเกี่ยวกับโครงการของคุณ:

  • สำหรับการเข้ารหัสโดยใช้รหัสผ่านคุณควรใช้ SecretKeyFactory และ PBEKeySpec แทนการใช้ SecureRandom กับ KeyGenerator เหตุผลก็คือ SecureRandom อาจเป็นอัลกอริทึมที่แตกต่างกันในการใช้งาน Java แต่ละครั้งทำให้คุณมีคีย์ที่แตกต่างกัน SecretKeyFactory สร้างคีย์ที่ได้มาในลักษณะที่กำหนด (และลักษณะที่ถือว่าปลอดภัยหากคุณเลือกอัลกอริทึมที่ถูกต้อง)

  • อย่าใช้โหมด ECB มันเข้ารหัสแต่ละบล็อกอย่างอิสระซึ่งหมายความว่าบล็อกข้อความธรรมดาที่เหมือนกันจะให้บล็อกการเข้ารหัสที่เหมือนกันเสมอ

    ควรใช้โหมดการทำงานที่ปลอดภัยเช่น CBC (Cipher block chaining) หรือ CTR (Counter) หรือใช้โหมดที่มีการพิสูจน์ตัวตนด้วยเช่น GCM (โหมด Galois-Counter) หรือ CCM (ตัวนับพร้อม CBC-MAC) โปรดดูจุดถัดไป

  • โดยปกติคุณไม่ต้องการเพียงแค่การรักษาความลับเท่านั้น แต่ยังรวมถึงการรับรองความถูกต้องด้วยซึ่งทำให้แน่ใจว่าข้อความจะไม่ถูกดัดแปลง (นอกจากนี้ยังป้องกันการโจมตีรหัสลับที่เลือกไว้ในการเข้ารหัสของคุณเช่นช่วยในการรักษาความลับ) ดังนั้นให้เพิ่ม MAC (รหัสรับรองความถูกต้องของข้อความ) ในข้อความของคุณหรือใช้โหมดการเข้ารหัสซึ่งรวมถึงการตรวจสอบสิทธิ์ (ดูจุดก่อนหน้า)

  • DES มีขนาดคีย์ที่มีประสิทธิภาพเพียง 56 บิต พื้นที่สำคัญนี้มีขนาดค่อนข้างเล็กผู้โจมตีเฉพาะสามารถบังคับได้ภายในเวลาไม่กี่ชั่วโมง หากคุณสร้างรหัสของคุณด้วยรหัสผ่านสิ่งนี้จะเร็วยิ่งขึ้น นอกจากนี้ DES ยังมีขนาดบล็อกเพียง 64 บิตซึ่งเพิ่มจุดอ่อนเพิ่มเติมในโหมดการผูกมัด ใช้อัลกอริทึมที่ทันสมัยเช่น AES แทนซึ่งมีขนาดบล็อก 128 บิตและขนาดคีย์ 128 บิต (สำหรับตัวแปรมาตรฐาน)


1
ฉันแค่อยากจะยืนยัน ฉันใหม่กับการเข้ารหัสและนี่คือสถานการณ์ของฉันฉันใช้การเข้ารหัส AES ในฟังก์ชันเข้ารหัส / ถอดรหัสของฉันฉันใช้คีย์เข้ารหัส javax.crypto.BadPaddingException: Given final block not properly paddedผมใช้คีย์การเข้ารหัสผิดพลาดในการถอดรหัสและผมได้รับนี้ ฉันควรถือว่านี่เป็นคีย์ผิดหรือไม่?
kenicky

เพื่อให้ชัดเจนสิ่งนี้อาจเกิดขึ้นได้เมื่อระบุรหัสผ่านที่ไม่ถูกต้องสำหรับไฟล์ที่เก็บคีย์เช่นไฟล์. p12 ซึ่งเป็นสิ่งที่เพิ่งเกิดขึ้นกับฉัน
Warren Dew

2
@WarrenDew "รหัสผ่านสำหรับไฟล์ที่เก็บคีย์ไม่ถูกต้อง" เป็นเพียงกรณีพิเศษของ "คีย์ผิด"
Paŭlo Ebermann

@kenicky ขอโทษฉันเห็นความคิดเห็นของคุณแล้ว ... ใช่คีย์ผิดมักทำให้เกิดผลกระทบนี้ (แน่นอนว่าข้อมูลที่เสียหายก็เป็นไปได้อีกอย่าง)
Paŭlo Ebermann

@ PaŭloEbermannฉันเห็นด้วย แต่ฉันไม่คิดว่าสิ่งนั้นจำเป็นต้องชัดเจนในทันทีเนื่องจากมันแตกต่างจากสถานการณ์ในโพสต์ต้นฉบับที่โปรแกรมเมอร์สามารถควบคุมคีย์และการถอดรหัสได้ ฉันพบว่าคำตอบของคุณมีประโยชน์มากพอที่จะโหวตได้
Warren Dew

1

ขึ้นอยู่กับอัลกอริทึมการเข้ารหัสที่คุณใช้คุณอาจต้องเพิ่มไบต์ช่องว่างที่ส่วนท้ายก่อนที่จะเข้ารหัสอาร์เรย์ไบต์เพื่อให้ความยาวของอาร์เรย์ไบต์มีหลายขนาดบล็อก:

โดยเฉพาะอย่างยิ่งในกรณีของคุณสคีมา padding ที่คุณเลือกคือ PKCS5 ซึ่งอธิบายไว้ที่นี่: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_ CJ _SYM__PAD.html

(ฉันถือว่าคุณมีปัญหาเมื่อพยายามเข้ารหัส)

คุณสามารถเลือกสคีมาการขยายของคุณเมื่อคุณสร้างอินสแตนซ์อ็อบเจ็กต์ Cipher ค่าที่รองรับขึ้นอยู่กับผู้ให้บริการความปลอดภัยที่คุณใช้

คุณแน่ใจหรือไม่ว่าต้องการใช้กลไกการเข้ารหัสแบบสมมาตรในการเข้ารหัสรหัสผ่าน จะแฮชทางเดียวไม่ดีกว่าเหรอ หากคุณต้องการถอดรหัสรหัสผ่านจริงๆ DES เป็นวิธีแก้ปัญหาที่อ่อนแอคุณอาจสนใจที่จะใช้สิ่งที่แข็งแกร่งกว่าเช่น AES หากคุณต้องการใช้อัลกอริทึมแบบสมมาตร


1
ดังนั้นโปรดโพสต์รหัสที่พยายามเข้ารหัส / ถอดรหัสได้ไหม (และตรวจสอบว่าอาร์เรย์ไบต์ที่คุณพยายามถอดรหัสไม่ใหญ่กว่าขนาดบล็อก)
fpacifici

1
ฉันยังใหม่กับ Java และการเข้ารหัสด้วยดังนั้นฉันจึงยังไม่รู้วิธีเข้ารหัสที่ดีกว่านี้ ฉันแค่อยากทำสิ่งนี้ให้เสร็จก่อนที่จะมองหาวิธีที่ดีกว่าในการนำไปใช้
Altrim

คุณสามารถอัปเดตลิงก์ได้หรือไม่เพราะ @fpacifici และฉันอัปเดตโพสต์ของฉันฉันรวมการทดสอบ JUnit ที่ทดสอบการเข้ารหัสและการถอดรหัส
Altrim

แก้ไขแล้ว (ขออภัยข้อผิดพลาดในการวางสำเนา) อย่างไรก็ตามปัญหาของคุณเกิดขึ้นเนื่องจากคุณถอดรหัสด้วยคีย์ซึ่งไม่เหมือนกับคีย์ที่ใช้ในการเข้ารหัสตามที่ Paulo อธิบาย สิ่งนี้เกิดขึ้นเนื่องจากเมธอดที่ใส่คำอธิบายประกอบด้วย @Before ใน junit ถูกเรียกใช้ก่อนวิธีการทดสอบทุกครั้งดังนั้นการสร้างคีย์ใหม่ทุกครั้ง เนื่องจากคีย์ถูกเริ่มต้นแบบสุ่มมันจะแตกต่างกันในแต่ละครั้ง
fpacifici

1

ฉันพบปัญหานี้เนื่องจากระบบการทำงานเรียบง่ายสำหรับแพลตฟอร์มที่แตกต่างกันเกี่ยวกับการใช้งาน JRE

new SecureRandom(key.getBytes())

จะได้รับค่าเดียวกันใน Windows ในขณะที่ Linux ต่างกัน ดังนั้นใน Linux จำเป็นต้องเปลี่ยนเป็นไฟล์

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

"SHA1PRNG" เป็นอัลกอริทึมที่ใช้คุณสามารถดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับขั้นตอนวิธีการ


0

นอกจากนี้ยังอาจเป็นปัญหาเมื่อคุณป้อนรหัสผ่านสำหรับคีย์ลงชื่อเข้าใช้ผิด


นี่คือความคิดเห็นจริงๆไม่ใช่คำตอบ ด้วยตัวแทนอีกเล็กน้อยคุณจะสามารถโพสต์ความคิดเห็นได้
Adrian Mole

นี่ไม่ใช่คำตอบ
zig razor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.