Импорт CngKey из открытого ключа ECSsaP192

Я работаю над проверкой подписи, открытый ключ которой указан как MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEMyHD625uvsmGq4C43cQ9BnfN2xslVT5V1nOmAMP6qaRRUll3PB1JYmgSm+62sosG

После долгих исследований я думаю, что это стандартный ключ ECDsaP192 (поправьте меня, если я ошибаюсь). Таким образом, поломка ключа будет

      30 13
        06 07 2A 86 48 CE 3D 02 01
        06 08 2A 86 48 CE 3D 03 01 01
    03 32 00
    04
    33 21 C3 EB 6E 6E BE C9 86 AB 80 B8 DD C4 3D 6 77 CD DB 1B 25 55 3E 55  // Qx, 24 bytes
    D6 73 A6 0 C3 FA A9 A4 51 52 59 77 3C 1D 49 62 68 12 9B EE B6 B2 8B 6   // Qy, 24 bytes

Я видел пример secp256r1 ключ, который очень похож на мой случай, но все же я не мог заставить его работать на меня. Мой код:

private static readonly byte[] p192r1Prefix =
    Convert.FromBase64String("MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAE");

private static readonly byte[] s_cngBlobPrefix = { 0x45, 0x43, 0x53, 0x31, 0x18, 0, 0, 0 };

void Main()
{
    var pubkey = @"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEMyHD625uvsmGq4C43cQ9BnfN2xslVT5V1nOmAMP6qaRRUll3PB1JYmgSm+62sosG";
    var key = ImportECDsa256PublicKey(pubkey);
}

private static CngKey ImportECDsa256PublicKey(string base64)
{
    byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64);
    byte[] prefix = p192r1Prefix;

    byte[] cngBlob = new byte[s_cngBlobPrefix.Length + 48];
    Buffer.BlockCopy(s_cngBlobPrefix, 0, cngBlob, 0, s_cngBlobPrefix.Length);

    Buffer.BlockCopy(
        subjectPublicKeyInfo,
        p192r1Prefix.Length,
        cngBlob,
        s_cngBlobPrefix.Length,
        48);

    return CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob);  // Error: The parameter is incorrect.
}

Редактировать: Использование BouncyCastle

void Main()
{
    // Documentation https://developer.apple.com/documentation/storekit/skadnetwork/verifying_an_install_validation_postback
    var applePublicKey = @"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEMyHD625uvsmGq4C43cQ9BnfN2xslVT5V1nOmAMP6qaRRUll3PB1JYmgSm+62sosG";
    var keyBytes = Convert.FromBase64String(applePublicKey);
    var param = GetPublicKeyParam(keyBytes);
    
    var dataStr = "2.0" + '\u2063' + "com.example" + '\u2063' + "42" + '\u2063' + "525463029" + '\u2063' + "6aafb7a5-0170-41b5-bbe4-fe71dedf1e28" + '\u2063' + "1" + '\u2063' + "1234567891";
    var data = Encoding.UTF8.GetBytes(dataStr);
    var signature = "MDYCGQCsQ4y8d4BlYU9b8Qb9BPWPi+ixk/OiRysCGQDZZ8fpJnuqs9my8iSQVbJO/oU1AXUROYU=";
    var sigBytes = Convert.FromBase64String(signature);
    
    ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
    signer.Init(false, param);
    signer.BlockUpdate(data, 0, dataStr.Length);
    Console.WriteLine(signer.VerifySignature(sigBytes));
}
private ECPublicKeyParameters GetPublicKeyParam(byte[] publicKeyBytes)
{
    // parse based on asn1 format the content of the certificate
    var asn1 = (Asn1Sequence)Asn1Object.FromByteArray(publicKeyBytes);
    var at1 = (DerBitString)asn1[1];
    var xyBytes = at1.GetBytes();
    //retrieve preddefined parameters for P192(?) curve
    X9ECParameters x9 = NistNamedCurves.GetByName("P-192");
    //establish domain we will be looking for the x and y
    ECDomainParameters domainParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
    ECPublicKeyParameters publicKeyParams = new ECPublicKeyParameters(x9.Curve.DecodePoint(xyBytes), domainParams);
    return publicKeyParams;
}

person Emile    schedule 27.08.2020    source источник
comment
Какую версию .NET вы используете?   -  person user 9014097    schedule 27.08.2020
comment
@Topaco Я использую 4.7.2   -  person Emile    schedule 27.08.2020
comment
0x45, 0x43, 0x53, 0x31 — это магический номер, который идентифицирует NIST P-256 (он же secp256r1), здесь, а не ваша кривая. Для .NET Framework возможен BouncyCastle. Какой формат имеет подпись (r|s или ASN.1) или какой длины? В идеале вы могли бы опубликовать тестовые данные (открытый текст и подпись), которые можно проверить с помощью опубликованного открытого ключа. Какой дайджест использовался для подписи SHA-256?   -  person user 9014097    schedule 28.08.2020
comment
@ Topaco Я не смог найти магическое число для P192, поэтому я использовал 0x45, 0x43, 0x53, 0x31 ... На самом деле я только что попробовал BouncyCastle, и это сработало для открытого ключа. Подпись ASN.1. Я попытался преобразовать его в r|s, но все еще не повезло с проверкой. Исходный пост обновлен со всей информацией.   -  person Emile    schedule 28.08.2020


Ответы (1)


Опубликованный открытый ключ представляет собой ключ X.509/SPKI для NIST P-192 (он же secp192r1 или prime192v1). Подпись дается в формате ASN.1. Это можно легко проверить с помощью синтаксического анализатора ASN.1, например. здесь.

В коде есть ошибка. В соответствии

signer.BlockUpdate(data, 0, dataStr.Length);

dataStr.Length необходимо заменить на data.Length. В остальном код работает.

Тем не менее, для ваших данных верификация не проходит, т.е. данные и подпись по какой-то причине несовместимы (возможно, открытый и закрытый ключи не совпадают, или подпись создавалась для других данных и т.п.).

Сам код не является причиной. Я успешно протестировал (исправленный) код, используя следующие закрытые (PKCS8) и открытые (X.509) ключи (для NIST P-192):

-----BEGIN PRIVATE KEY-----
MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBgN4QXSZ9VyMP0sfb/E
vPObk83EHj2gemmhNAMyAAQESDhrEDN9oOetGgTzf+hN5Wm6xQqjOgjrDIdlXunl
gvQU9HS0dd/wzNuFy2pqD4I=
-----END PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEBEg4axAzfaDnrRoE83/oTeVpusUK
ozoI6wyHZV7p5YL0FPR0tHXf8Mzbhctqag+C
-----END PUBLIC KEY-----

Если отправленный вами открытый текст подписан указанным выше закрытым ключом, вы получите, например, следующую подпись (в кодировке Base64):

MDYCGQDrACykwYbQ6lQppw5PEcu5Bm7BuHjkVHoCGQDZ+RD3KvanoOzYj9bqQP2GHGhyrH6NOwA=

Если эта подпись подтверждена вашим (фиксированным) кодом и указанным выше открытым ключом, проверка прошла успешно!

Кстати, открытый ключ проще импортировать с помощью:

using Org.BouncyCastle.Security;
...
//var param = GetPublicKeyParam(keyBytes);          // remove
var param = PublicKeyFactory.CreateKey(keyBytes);   // add
person user 9014097    schedule 28.08.2020