Verilen son blok uygun şekilde doldurulmamış


115

Parola tabanlı şifreleme algoritması uygulamaya çalışıyorum, ancak şu istisnayı alıyorum:

javax.crypto.BadPaddingException: Son blok verildiğinde doğru şekilde doldurulmamış

Sorun ne olabilir?

İşte kodum:

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 Testi)

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

Yanıtlar:


197

PKCS5 dolgulu verilerin şifresini yanlış anahtarla çözmeye çalışırsanız ve sonra açarsanız (bu, Cipher sınıfı tarafından otomatik olarak yapılır), büyük olasılıkla BadPaddingException'ı alırsınız (muhtemelen 255 / 256'dan biraz daha az, yaklaşık% 99,61 ), çünkü doldurma, pedin kaldırılması sırasında doğrulanan özel bir yapıya sahiptir ve çok az tuş geçerli bir dolgu üretecektir.

Yani, bu istisnayı alırsanız, yakalayın ve "yanlış anahtar" olarak değerlendirin.

Bu, daha sonra anahtarı bir anahtar deposundan almak için kullanılan veya bir anahtar oluşturma işlevi kullanılarak bir anahtara dönüştürülen yanlış bir parola sağladığınızda da olabilir.

Elbette, verileriniz aktarım sırasında bozulursa kötü dolgu da olabilir.

Bununla birlikte, planınız hakkında bazı güvenlik açıklamaları var:

  • Parola tabanlı şifreleme için, KeyGenerator ile SecureRandom kullanmak yerine bir SecretKeyFactory ve PBEKeySpec kullanmalısınız. Bunun nedeni, SecureRandom'un her Java uygulamasında farklı bir algoritma olması ve size farklı bir anahtar vermesidir. SecretKeyFactory, anahtar türetmeyi tanımlanmış bir şekilde (ve doğru algoritmayı seçerseniz güvenli kabul edilen bir şekilde) yapar.

  • ECB modunu kullanmayın. Her bloğu bağımsız olarak şifreler, yani aynı düz metin blokları da her zaman aynı şifreli metin blokları verir.

    Tercihen CBC (Şifreleme bloğu zincirleme) veya CTR (Sayaç) gibi güvenli bir çalışma modu kullanın . Alternatif olarak, GCM (Galois-Counter modu) veya CCM (CBC-MAC ile Sayaç) gibi kimlik doğrulamayı da içeren bir mod kullanın, bir sonraki noktaya bakın.

  • Normalde yalnızca gizlilik istemezsiniz, aynı zamanda mesajın kurcalanmamasını sağlayan kimlik doğrulaması da istersiniz. (Bu aynı zamanda şifrenizde seçilen şifreli metin saldırılarını önler, yani gizliliğe yardımcı olur.) Bu nedenle, mesajınıza bir MAC (mesaj doğrulama kodu) ekleyin veya kimlik doğrulamasını içeren bir şifre modu kullanın (önceki noktaya bakın).

  • DES, yalnızca 56 bitlik etkili bir anahtar boyutuna sahiptir. Bu anahtar alanı oldukça küçüktür ve özel bir saldırgan tarafından birkaç saat içinde kaba kuvvetle zorlanabilir. Anahtarınızı bir parola ile oluşturursanız, bu daha da hızlı olacaktır. Ayrıca DES, yalnızca 64 bitlik bir blok boyutuna sahiptir, bu da zincirleme modlarında bazı zayıflıklar ekler. Bunun yerine blok boyutu 128 bit ve anahtar boyutu 128 bit (standart varyant için) olan AES gibi modern bir algoritma kullanın.


1
Sadece onaylamak istiyorum. Şifreleme konusunda yeniyim ve bu benim senaryom, AES şifreleme kullanıyorum. şifreleme / şifre çözme işlevimde bir şifreleme anahtarı kullanıyorum. Şifre çözmede yanlış bir şifreleme anahtarı kullandım ve bunu aldım javax.crypto.BadPaddingException: Given final block not properly padded. Bunu yanlış anahtar olarak değerlendirmeli miyim?
kenicky

