it-swarm-eu.dev

Der letzte Block ist nicht richtig aufgefüllt

Ich versuche, einen kennwortbasierten Verschlüsselungsalgorithmus zu implementieren, erhalte jedoch die folgende Ausnahme:

javax.crypto.BadPaddingException: Der angegebene letzte Block wurde nicht richtig aufgefüllt

Was könnte das Problem sein? (Ich bin neu in Java.)

Hier ist mein Code:

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;
    }
}

(Der JUnit-Test)

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.
        }
    }
}
93
Altrim

Wenn Sie versuchen, mit PKCS5 aufgefüllte Daten mit dem falschen Schlüssel zu entschlüsseln und sie anschließend zu entfernen (was automatisch von der Cipher-Klasse durchgeführt wird), erhalten Sie höchstwahrscheinlich die BadPaddingException (mit etwas weniger als 255/256, etwa 99,61%). ), weil die Auffüllung eine spezielle Struktur hat, die beim Aufheben der Auffüllung validiert wird und nur wenige Tasten eine gültige Auffüllung erzeugen würden.

Wenn Sie also diese Ausnahme erhalten, fangen Sie sie ein und behandeln Sie sie als "falschen Schlüssel".

Dies kann auch passieren, wenn Sie ein falsches Kennwort eingeben, das dann verwendet wird, um den Schlüssel aus einem Schlüsselspeicher zu erhalten, oder das mithilfe einer Schlüsselgenerierungsfunktion in einen Schlüssel umgewandelt wird.

Natürlich kann es auch zu einer schlechten Auffüllung kommen, wenn Ihre Daten beim Transport beschädigt werden.

Es gibt jedoch einige Sicherheitshinweise zu Ihrem System:

  • Für die kennwortbasierte Verschlüsselung sollten Sie SecretKeyFactory und PBEKeySpec anstelle eines SecureRandom mit KeyGenerator verwenden. Der Grund ist, dass SecureRandom bei jeder Java-Implementierung einen anderen Algorithmus verwenden kann, der Ihnen einen anderen Schlüssel gibt. Die SecretKeyFactory führt die Schlüsselableitung auf definierte Weise aus (und eine Art, die als sicher gilt, wenn Sie den richtigen Algorithmus auswählen).

  • Verwenden Sie nicht den ECB-Modus. Es verschlüsselt jeden Block unabhängig voneinander, was bedeutet, dass identische Klartextblöcke auch immer identische Chiffretextblöcke ergeben.

    Verwenden Sie vorzugsweise einen sicheren Betriebsmodus , wie CBC (Cipher Block Chaining) oder CTR (Counter). Verwenden Sie alternativ einen Modus, der auch eine Authentifizierung umfasst, wie GCM (Galois-Counter-Modus) oder CCM (Counter mit CBC-MAC), siehe nächster Punkt.

  • Normalerweise möchten Sie nicht nur die Vertraulichkeit, sondern auch die Authentifizierung, um sicherzustellen, dass die Nachricht nicht manipuliert wird. (Dies verhindert auch, dass Chiffrierung mit ausgewählten Chiffren auf Ihre Chiffre erfolgt, d. H. Sie hilft bei der Vertraulichkeit.) Fügen Sie Ihrer Nachricht also einen MAC-Code (Message Authentication Code) hinzu oder verwenden Sie einen Chiffriermodus, der die Authentifizierung beinhaltet (siehe vorherigen Punkt).

  • DES hat eine effektive Schlüsselgröße von nur 56 Bit. Dieser Schlüsselbereich ist ziemlich klein und kann von einigen Angreifern innerhalb weniger Stunden brutal erzwungen werden. Wenn Sie Ihren Schlüssel durch ein Kennwort generieren, wird dies noch schneller. Außerdem hat DES eine Blockgröße von nur 64 Bit, was weitere Schwächen in den Verkettungsmodi hinzufügt. Verwenden Sie einen modernen Algorithmus wie AES stattdessen mit einer Blockgröße von 128 Bit und einer Schlüsselgröße von 128 Bit (für die Standardvariante).

177
Paŭlo Ebermann

abhängig vom verwendeten Kryptographiealgorithmus müssen Sie möglicherweise am Ende einige Auffüllbytes hinzufügen, bevor Sie ein Byte-Array verschlüsseln, sodass die Länge des Byte-Arrays ein Vielfaches der Blockgröße beträgt:

In Ihrem Fall ist das von Ihnen gewählte Padding-Schema PKCS5, das hier beschrieben wird: http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_CJ _SYM__PAD.html

(Ich gehe davon aus, dass Sie das Problem haben, wenn Sie versuchen zu verschlüsseln)

Sie können Ihr Füllschema auswählen, wenn Sie das Cipher-Objekt instanziieren. Unterstützte Werte hängen vom verwendeten Sicherheitsanbieter ab. 

Sind Sie sicher, dass Sie einen symmetrischen Verschlüsselungsmechanismus verwenden möchten, um Kennwörter zu verschlüsseln? Wäre kein Einweg-Hash besser? Wenn Sie wirklich in der Lage sein müssen, Kennwörter zu entschlüsseln, ist DES eine ziemlich schwache Lösung. Möglicherweise möchten Sie etwas Stärkeres wie AES verwenden, wenn Sie sich an einen symmetrischen Algorithmus halten müssen.

1
fpacifici

Ich habe dieses Problem aufgrund des Betriebssystems getroffen, eine einfache bis andere Plattform zur JRE-Implementierung. 

        new SecureRandom(key.getBytes())

wird unter Windows denselben Wert erhalten, während er in Linux unterschiedlich ist. Also in Linux muss geändert werden

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

"SHA1PRNG" ist der verwendete Algorithmus. Sie können hier für weitere Informationen zu Algorithmen nachschlagen.

0
Bejond