Я пытаюсь сгенерировать общий секрет между веб-сервером под управлением PHP и настольным приложением C #. Мне известна библиотека BouncyCastle, но я бы предпочел не использовать ее, поскольку она довольно большая.
я использую phpecc а также ECDiffieHellmanCng и пытаюсь сгенерировать общий секрет между двумя сторонами, но у меня возникают проблемы с экспортом / импортом в C #.
Кажется, для импорта ключа phpecc требуется формат der / pem, а ECDiffieHellmanCng, похоже, не имеет простого способа экспорта в совместимый формат.
Нужно ли мне писать свой собственный кодер и декодер pem / der, чтобы сделать это, или есть какой-то другой более простой способ?
В настоящее время я делаю следующее в 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();
}
Очевидно, что вышеприведенный код выполняет только кодирование pem, поэтому на стороне php он возвращает ошибку при попытке его проанализировать:
Тип: Время выполнения
Сообщение об исключении: неверные данные.
Файл: /…/vendor/mdanter/ecc/src/Serializer/PublicKey/Der/Parser.php
Линия: 49
.NET Core 1.0 и .NET Framework 4.7 иметь ECParameters структура для импорта / экспорта ключей. ToByteArray()
метод, который вы назвали, производит CNG EccPublicBlob который имеет очень мало общего с форматом SEC-1 ECParameters.
Я собираюсь предположить, что вы хотели использовать secp384r1 / NIST P-384, даже если вы указали это в качестве алгоритма хеширования. Если вам нужна другая кривая, вам нужно будет сделать несколько переводов.
Структура (.NET) ECParameters только поможет вам начать работу. Превращение в файл требует преобразования его в структуру на основе ASN.1 в кодировке PEM DER. (Но если вы придерживаетесь NIST P-256/384/521, вы можете сделать это с помощью имеющегося у вас байта [])
В SEC 1 v2.0 мы получаем следующие структуры:
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.
Дистиллируя это до соответствующих частей, вам нужно написать
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 содержит данные, которые исправлены, если вы не меняете кривую:
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
и теперь мы можем подсчитать, сколько байтов было в полезной нагрузке: 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, вы можете сделать это за один проход :). Для secp384r1 координаты x и y представляют собой каждое 384-битное значение или (384 + 7) / 8 == 48 байт, поэтому значение ECPoint составляет 48 + 48 + 1 == 97 (0x61) байт. Затем его нужно обернуть в BIT STRING, который добавляет один байт полезной нагрузки, длину и тег. Итак, мы получаем:
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:
$ 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
Других решений пока нет …