3

私は3日間学校の課題を片付けていましたが、今日ようやくそれを終えました。エラーはなく、正常に機能しています。ただし、私はJava 1.7でテストしており、学校のサーバー(教授がコンパイルする場所)は1.6を実行しています。それで、私は自分のコードを1.6でテストし、すべてのベースをカバーしたいと思っていBadPaddingExceptionました。

[編集]警告:このコードは一般的なセキュリティ慣行に準拠していないため、本番コードでは使用しないでください。

もともと、私はこれを持っていましたが、1.7で正常に動作します(申し訳ありませんが、たくさんのコード..すべて関連しています..):

public static String aes128(String key, String data, final int direction) {
    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES");
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    }

    try {
        if (direction == ENCRYPT) {
            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

ただし、私のBadPaddingException catchブロックは復号化時に実行されます。

javax.crypto.BadPaddingException: Given final block not properly padded
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
        at javax.crypto.Cipher.doFinal(DashoA13*..)
        at CipherUtils.aes128(CipherUtils.java:112)
        at CipherUtils.decryptFile(CipherUtils.java:44)
        at decryptFile.main(decryptFile.java:21)

これは私がそれを修正しようとしたものです(基本的に、私はすべてのパディング/アンパディングを自分で追加し、使用しましたNoPadding):

public static String aes128(String key, String data, final int direction) {
    // PADCHAR = (char)0x10 as String
    while (key.length() % 16 > 0)
        key = key + PADCHAR; // Added this loop

    SecureRandom rand = new SecureRandom(key.getBytes());
    byte[] randBytes = new byte[16];
    rand.nextBytes(randBytes);
    SecretKey encKey = new SecretKeySpec(randBytes, "AES");
    AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this

    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
        cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
    } catch (InvalidKeyException e) {
        return null;
    } catch (NoSuchPaddingException e) {
        return null;
    } catch (NoSuchAlgorithmException e) {
        return null;
    } catch (InvalidAlgorithmParameterException e) {
        return null; // Added this catch{}
    }

    try {
        if (direction == ENCRYPT) {
            while (data.length() % 16 > 0)
                data = data + PADCHAR; // Added this loop

            byte[] encVal = cipher.doFinal(data.getBytes());
            String encryptedValue = Base64.encode(encVal);
            return encryptedValue;
        } else {
            byte[] dataBytes = Base64.decode(data);
            byte[] encVal = cipher.doFinal(dataBytes);
            return new String(encVal);
        }
    } catch (NullPointerException e) {
        return null;
    } catch (BadPaddingException e) {
        return null;
    } catch (IllegalBlockSizeException e) {
        return null;
    }
}

これを使用するとき、私はただぎこちなく出入りします:

Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)

1.6と1.7では異なる暗号化文字列が生成されることにも注意してください。

たとえば、1.7では、キーを使用して暗号化xySHA-1ハッシュを含む)すると、次のようになりますhi

XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=

1.6では、同じことが次のようになります。

nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=

割り当てにそれほど時間がかかるとは思っていなかったので、時間がなくなり、今夜行う必要があります。ただし、それまでに回答がない場合は、先生にメモを残しておきます。1.7で修正された問題のようですが、コードを正しく追加/修正することで修正できるといいのですが。

みなさん、ありがとうございました!

4

2 に答える 2

3

最初に:

ほとんどすべてのシステムで、同じ平文を2回暗号化すると、常に(つまり、非常に高い確率で)異なる暗号文が生成されます。

従来の例では、CPAの攻撃者は、2つのクエリだけでE(「夜明けの攻撃」)とE(「夕暮れの攻撃」)を区別できます。(確定的暗号化が必要なシステムはいくつかありますが、これを行う正しい方法は、「合成IV」またはCMCやEMEなどの暗号モードです。)

最終的に、問題はSecureRandom()鍵導出を目的としていないことです。

  • 入力された「キー」がパスフレーズの場合は、PBKDF2(またはscrypt()またはbcrypt())のようなものを使用する必要があります。
    • さらに、明示的な文字セットを使用する必要がありますString.getBytes("UTF-8")
  • 入力「キー」がキーの場合、最も一般的な文字列表現は16進ダンプです。Javaにはアンヘックス関数は含まれていませんが、ここにはいくつかあります。
    • 入力が「マスターキー」であり、サブキーを派生させたい場合は、他のデータでハッシュする必要があります。サブキーが常に同じである場合、あまり意味がありません。

追加の落とし穴:

  • あなたのコードはパディングオラクル攻撃に対して脆弱です。データを処理する前に(または、認証された暗号化モードを使用して)MACを検証する必要があります。
  • 2番目のリストでは、IVを明示的に再利用します。悪い!CBCモードを想定すると、使用されるIVは予測できないはずです。SecureRandomここで便利です。
于 2012-10-09T01:37:53.680 に答える
2

私は何度も何度も見てきましたが、NullUserExceptionに同意する必要があります。問題はの使用ですSecureRandom。これは、自分のキーが何であるかを実際に知ることは決してないため、必ずしも同じキーであるとは限らないことを意味します。

encKeyは、提供されたキーによってシードされるSecureRandomから取得されます。したがって、キーが同じである場合、シードは同じであるため、ランダムは同じである必要があります...

...もちろん、Oracle(または別のプロバイダー)がバージョン間で実装を変更しない限り。

さて、私が調査した情報をさらに追加します。この答えが最も役に立ちました。

ユーザーからパスワードとクリアテキストを取得し、それらをバイト配列に変換します。
安全なランダムソルトを生成します。
パスワードにソルトを追加し、その暗号化ハッシュを計算します。これを何度も繰り返します。
結果のハッシュを初期化ベクトルおよび/または秘密鍵として使用して、クリアテキストを暗号化します。
ソルトと結果の暗号文を保存します。

私には、SecureRandomを生成するために一度使用されたように聞こえますsaltsalt、暗号化プロセスを元に戻すには、暗号文とともに保存する必要があります。追加のセキュリティは、ステップの繰り返しと分散(あいまいさ)からもたらされます。

注:これらの手順がベストプラクティスであるというコンセンサスは見つかりませんでした。

于 2012-10-09T01:06:20.073 に答える