8

私はこれまで2日間を費やし、自由に使えるすべてのソースを調べてきたので、これが最後の手段です。

公開鍵をiPhoneのキーチェーンに保存したX509証明書があります(現時点ではシミュレーターのみ)。ASP.NET側では、秘密鍵を使用して証明書ストアに証明書を取得しています。iPhoneで文字列を暗号化し、サーバーで復号化すると、CryptographicException「不正なデータ」が表示されます。Array.Reverseロングショットのページで提案されたものを試しましたRSACryptoServiceProviderが、役に立ちませんでした。

両側のbase-64ストリングを比較しましたが、それらは同じです。デコード後の生のバイト配列を比較しましたが、それらも同じです。サーバーで公開鍵を使用して暗号化すると、バイト配列はiPhoneのバージョンとは異なり、秘密鍵を使用して簡単に復号化できます。生のプレーンテキスト文字列は115文字なので、2048ビットキーの256バイトの制限内です。

iPhoneの暗号化方法は次のとおりです(CryptoExerciseサンプルアプリwrapSymmetricKey方法とほぼ同じです)。

+ (NSData *)encrypt:(NSString *)plainText usingKey:(SecKeyRef)key error:(NSError **)err
{
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    uint8_t *cipherBuffer = NULL;
    cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    memset((void *)cipherBuffer, 0x0, cipherBufferSize);
    NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding];
    OSStatus status = SecKeyEncrypt(key, kSecPaddingNone,
                                (const uint8_t *)[plainTextBytes bytes], 
                                [plainTextBytes length], cipherBuffer, 
                                &cipherBufferSize);
    if (status == noErr)
    {
        NSData *encryptedBytes = [[[NSData alloc]
                    initWithBytes:(const void *)cipherBuffer 
                    length:cipherBufferSize] autorelease];
        if (cipherBuffer)
        {
            free(cipherBuffer);
        }
        NSLog(@"Encrypted text (%d bytes): %@",
                    [encryptedBytes length], [encryptedBytes description]);
        return encryptedBytes;
    }
    else
    {
        *err = [NSError errorWithDomain:@"errorDomain" code:status userInfo:nil];
        NSLog(@"encrypt:usingKey: Error: %d", status);
        return nil;
    }
}

そして、これがサーバー側のC#復号化方法です。

private string Decrypt(string cipherText)
{
    if (clientCert == null)
    {
        // Get certificate
        var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        store.Open(OpenFlags.ReadOnly);
        foreach (var certificate in store.Certificates)
        {
            if (certificate.GetNameInfo(X509NameType.SimpleName, false) == CERT)
            {
                clientCert = certificate;
                break;
            }
        }
    }

    using (var rsa = (RSACryptoServiceProvider)clientCert.PrivateKey)
    {
        try
        {
            var encryptedBytes = Convert.FromBase64String(cipherText);
            var decryptedBytes = rsa.Decrypt(encryptedBytes, false);
            var plaintext = Encoding.UTF8.GetString(decryptedBytes);
            return plaintext;
        }
        catch (CryptographicException e)
        {
            throw(new ApplicationException("Unable to decrypt payload.", e));
        }
    }
}

私の疑いは、プラットフォーム間にいくつかのエンコーディングの問題があったことでした。1つはビッグエンディアンで、もう1つはリトルエンディアンであることは知っていますが、どちらがどちらであるか、またはどのように違いを克服するかについては十分にわかりません。Mac OS X、Windows、iPhoneはすべてリトルエンディアンなので、問題はありません。

新しい理論:OAEPパディングブール値をfalseに設定すると、デフォルトでPKCS#11.5パディングになります。、、、、およびの定義SecKeyのみがあります。おそらく、MicrosoftのPKCS#1 1.5!= AppleのPKCS1であるため、パディングは暗号化のバイナリ出力に影響を与えています。に設定して使用してみましたが、それでも動作しませんでした。SecPaddingPKCS1PKCS1MD2PKCS1MD5PKCS1SHA1kSecPaddingPKCS1fOAEPfalseどうやら、PKCS#11.5kSecPaddingPKCS1同等です。理論の設計図に戻る…

その他の新たに試みられた理論:

  1. iPhoneの証明書(.cerファイル)は、サーバーのPKCS#12バンドル(.pfxファイル)とまったく同じではないため、機能しません。別の証明書ストアに.cerファイルをインストールし、サーバーで暗号化された文字列を正常にラウンドトリップしました。
  2. base-64への変換とサーバーへのPOSTの動作により、同じクラスのラウンドトリップには存在しない奇妙な結果が生じたため、最初にURLEncoding / Decodingを試し、次にiPhoneから生のバイナリを投稿し、それが等しいことを確認して、同じ不良データを取得しました;
  3. 元の文字列は125バイトだったので、UTF-8(ロングショット)で切り捨てられる可能性があると思ったので、44バイトの文字列に切り詰めましたが結果はありませんでした。
  4. System.Cryptographyライブラリを振り返って、適切なクラスを使用していることを確認し、 `RSAPKCS1KeyExchangeDeformatter`を発見し、新しい見込み客に大喜びし、まったく同じように動作したときに落胆しました。

成功!

iPhoneシミュレーターのキーチェーンに、いわば水が濁っていたいくつかのがらくたがあったことが判明しました。でキーチェーンDBを削除して~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db再作成しましたが、正常に機能しました。よろしくお願いします。それは単純なものでしたが、自明ではなかったでしょう。(私が学んだ2つのこと:1)シミュレーターからアプリをアンインストールしても、キーチェーンエントリがクリアされないこと、および2)定期的に完全に新しく開始すること。)

