5

結果が同じでなければならない場合、Javaとphpでクリアテキストをエンコードする必要があります。

次の条件が与えられます。

  1. アルゴリズム: RIJNDAEL-128
  2. キー: 1234567890123456
  3. モード: cfb
  4. 初期化ベクトル: 1234567890123456

次のコードは機能し、最初の要件と 2 番目の要件を満たしますが、ECB をモードとして使用するため、初期化ベクトルを使用しません。

PHP:

 <?php  
        $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');     
        $cleartext = 'abcdefghijklmnop';    
        $key128 = '1234567890123456';
        $iv = '1234567890123456';

        if (mcrypt_generic_init($cipher, $key128, $iv) != -1)  //Parameter iv will be ignored in ECB mode
        {
            $cipherText = mcrypt_generic($cipher,$cleartext );
            mcrypt_generic_deinit($cipher);     
            printf(bin2hex($cipherText));       
        }
    ?>

出力: fcad715bd73b5cb0488f840f3bad7889

ジャワ:

public class AES {

    public static void main(String[] args) throws Exception {
        String cleartext = "abcdefghijklmnop";
        String key = "1234567890123456";
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(cleartext.getBytes());
        System.out.println(asHex(encrypted));
    }

    public static String asHex(byte buf[]) {
        StringBuffer strbuf = new StringBuffer(buf.length * 2);
        int i;
        for (i = 0; i < buf.length; i++) {
            if (((int) buf[i] & 0xff) < 0x10)
                strbuf.append("0");
            strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
        }
        return strbuf.toString();
    }

}

出力は次のとおりです (PHP バージョンと同じ): fcad715bd73b5cb0488f840f3bad7889

要件 3 と 4 を満たすために、コードが次のようになるように、PHP バージョンでモードを MCRYPT_MODE_CFB に変更しました。

 <?php  
        $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CFB, '');     
        $cleartext = 'abcdefghijklmnop';    
        $key128 = '1234567890123456';
        $iv = '1234567890123456';


        if (mcrypt_generic_init($cipher, $key128, $iv) != -1)  //Parameter iv will be ignored in ECB mode
        {
            $cipherText = mcrypt_generic($cipher,$cleartext );
            mcrypt_generic_deinit($cipher);     
            printf(bin2hex($cipherText));       
        }
    ?>

これにより、次の出力が得られます: 14a53328feee801b3ee67b2fd627fea0

JAVA バージョンでもモードを適応させ、iv を Cipher オブジェクトの init 関数に追加しました。

public class AES {

    public static void main(String[] args) throws Exception {
        String cleartext = "abcdefghijklmnop";
        String key = "1234567890123456";
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec,  new IvParameterSpec("1234567890123456".getBytes()));
        byte[] encrypted = cipher.doFinal(cleartext.getBytes());
        System.out.println(asHex(encrypted));
    }

    public static String asHex(byte buf[]) {
        StringBuffer strbuf = new StringBuffer(buf.length * 2);
        int i;
        for (i = 0; i < buf.length; i++) {
            if (((int) buf[i] & 0xff) < 0x10)
                strbuf.append("0");
            strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
        }
        return strbuf.toString();
    }

}

しかし、ここでの出力は 141eae68b93af782b284879a55b36f70 で、PHP バージョンとは異なります。

JAVA と PHP のバージョンの違いを知っている人はいますか?

4

2 に答える 2

8

十分に文書化されていませんが、PHP のMCRYPT_RIJNDAEL_128withMCRYPT_MODE_CFBは Java の と一貫した結果を生成しますAES/CFB8/NoPadding

したがって、PHP の次の行:

$encrypted = base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CFB, $iv ) );

Java では次のブロックに一致します。

SecretKeySpec   key = new SecretKeySpec(KEY.getBytes(), "AES");
IvParameterSpec iv  = new IvParameterSpec(IV.getBytes());

Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);

byte[] output = cipher.doFinal(cleartext.getBytes());

String signature = Base64.encode(output);
于 2012-05-22T16:03:33.427 に答える
3

ここで 3 つのこと:

  • PHP の「MCRYPT_RIJNDAEL_128」は、Java の「AES」とまったく同じアルゴリズムではない可能性が非常に高いです。AES Wiki エントリでは、イントロの下部で RIJNDAEL と AESの違いについて説明しています。

  • PHP 版では CBC を使用していますが、Java 版では CFB を使用しています。アルゴリズムが同じであっても、これにより確実に異なる出力が得られます。

  • PHP バージョンにはパディングがありませんが、Java バージョンは PKCS5Padding を使用しています。Java バージョンは、「Cipher.getInstance("AES/CFB/NoPadding");」で暗号をインスタンス化する必要があります。

また、キー文字列のバイトを使用して SecretKeySpec を構築する代わりに、実際に AES キーを生成する必要があります。これは次のようになります。

KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sec = new SecureRandom(key.getBytes());
keygen.init(128, sec);
Key key = keygen.generateKey();
SecretKeySpec skeySpec = new SecretKeySpec(key.getEncoded(), "AES");
...

基本的に、文字列キーは、キー自体ではなく、SecretKey を生成するためのシードです。

于 2012-04-13T07:12:18.877 に答える