web-dev-qa-db-ja.com

モバイルアプリとWebサービスの間でIVを使用して暗号化/復号化システムを実装する方法

私のアプリでは、暗号化されたデータをWebサービスに送信します。次のコードを使用してデータを暗号化および復号化しています-

public class DecEn {
    private static final String password = "test";
    private static String salt;
    private static int pswdIterations = 65536;
    private static int keySize = 256;
    private byte[] ivBytes;

    public String encrypt(String plainText) throws Exception {

        //get salt
        salt = generateSalt();
        byte[] saltBytes = salt.getBytes("UTF-8");

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(
                password.toCharArray(),
                saltBytes,
                pswdIterations,
                keySize
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        //encrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        return new Base64().encodeAsString(encryptedTextBytes);
//      return Base64.encodeBase64String(encryptedTextBytes);
//      return Hex.encodeHexString(encryptedTextBytes);
    }

    @SuppressWarnings("static-access")
    public String decrypt(String encryptedText) throws Exception {

        byte[] saltBytes = salt.getBytes("UTF-8");
        byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);
//      byte[] encryptedTextBytes = Base64.decodeBase64(encryptedText);
//      byte[] encryptedTextBytes = Hex.decodeHex(encryptedText.toCharArray());

        // Derive the key
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(
                password.toCharArray(),
                saltBytes,
                pswdIterations,
                keySize
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

        // Decrypt the message
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));


        byte[] decryptedTextBytes = null;
        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);
    }

    public String generateSalt() {
        SecureRandom random = new SecureRandom();
        byte bytes[] = new byte[20];
        random.nextBytes(bytes);
        String s = new String(bytes);
        return s;
    }
}

上記のコードは、生成されたIVとソルトを格納し、それらを再利用してデータを復号化します。これは、アプリとWebサービス間のコラボレーションの場合には当てはまりません。

私が実際に混乱しているのは、データが暗号化されるたびにIVとソルトが新しく生成されるということです。これは、私のグーグルからわかる限りでは推奨される手順ですが、このデータはWebサービスに送信されているため、IVとソルトそこで利用できる必要があります。つまり、データと一緒に送信する必要があります。これは暗号化の目的に反しませんか?攻撃者が情報を利用できるようにするため。

または、静的IVとソルトを使用する方法も考えられます。これにより、暗号化が攻撃を受けやすくなります。暗号化が世界中で成功裏に使用されていることは、この件に関する私の理解がどこかに欠けていることを示しています。

疑問を片付けて助けてください。

3
Opax Web

初期化ベクトル はプライベートである必要はありません。 IVの目標は、キーを強化することではなく、2つの同一(または類似)のクリアテキストが同一(または類似)の暗号化テキストにならないようにすることです。

これは次のように機能します。 [〜#〜] aes [〜#〜] (最新の暗号アルゴリズムのほとんど-すべてではない-)はブロック暗号です。一定量のデータのみを操作します(AESの場合は 128ビット )。したがって、ソースデータ内の同一のデータブロックを同一にする(つまり、同じキーを使用する)ために、2つのデータブロック間に何らかの変更を導入する 操作モード を指定するのが通常です。

あなたのケースでは、 [〜#〜] cbc [〜#〜] を選択しています。これは基本的にXORブロックNのクリアテキストと暗号化する前にN-1をブロックします。

これは最初のブロックを除いてうまく機能します:それと混合する前のブロックがないため、それについて何もしない場合、クリアテキストが同じ128ビットで始まる場合、同じキーが使用されると同様になります暗号化のため。 2つのメッセージが同じ最初の128ビットを持つことは非常に頻繁であるため、これは実際の問題です。さらに、アプリケーションによっては、メッセージを解読することなくメッセージの意味を推測することができます(たとえば、「はい」と「いいえ」の答えを考えてみてください)。

IVの目的:IVを再利用しない限り、2つのメッセージが同じクリアテキストを生成することを心配する必要がなくなります。

もう1つの重要なポイントは、キーを変更しても同じことができるということです。ここには2つの問題があります。

  • キーは頻繁にその場で取得されるため、統計的に一意であるだけです(これは問題ではありません)。
  • キーの変更にはコストがかかります。せいぜい、キーを再度関連付ける必要があります(たとえば、 [〜#〜] dh [〜#〜] を使用)。価値があるのは、新しいキーを再作成して、安全なチャネルを介して交換することです。
3
Stephane