2

私が解決しようとしている状況: 私の Cocoa アプリでは、対称暗号で文字列を暗号化し、それを PHP に POST し、そのスクリプトでデータをデコードする必要があります。回答を返すには、このプロセスを逆に実行する必要があります (PHP エンコード、Cocoa デコード)。

PHP と Cocoa の両方でキーと初期化ベクトル (iv) の両方を同じにすることはできますが、一方のアプリがエンコードされたデータを他方に送信すると、デコードが機能しないため、何かが欠けています。どちらも独自のデータのエンコード/デコードに問題なく機能します (手元に PEBKAC の問題がないことを確認するために検証済み)。どこかにパディングの問題があるのではないかと疑っていますが、見当たりません。

私のココア アプリは、SSCrypto を使用してエンコードします (これは、OpenSSL 関数の便利なラッパーです)。暗号は Blowfish、モードは CBC です。(メモリリークを許してください、コードは必要最小限に取り除かれています)

NSData *secretText = [@"secretTextToEncode" dataUsingEncoding:NSUTF8StringEncoding];
NSData *symmetricKey = [@"ThisIsMyKey" dataUsingEncoding:NSUTF8StringEncoding];

unsigned char *input = (unsigned char *)[secretText bytes];
unsigned char *outbuf;
int outlen, templen, inlen;
inlen = [secretText length];

unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"};
int cipherMaxIVLength = EVP_MAX_IV_LENGTH;
EVP_CIPHER_CTX cCtx;
const EVP_CIPHER *cipher = EVP_bf_cbc();

cipherMaxIVLength = EVP_CIPHER_iv_length( cipher );
unsigned char iv[cipherMaxIVLength];

EVP_BytesToKey(cipher, EVP_md5(), NULL, [symmetricKey bytes], [symmetricKey length], 1, evp_key, iv);

NSData *initVector = [NSData dataWithBytes:iv length:cipherMaxIVLength];

EVP_CIPHER_CTX_init(&cCtx);

if (!EVP_EncryptInit_ex(&cCtx, cipher, NULL, evp_key, iv)) {
    EVP_CIPHER_CTX_cleanup(&cCtx);
    return nil;
}
int ctx_CipherKeyLength = EVP_CIPHER_CTX_key_length( &cCtx );
EVP_CIPHER_CTX_set_key_length(&cCtx, ctx_CipherKeyLength);

outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx), sizeof(unsigned char));

if (!EVP_EncryptUpdate(&cCtx, outbuf, &outlen, input, inlen)){
    EVP_CIPHER_CTX_cleanup(&cCtx);
    return nil;
}
if (!EVP_EncryptFinal(&cCtx, outbuf + outlen, &templen)){
    EVP_CIPHER_CTX_cleanup(&cCtx);
    return nil;
}
outlen += templen;
EVP_CIPHER_CTX_cleanup(&cCtx);

NSData *cipherText = [NSData dataWithBytes:outbuf length:outlen];

NSString *base64String = [cipherText encodeBase64WithNewlines:NO];
NSString *iv = [initVector encodeBase64WithNewlines:NO];

base64String と iv は、それをデコードしようとする PHP に POST されます。

<?php

import_request_variables( "p", "p_" );

if( $p_data != "" && $p_iv != "" )
{
    $encodedData = base64_decode( $p_data, true );
    $iv = base64_decode( $p_iv, true );

    $td = mcrypt_module_open( MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '' );
    $keySize = mcrypt_enc_get_key_size( $td );
    $key = substr( md5( "ThisIsMyKey" ), 0, $keySize );

    $decodedData = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encodedData, MCRYPT_MODE_CBC, $iv );
    mcrypt_module_close( $td );

    echo "decoded: " . $decodedData;
}
?>

decodedData は常に意味不明です。

プロセスを逆にして、エンコードされた出力を PHP から Cocoa に送信しようとしましたが、EVP_DecryptFinal() が失敗したため、どこかに NULL パディングの問題があると思われます。PHP と OpenSSL のドキュメントを読んで読み直しましたが、今ではすべてがぼやけていて、試してみるアイデアがありません。

4

2 に答える 2

3

あなたの問題は、キー文字列から生の暗号化キーを導出する方法が両側で異なることだと思います。EVP_BytesToKey() は生のバイト文字列を返すかなり複雑なハッシュ ルーチンですが、php md5() 関数は 16 進数の文字列、つまり 'a476c3...' をキー サイズに切り詰めて返します。提供されたパラメーターを使用すると、生の MD5 ハッシュに単純化される可能性がありますが、実際にはわかりません。いずれにせよ、php ハッシュとは異なるものになります。

