2017-05-09 12 views
0

PHPを実行しているWebサーバーとC#デスクトップアプリケーションの間で共有秘密情報を生成しようとしています。私はBouncyCastleライブラリを知っていますが、かなり巨大なので、使用する必要はありません。C#とPHP ECDHが一致しない

私はphpeccECDiffieHellmanCngを使用していますが、両者の間で共有秘密情報を生成しようとしていますが、C#でエクスポート/インポートに問題があります。

キーをインポートするにはphpeccがder/pem形式を必要とし、ECDiffieHellmanCngは互換性のある形式でエクスポートする簡単な方法がないようです。

これを行うには、独自のpem/derエンコーダとデコーダを作成する必要がありますか、それとも簡単な方法がありますか?

は現在、私はC#で次のようにやっている:それを解析しようとしているとき、それはエラーを返し、PHP側のように、明らかに

using (var ecdh = new ECDiffieHellmanCng()) 
     { 
      ecdh.HashAlgorithm = CngAlgorithm.ECDiffieHellmanP384; 
      ecdh.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; 

      var encoded = EncodePem(ecdh.PublicKey.ToByteArray()); 
      //... do something with encoded 
     } 

private static string EncodePem(byte[] data) 
    { 
     var pemDat = new StringBuilder(); 
     var chunk = new char[64]; 

     pemDat.AppendLine("-----BEGIN PUBLIC KEY-----"); 

     var encodedData = Convert.ToBase64String(data); 
     for (var i = 0; i < encodedData.Length; i += chunk.Length) 
     { 
      var index = 0; 
      while (index != chunk.Length && i + index < encodedData.Length) 
      { 
       chunk[index] = encodedData[i + index]; 
       index++; 
      } 
      pemDat.AppendLine(new string(chunk)); 
     } 

     pemDat.AppendLine("-----END PUBLIC KEY-----"); 
     return pemDat.ToString(); 
    } 

上記のみ、PEMエンコードをやっている:

タイプ:ランタイム

例外メッセージ:無効なデータです。

ファイル:/.../vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php

ライン:49

答えて

0

.NETのコア1.0と.NET Framework 4.7ECParameters持っていますキーをインポート/エクスポートする構造体。 ToByteArray()メソッドを呼び出すとCNG EccPublicBlobが生成され、SEC-1 ECParametersフォーマットとはほとんど関係がありません。

ハッシュアルゴリズムとして指定したにもかかわらず、secp384r1/NIST P-384を使用すると仮定します。他のカーブが必要な場合は、いくつかの翻訳を行う必要があります。

(.NET)ECParameters構造体は、開始に役立ちます。これをファイルに変換するには、PEMでエンコードされたDERでエンコードされたASN.1ベースの構造に変換する必要があります。

SubjectPublicKeyInfo ::= SEQUENCE { 
    algorithm AlgorithmIdentifier {{ECPKAlgorithms}} (WITH COMPONENTS {algorithm, parameters}), 
    subjectPublicKey BIT STRING 
} 

ECPKAlgorithms ALGORITHM ::= { 
    ecPublicKeyType | 
    ecPublicKeyTypeRestricted | 
    ecPublicKeyTypeSupplemented | 
    {OID ecdh PARMS ECDomainParameters {{SECGCurveNames}}} | 
    {OID ecmqv PARMS ECDomainParameters {{SECGCurveNames}}}, 
    ... 
} 

ecPublicKeyType ALGORITHM ::= { 
    OID id-ecPublicKey PARMS ECDomainParameters {{SECGCurveNames}} 
} 

ECDomainParameters{ECDOMAIN:IOSet} ::= CHOICE { 
    specified SpecifiedECDomain, 
    named ECDOMAIN.&id({IOSet}), 
    implicitCA NULL 
} 

An elliptic curve point itself is represented by the following type 
    ECPoint ::= OCTET STRING 
whose value is the octet string obtained from the conversion routines given in Section 2.3.3. 

蒸留:私たちは以下の構造を得るSEC 1 v2.0

(あなたはNIST P-256/521分の384にこだわっている場合でも、あなたはバイト[]あなたが現在持っているとそれを行うことができます)

SEQUENCE (AlgorithmIdentifier) 
30 xx [yy [zz]] 
    OBJECT IDENTIFIER id-ecPublicKey (1.2.840.10045.2.1) 
    06 07 2A 86 48 CE 3D 02 01 
    OBJECT IDENTIFIER secp384r1 (1.3.132.0.34) 
    06 05 2B 81 04 00 22 
