web-dev-qa-db-ja.com

JavaとObjective-C(iPhone)の間でAES暗号化を同一にするにはどうすればよいですか?

Objective-cで文字列を暗号化し、JavaでAESを使用して同じ文字列を暗号化していますが、いくつかの奇妙な問題が発生しています。結果の最初の部分は特定のポイントまで一致しますが、その後は一致します。が異なるため、JavaからiPhoneに結果をデコードしようとすると、復号化できません。

私は「それでは、このナンセンスとは何ですか。知っていますか?」というソース文字列を使用しています。 「1234567890123456」のキーを使用

暗号化するObjective-cコードは次のとおりです。注:これはNSDataカテゴリであるため、メソッドがNSDataオブジェクトで呼び出され、「self」に暗号化するバイトデータが含まれていると想定します。

   - (NSData *)AESEncryptWithKey:(NSString *)key {
 char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
 bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

 // fetch key data
 [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

 NSUInteger dataLength = [self length];

 //See the doc: For block ciphers, the output size will always be less than or 
 //equal to the input size plus the size of one block.
 //That's why we need to add the size of one block here
 size_t bufferSize = dataLength + kCCBlockSizeAES128;
 void *buffer = malloc(bufferSize);

 size_t numBytesEncrypted = 0;
 CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
            keyPtr, kCCKeySizeAES128,
            NULL /* initialization vector (optional) */,
            [self bytes], dataLength, /* input */
            buffer, bufferSize, /* output */
            &numBytesEncrypted);
 if (cryptStatus == kCCSuccess) {
  //the returned NSData takes ownership of the buffer and will free it on deallocation
  return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
 }

 free(buffer); //free the buffer;
 return nil;
}

そして、Java暗号化コードは...

public byte[] encryptData(byte[] data, String key) {
    byte[] encrypted = null;

    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    byte[] keyBytes = key.getBytes();

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");

    try {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        encrypted = new byte[cipher.getOutputSize(data.length)];
        int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
        ctLength += cipher.doFinal(encrypted, ctLength);
    } catch (Exception e) {
        logger.log(Level.SEVERE, e.getMessage());
    } finally {
        return encrypted;
    }
}

Objective-cコードの16進出力は-

7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0

そしてJava出力は-

7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d

あなたが見ることができるようにすべてがまで大丈夫です-

7a68ea36 8288c73d f7c45d8d 22432577

いくつかの設定が異なっていると思いますが、何がうまくいかないので、Java側でECBとCBCを切り替えてみましたが、効果がありませんでした。

誰か助けてもらえますか!?お願いします....

25
Simon Lee

CCCryptはIVを取るので、連鎖ブロック暗号方式(CBCなど)を使用しませんか?これは、表示されている内容と一致します。最初のブロックは同じですが、2番目のブロックではJavaバージョンは元のキ​​ーを暗号化に適用しますが、OSXバージョンは別のものを使用しているようです。

編集:

から ここ 私は例を見ました。 kCCOptionECBModeをCCCryptに渡す必要があるようです。

ccStatus = CCCrypt(encryptOrDecrypt,
        kCCAlgorithm3DES,
        kCCOptionECBMode, <-- this could help
        vkey, //"123456789012345678901234", //key
        kCCKeySize3DES,
        nil, //"init Vec", //iv,
        vplainText, //"Your Name", //plainText,
        plainTextBufferSize,
        (void *)bufferPtr,
        bufferPtrSize,
        &movedBytes);

編集2:

コマンドラインをいじって、どれが正しいかを確認しました。私はそれを貢献できると思いました:

$ echo "Now then and what is this nonsense all about. Do you know?" | openssl enc -aes-128-ecb -K $(echo 1234567890123456 | xxd -p) -iv 0 | xxd 
0000000: 7a68 ea36 8288 c73d f7c4 5d8d 2243 2577  zh.6...=..]."C%w
0000010: e66b 32f9 772b 6679 d7c0 cb69 037b 8740  .k2.w+fy...i.{.@
0000020: 883f 8211 7482 29f4 7239 84be b50b 5aea  .?..t.).r9....Z.
0000030: eaa7 519b 65e8 fa26 a1bb de52 083b 478f  ..Q.e..&...R.;G.
18

Base64でエンコードされたAES256で暗号化された文字列を復号化するのに数週間を費やしました。暗号化はiPadのCCCrypt(Objective-C)によって行われました。復号化はJava(Bouncy Castleを使用)で行われました。

私はついに成功し、その過程でかなり多くのことを学びました。暗号化コードは上記とまったく同じでした(iPhone開発者向けドキュメントのObjective-Cサンプルから取得したと思います)。

CCCrypt()のドキュメントで言及されていないのは、デフォルトでCBCモードを使用することです(kCCOptionECBModeなどのオプションを指定しない場合)。 IVが指定されていない場合、デフォルトですべてゼロになることが記載されています(したがって、IVは0x00のバイト配列で、長さは16メンバーになります)。

これらの2つの情報を使用して、JavaとOSx/iphone/ipad(CCCrypt)の両方でCBCを使用して機能的に同一の暗号化モジュールを作成できます(安全性の低いECBの使用を回避できます)。

Cipher init関数は、IVバイト配列を3番目の引数として受け取ります。

cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV).
11
user357614

これを必要とする他の誰にとっても、disownは絶対に注目されました... Objective-cで暗号を作成するための改訂された呼び出しは次のとおりです(ECBモードとパディングが必要であることに注意してください)...

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES128,
                                          NULL /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
9
Simon Lee

最初の投稿に追加するだけです。ObjectiveC/ cocoaコードではCBCモードを使用し、JavaコードではEBCを使用し、IV初期化ベクトルも使用しませんでした。EBC暗号はブロックごとであり、前のブロックのCBCチェーンであるため、テキストが1ブロック(この例では= 16バイト)よりも小さい場合、両方によって生成された暗号文は、もう一方(同じ)によって復号化できます。

暗号の使用を標準化する方法を探している場合は、NIST Special Publication 800-38A、2001Editionにテストベクトルがあります。誰かに役立つ場合は、AESCBCおよびEBCベクトルのコードを投稿できます。

2
Nichole