php を md5( "ThisIsMyKey", TRUE ) に変更すると、生の md5 ハッシュが得られます。ココア側では、SSCrypto の +getMD5ForData: メソッドは、同じ文字列に対して同じものを生成する必要があります (テキスト エンコーディングの問題は別として)。

編集 1: PHP 文字列と Cocoa データが同じように出力される場合でも、バイト レベルではまだ異なります。php 文字列は 16 進数でエンコードされます (つまり、0 ~ 9 と af の文字のみで構成されます)。一方、cocoa データは生のバイトです (ただし、NSData は NSLogged のときにその内容の 16 進数でエンコードされた文字列を出力します)。生のバイト文字列を取得するには、php の md5() 関数に 2 番目の TRUE パラメータを追加する必要があります。

編集 2: OpenSSL 1.1.0c は、一部の内部コンポーネントで使用されるダイジェスト アルゴリズムを変更しました。以前は MD5 を使用していましたが、1.1.0 で SHA256 に切り替わりました。EVP_BytesToKey変更が と のようなコマンドの両方に影響を与えないように注意してくださいopenssl enc

于 2008-11-26T17:58:31.357 に答える
1

私は自分の問題を理解しました。簡単な答え: 使用されているキーは、Cocoa と PHP で異なる長さでした。長い答え...

私の最初の問い合わせは、16 バイトから 56 バイトまでの可変鍵長暗号である Blowfish/CBC を使用することでした。何らかの理由で鍵が原因であるという Boaz の考えから外れて、24 の固定鍵長を使用するため、暗号を TripleDES に切り替えました。バイト。Cocoa/EVP_BytesToKey() によって返されるキーの長さは 24 バイトでしたが、キーをハッシュする md5() によって返される値は 16 バイトしかありませんでした。

この問題の解決策はEVP_BytesToKey、出力の長さが少なくとも (cipherKeyLength + cipherIVLength) になるまで、PHP に同じ方法で鍵を作成させることでした。次の PHP はまさにそれを行います (salt または count の反復を無視します)。

$cipher = MCRYPT_TRIPLEDES;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = mcrypt_get_key_size( $cipher, $cipherMode );
$ivSize    = mcrypt_get_iv_size( $cipher, $cipherMode );

$rawKey = "ThisIsMyKey";
$genKeyData = '';
do
{
    $genKeyData = $genKeyData.md5( $genKeyData.$rawKey, true );
} while( strlen( $genKeyData ) < ($keySize + $ivSize) );

$generatedKey = substr( $genKeyData, 0, $keySize );
$generatedIV  = substr( $genKeyData, $keySize, $ivSize );

$output = mcrypt_decrypt( $cipher, $generatedKey, $encodedData, $cipherMode, $generatedIV );

echo "output (hex)" . bin2hex($output);

その出力の最後に PKCS#5 パディングがある可能性が高いことに注意してください。上記のパディングの追加と削除については、 http: //us3.php.net/manual/en/ref.mcrypt.phpのコメントを確認してください。pkcs5_padpkcs5_unpad

基本的に、キーの生の md5 値を取得し、それが十分な長さでない場合は、キーを md5 の結果に追加し、その文字列を再度 md5 します。洗って、すすいで、繰り返します。EVP_BytesToKey() のマニュアル ページでは、実際に何を行っているかを説明し、必要に応じてソルト値を配置する場所を示しています。キーを再生成するこの方法では、初期化ベクトル (iv) も正しく再生成されるため、それを渡す必要はありません。

しかし、フグはどうですか?

EVP_BytesToKey()キーサイズのベースとなるコンテキストを受け入れないため、暗号で可能な最小のキーを返します。したがって、デフォルトのサイズは取得できるすべてであり、Blowfish の場合は 16 バイトです。 mcrypt_get_key_size()一方、可能な最大の鍵サイズを返します。したがって、元のコードの次の行:

$keySize = mcrypt_enc_get_key_size( $td );
$key = substr( md5( "ThisIsMyKey" ), 0, $keySize );

$keySize が 56 に設定されているため、常に 32 文字のキーが返されます。上記のコードを次のように変更します。

$cipher = MCRYPT_BLOWFISH;
$cipherMode = MCRYPT_MODE_CBC;

$keySize   = 16;

フグが適切にデコードできるようにしますが、可変長キーの利点をほとんど台無しにします。要約すると、EVP_BytesToKey()可変キー長暗号に関しては壊れています。可変鍵暗号を使用する場合は、別の方法で key/iv を作成する必要があります。3DES は私が必要としているものに対して機能するため、あまり詳しく説明しませんでした。

于 2008-12-01T18:27:00.897 に答える