:関連部分には、このダウンは、あなたAlgorithmIdentifierはあなたがカーブを変更しないある一定のデータが含まれてい

SEQUENCE (SubjectPublicKeyInfo) 
    SEQUENCE (AlgorithmIdentifier) 
    OBJECT IDENTIFIER id-ecPublicKey 
    OBJECT IDENTIFIER secp384r1 (or whatever named curve you're using) 
    BIT STRING 
    public key encoded as ECPoint 

を作成する必要があります

、私たちは今、ペイロードにあったバイト数を数えることができます:16(0x10を)ので、私たちは、長さを埋める:

30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 
00 22 

誰もが理解し、公開鍵暗号は、「非圧縮ポイント」、です

04 th eb yt es of x. th eb yt es of y. 

は、DERでエンコードされているほとんどのものとは異なり、1つのパス:)でこれを行うことができますので、あまりにも、与えられた曲線のための固定サイズを持っていること、が判明しました。 secp384r1の場合、x座標とy座標はそれぞれ384ビット値、つまり(384 + 7)/ 8 == 48バイトであるため、ECPointは48 + 48 + 1 == 97(0x61)バイトです。次に、1つのペイロードバイトと長さとタグを追加するBIT STRINGにラップする必要があります。そこで、我々が得る:

$ openssl ec -pubin -text -noout 
read EC key 
(paste) 
-----BEGIN PUBLIC KEY----- 
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEwpbxYmcsNvr14D8k+0VQCkSY4WCV/3V10AiIq7sFdmUX 
9+0DMuuLDmcKjL1ZFEFk0yHCPpY+pdkYtzPwE+dsApCPT3Ljk0AxHQBTSo4yjwsElMoA4Mtp8Qdo 
LZD1Nx6v 
-----END PUBLIC KEY----- 
Private-Key: (384 bit) 
pub: 
    04:c2:96:f1:62:67:2c:36:fa:f5:e0:3f:24:fb:45: 
    50:0a:44:98:e1:60:95:ff:75:75:d0:08:88:ab:bb: 
    05:76:65:17:f7:ed:03:32:eb:8b:0e:67:0a:8c:bd: 
    59:14:41:64:d3:21:c2:3e:96:3e:a5:d9:18:b7:33: 
    f0:13:e7:6c:02:90:8f:4f:72:e3:93:40:31:1d:00: 
    53:4a:8e:32:8f:0b:04:94:ca:00:e0:cb:69:f1:07: 
    68:2d:90:f5:37:1e:af 
ASN1 OID: secp384r1 
NIST CURVE: P-384 

private static byte[] s_secp384r1PublicPrefix = { 
    // SEQUENCE (SubjectPublicKeyInfo, 0x76 bytes) 
    0x30, 0x76, 
    // SEQUENCE (AlgorithmIdentifier, 0x10 bytes) 
    0x30, 0x10, 
    // OBJECT IDENTIFIER (id-ecPublicKey) 
    0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 
    // OBJECT IDENTIFIER (secp384r1) 
    0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 
    // BIT STRING, 0x61 content bytes, 0 unused bits. 
    0x03, 0x62, 0x00, 
    // Uncompressed EC point 
    0x04, 
} 

... 

using (ECDiffieHellman ecdh = ECDiffieHellman.Create()) 
{ 
    ecdh.KeySize = 384; 

    byte[] prefix = s_secp384r1PublicPrefix; 
    byte[] derPublicKey = new byte[120]; 
    Buffer.BlockCopy(prefix, 0, derPublicKey, 0, prefix.Length); 

    byte[] cngBlob = ecdh.PublicKey.ToByteArray(); 
    Debug.Assert(cngBlob.Length == 104); 

    Buffer.BlockCopy(cngBlob, 8, derPublicKey, prefix.Length, cngBlob.Length - 8); 

    // Now move it to PEM 
    StringBuilder builder = new StringBuilder(); 
    builder.AppendLine("-----BEGIN PUBLIC KEY-----"); 
    builder.AppendLine(
     Convert.ToBase64String(derPublicKey, Base64FormattingOptions.InsertLineBreaks)); 
    builder.AppendLine("-----END PUBLIC KEY-----"); 

    Console.WriteLine(builder.ToString()); 
} 

は、私には、OpenSSLにそこから得た出力を実行します

関連する問題