1. ホーム
  2. java

[解決済み】与えられた最終ブロックが正しくパディングされない

2022-02-01 18:10:28

質問

パスワードベースの暗号化アルゴリズムを実装しようとしているのですが、この例外が発生します。

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

解決方法は?

PKCS5 でパディングされたデータを間違った鍵で復号し、パディングを解除すると (Cipher クラスが自動的に行います)、ほとんどの場合 BadPaddingException (255/256 より少し少ない、約 99.61%) が発生します。パディングは特殊な構造を持っており、解除時に検証されるため、有効なパディングを生成する鍵が非常に少なくなるからです。

ですから、もしこの例外が発生したら、それをキャッチして "間違ったキー" として処理してください。

また、間違ったパスワードを入力し、そのパスワードを使って鍵ストアから鍵を取得したり、鍵生成機能を使って鍵を変換したりした場合にも、このようなことが起こる可能性があります。

もちろん、輸送中にデータが破損した場合にも、不正なパディングが発生する可能性があります。

とはいえ、あなたのスキームにはセキュリティ上の注意点があります。

  • パスワードベースの暗号化では、SecureRandomとKeyGeneratorを使うのではなく、SecretKeyFactoryとPBEKeySpecを使うべきでしょう。理由は、SecureRandomはJavaの実装ごとに異なるアルゴリズムになる可能性があり、異なるキーを与えるからです。SecretKeyFactoryは、定義された方法で鍵の導出を行います(正しいアルゴリズムを選択すれば、安全であるとみなされる方法です)。

  • ECB-modeは使わないでください。これは各ブロックを独立して暗号化するため、同一の平文ブロックは常に同一の暗号文ブロックになることを意味します。

    好ましくは、安全な 動作モード CBC (Cipher block chaining) や CTR (Counter) のような。あるいは、GCM (Galois-Counter mode) や CCM (Counter with CBC-MAC) のような、認証も含むモードを使用することもできます。

  • 通常、機密性だけでなく、メッセージが改ざんされていないことを確認するための認証も必要です。(これは暗号に対する選択暗号文攻撃も防ぐことができ、つまり機密保持に役立ちます)。そこで、メッセージにMAC(メッセージ認証コード)を追加するか、認証を含む暗号モードを使用します(前項参照)。

  • DESの有効な鍵のサイズはわずか56ビットです。この鍵空間は非常に小さく、熱心な攻撃者であれば数時間でブルートフォースさせることができます。もし、パスワードで鍵を生成した場合は、さらに速くなります。 また、DESはブロックサイズが64ビットしかないため、連鎖的なモードではさらに弱点が増えます。 代わりに、AESのような最新のアルゴリズムを使用してください。 128ビット(標準型)。