注:キーチェーンファイルの一般的なパスは、iOSのバージョンによって異なります:〜/ Library / Application Support / iPhone Simulator / [version] /Library/Keychains/keychain-2-debug.db例:〜/ Library / Application Support / iPhone Simulator / 4.3 / Library / Keychains / keychain-2-debug.db

4

4 に答える 4

3

さて...最初のステップ(あなたが行ったように)は、iPhoneとC#の実装の両方を使用して、同じ初期化ベクトルで同じメッセージを暗号化することです。同じ出力が得られるはずです。あなたはそうしなかったと言ったので、問題があります。

これは、次のいずれかを意味します。

  • RSAのiPhone実装は正しくありません。
  • RSAの.NET実装が正しくありません。
  • キーファイルは異なります(または解釈が異なります)。

最初の2つはありそうもないことをお勧めしますが、リモートで可能です。

あなたは次のように述べています:「異なる証明書ストアにインストールされた.cerファイルとサーバーで暗号化された文字列はうまくラウンドトリップされました」...これは何も証明しません:これはすべて、特定のランダムな数値のセットが与えられた場合に暗号化/復号化を正常に実行できることを証明します1つのプラットフォーム。両方のプラットフォームで同じ乱数のセットが表示されることを保証するものではありません。

したがって、ここで可能な限り低いレベルに下げることをお勧めします。両方のプラットフォームで暗号化の直接(バイト配列)入力と出力を検査します。まったく同じ(バイナリ)入力で同じ出力が得られない場合は、プラットフォームに問題があります。これはありそうもないと思うので、IVが異なって解釈されていることに気付くと思います。

于 2009-07-17T20:55:09.380 に答える
1

これはstackoverflowに関する私の最初の答えですので、間違った場合はご容赦ください。

完全な答えを出すことはできませんが、PHPと統合しようとしたときに非常によく似た問題が発生しました。Appleの証明書ファイルの形式は、他のソフトウェアが期待する形式(opensslを含む)とは少し異なるようです。

PHPで暗号化された署名を復号化する方法は次のとおりです。実際には、送信された公開鍵からモジュラスとPKを手動で抽出し、鍵をインポートするのではなく、RSAのものに使用します。

// Public key format in hex (2 hex chars = 1 byte):
//30480241009b63495644db055437602b983f9a9e63d9af2540653ee91828483c7e302348760994e88097d223b048e42f561046c602405683524f00b4cd3eec7e67259c47e90203010001
//<IGNORE><--------------------------------------------- MODULUS --------------------------------------------------------------------------><??>< PK > 
// We're interested in the modulus and the public key.
// PK = Public key, probably 65537

// First, generate the sha1 of the hash string:
$sha1 = sha1($hashString,true);

// Unencode the user's public Key:
$pkstr = base64_decode($publicKey);
// Skip the <IGNORE> section:
$a = 4;
// Find the very last occurrence of \x02\x03 which seperates the modulus from the PK:
$d = strrpos($pkstr,"\x02\x03");
// If something went wrong, give up:
if ($a == false || $d == false) return false;
// Extract the modulus and public key:
$modulus = substr($pkstr,$a,($d-$a));
$pk = substr($pkstr,$d+2);

// 1) Take the $signature from the user
// 2) Decode it from base64 to binary
// 3) Convert the binary $pk and $modulus into (very large!) integers (stored in strings in PHP)
// 4) Run rsa_verify, from http://www.edsko.net/misc/rsa.php
$unencoded_signature = rsa_verify(base64_decode($signature), binary_to_number($pk), binary_to_number($modulus), "512");

//Finally, does the $sha1 we calculated match the $unencoded_signature (less any padding bytes on the end)?
return ($sha1 == substr($unencoded_signature,-20)); // SHA1 is only 20 bytes, whilst signature is longer than this.  

この公開鍵を生成するObjective-cは次のとおりです。

NSData * data = [[SecKeyWrapper sharedWrapper] getPublicKeyBits];
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Public-Key"];
data = [[SecKeyWrapper sharedWrapper] getSignatureBytes:[signatureData dataUsingEncoding:NSUTF8StringEncoding]];
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Signature"];

AppleのサンプルプロジェクトCryptoExerciseのSecKeyWrapperを使用します(ファイルはここで表示できます:https ://developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html )

これがお役に立てば幸いです。

于 2009-07-20T10:15:07.963 に答える
0

これはあなたを助けますか?

.NETおよびC#を使用した非対称鍵暗号化

  • 短い投稿、時間の制約、その他すべてについて申し訳ありません。とにかく、あなたのツイッターの助けを求めるリクエストを見ました。これは、私がPHPでこれを行い、.NETで復号化した方法を示しています。あなたの復号化クラスは私のものとは少し違うので、この記事が役立つかもしれません。
于 2009-07-17T17:07:21.680 に答える
-2

あなたは自分で質問に答えたと思います。問題は間違いなくエンディアンの中にあります。

これは、双方向の変換メソッドを作成するための可能な方法です。

short convert_short(short in)
{
 short out;
 char *p_in = (char *) &in;
 char *p_out = (char *) &out;
 p_out[0] = p_in[1];
 p_out[1] = p_in[0];  
 return out;
}

long convert_long(long in)
{
 long out;
 char *p_in = (char *) &in;
 char *p_out = (char *) &out;
 p_out[0] = p_in[3];
 p_out[1] = p_in[2];
 p_out[2] = p_in[1];
 p_out[3] = p_in[0];  
 return out;
} 

これはあなたにとって良いリソースかもしれません(ウィキペディア以外): http: //betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

于 2009-07-15T22:26:48.817 に答える