[解決済み】与えられた最終ブロックが正しくパディングされない
質問
パスワードベースの暗号化アルゴリズムを実装しようとしているのですが、この例外が発生します。
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ビット(標準型)。
関連
-
[解決済み】Javaの".class期待値"
-
[解決済み】Android java.lang.IllegalStateException: Android java.lang.IllegalStateException: Could not execute method of the activity
-
[解決済み】スレッド「main」での例外 java.lang.StringIndexOutOfBoundsException: 文字列のインデックスが範囲外です。0 [閉店]
-
[解決済み】指定された子にはすでに親がいます。先に子の親に対してremoveView()を呼び出す必要がある(Android)
-
[解決済み】Javaの部分文字列:「文字列のインデックスが範囲外」。
-
[解決済み】フォルダに書き込もうとすると「java.nio.file.AccessDeniedException」が発生する件
-
[解決済み】 executeQuery()でデータ操作文が発行できない。)
-
[解決済み] 複数の例外を1行でキャッチする(ブロックを除く)
-
[解決済み] 例外を正しく無視する方法
-
[解決済み】Javaではfinallyブロックは必ず実行されるのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Javaパッケージが存在しないエラー
-
[解決済み】Android Studio クラス org.codehaus.groovy.runtime.InvokerHelper を初期化できませんでした。
-
[解決済み] 二項演算子「&」のオペランド型がおかしい java
-
[解決済み】非閉鎖文字リテラルエラー
-
[解決済み】Javaで無限大を実装する方法とは?
-
[解決済み】JLabelのテキストを中央に配置するには?
-
[解決済み】Javaでユーザー入力を待機させる方法
-
[解決済み】スレッド "main "での例外 java.util.NoSuchElementException
-
[解決済み】Java LinkedListでNodesを使用する
-
[解決済み】予期しない型エラー