Daha açık olmak gerekirse, bu, bir .p12 dosyası gibi bir anahtar deposu dosyası için yanlış şifre girildiğinde de olabilir, ki bu benim başıma gelen buydu.
Warren Çiğ

2
@WarrenDew "Bir anahtar deposu dosyası için yanlış şifre", "yanlış anahtar" ın özel bir durumudur.
Paŭlo Ebermann

@kenicky üzgünüm, az önce yorumunuzu gördüm ... evet, yanlış bir anahtar neredeyse her zaman bu etkiye neden olur. (Elbette, bozuk veriler başka bir olasılıktır.)
Paŭlo Ebermann

@ PaŭloEbermann Katılıyorum, ancak bunun hemen aşikar olduğunu sanmıyorum, çünkü programcının anahtar ve şifre çözme üzerinde kontrol sahibi olduğu orijinal gönderideki durumdan farklı. Yine de cevabınızı ona olumlu oy verecek kadar faydalı buldum.
Warren Dew

1

Kullandığınız kriptografi algoritmasına bağlı olarak, bayt dizisinin uzunluğunun blok boyutunun katı olması için, bir bayt dizisini şifrelemeden önce sonuna bazı dolgu baytları eklemeniz gerekebilir:

Özellikle sizin durumunuzda, seçtiğiniz dolgu şeması burada açıklanan PKCS5'tir : http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_ CJ _SYM__PAD.html

(Şifrelemeye çalıştığınızda sorun yaşadığınızı varsayıyorum)

Cipher nesnesini başlatırken dolgu şemanızı seçebilirsiniz. Desteklenen değerler, kullandığınız güvenlik sağlayıcısına bağlıdır.

Bu arada, parolaları şifrelemek için simetrik bir şifreleme mekanizması kullanmak istediğinizden emin misiniz? Daha iyi bir hash olmaz mıydı? Şifrelerin şifresini gerçekten çözebilmeniz gerekiyorsa, DES oldukça zayıf bir çözümdür, simetrik bir algoritma ile kalmanız gerekiyorsa AES gibi daha güçlü bir şey kullanmak ilginizi çekebilir.


1
Bu yüzden lütfen şifrelemeye / çözmeye çalışan kodu gönderebilir misiniz? (ve şifresini
çözmeye

1
Java'da ve ayrıca Kriptografide çok yeniyim, bu yüzden hala şifreleme yapmanın daha iyi yollarını bilmiyorum. Bunu uygulamak için muhtemelen daha iyi yollar aramaktansa bunu halletmek istiyorum.
Altrim

bağlantıyı güncelleyebilir misiniz, çünkü @ fpacifici çalışmıyor ve gönderimi güncelledim Şifreleme ve şifre çözmeyi test eden JUnit testini
ekledim

Düzeltildi (üzgünüm kopyala yapıştır hatası). Her neyse, aslında sorununuz, Paulo'nun açıkladığı gibi şifreleme için kullanılanla aynı olmayan bir anahtarla şifresini çözdüğünüz için oluyor. Bu, junit'te @Before ile açıklanmış yöntem her test yönteminden önce çalıştırıldığı ve böylece her seferinde anahtarı yeniden oluşturduğu için gerçekleşir. anahtar rasgele başlatıldığı için her seferinde farklı olacaktır.
fpacifici

1

JRE uygulamasıyla ilgili basitten farklı platforma işletim sistemi nedeniyle bu sorunla karşılaştım.

new SecureRandom(key.getBytes())

Linux'ta farklı iken Windows'ta aynı değeri alacaktır. Yani Linux'ta şu şekilde değiştirilmelidir:

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

"SHA1PRNG" kullanılan algoritmadır, algoritmalar hakkında daha fazla bilgi için buraya başvurabilirsiniz .


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.