2017-05-09 6 views
0

J'essaie de générer un secret partagé entre un serveur Web exécutant PHP et une application de bureau C#. Je suis au courant de la bibliothèque BouncyCastle, mais je préférerais ne pas l'utiliser car c'est assez énorme. J'utilise phpecc et ECDiffieHellmanCng et tente de générer un secret partagé entre les deux parties, mais j'ai des problèmes avec l'exportation/importation en C#.C# et PHP ECDH ne correspondent pas

Il semble que phpecc nécessite un format der/pem pour importer une clé, et ECDiffieHellmanCng ne semble pas avoir un moyen facile d'exporter dans un format compatible. Est-ce que je devrais écrire mon propre codeur/décodeur de pem/der pour faire ceci ou est-il une certaine manière plus facile de rechange?

Actuellement, je fais ce qui suit dans C#:

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(); 
    } 

Il est évident que le fait ci-dessus est que le codage pem, donc du côté php il renvoie une erreur quand il essaie de l'analyser:

Type: Runtime

Message d'exception: Données non valides.

fichier: /.../vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php

Ligne: 49

Répondre

0

.NET 1.0 et base .NET Framework 4.7 ont le ECParameters struct pour importer/exporter des clés. La méthode ToByteArray() que vous avez appelée génère un CNG EccPublicBlob qui a très peu à voir avec le format ECParameters SEC-1.

Je vais supposer que vous vouliez utiliser secp384r1/NIST P-384, même si vous l'avez spécifié comme algorithme de hachage. Si vous voulez une autre courbe, vous devrez faire quelques traductions.

La structure (.NET) ECParameters vous aidera uniquement à démarrer. Transformer cela en un fichier nécessite de le traduire en une structure basée sur ASN.1 codée en PEM et codée en PEM.(Mais si vous coller avec NIST P-256/384/521, vous pouvez le faire avec l'octet [] que vous avez actuellement)

En SEC 1 v2.0 nous obtenons les structures suivantes:

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. 

Distillation cette baisse aux parties pertinentes, vous devez écrire

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 

AlgorithmIdentifier contient des données qui vous a donné fixe ne change pas la courbe:

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 

et nous pouvons maintenant compter combien d'octets sont dans la charge utile: 16 (0x10), donc nous remplissons la longueur:

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

La clé publique de codage que tout le monde comprend est « point non compressé », ce qui est

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

Il s'avère, qui a une taille fixe pour une courbe donnée, aussi, donc contrairement à la plupart des choses qui sont DER encodée, vous pouvez le faire en une seule passe :). Pour secp384r1 les coordonnées x et y sont chacune de 384 bits, ou (384 + 7)/8 == 48 octets, donc le point ECPoint est 48 + 48 + 1 == 97 (0x61) octets. Ensuite, il doit être enveloppé dans un STRING BIT, qui ajoute un octet de charge utile et la longueur et l'étiquette. Donc, nous obtenons:

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()); 
} 

L'exécution de la sortie, je suis arrivé de cela dans OpenSSL:

$ 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