【问题标题】:CngKey import from a ECSsaP192 public key从 ECSsaP192 公钥导入 CngKey
【发布时间】:2020-08-27 17:33:21
【问题描述】:

我正在验证提供公钥的签名 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;
}

【问题讨论】:

  • 您使用哪个 .NET 版本?
  • @Topaco 我正在使用 4.7.2
  • 0x45, 0x43, 0x53, 0x31 是标识 NIST P-256(又名 secp256r1)、here 而非您的曲线的神奇数字。对于 .NET Framework,BouncyCastle 是一种可能。哪种格式具有签名(r|sASN.1)或具有哪种长度?理想情况下,您可以发布可以使用发布的公钥验证的测试数据(明文和签名)。哪个摘要用于签名,SHA-256?
  • @Topaco 我找不到 P192 的幻数,所以我使用了 0x45、0x43、0x53、0x31 ... 实际上我只是尝试了 BouncyCastle,它适用于公钥。签名是 ASN.1。我尝试将其转换为 r|s 但验证仍然没有运气。原始帖子已更新所有信息。

标签: c# .net cryptography public-key cng


【解决方案1】:

发布的公钥是 NIST P-192(又名 secp192r1 或 prime192v1)的 X.509/SPKI 密钥。签名以 ASN.1 格式给出。这可以使用 ASN.1 解析器最容易地验证,例如here.

代码中存在错误。在行中

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

【讨论】:

    猜你喜欢
    • 2014-08-06
    • 2012-03-13
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    相关资源
    最近更新 更多