Emile
Emile

Reputation: 197

CngKey import from a ECSsaP192 public key

I'm working on verifying a signature which public key is provided as MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEMyHD625uvsmGq4C43cQ9BnfN2xslVT5V1nOmAMP6qaRRUll3PB1JYmgSm+62sosG

After a lot of research I think it is a ECDsaP192 standard key (Correct me if I'm wrong). So the break down of the key would be

      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

I saw an example of a secp256r1 key which is very similar to my case but still I couldn't get it to work for me. My code:

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.
}

Edit: Using 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;
}

Upvotes: 2

Views: 884

Answers (1)

Topaco
Topaco

Reputation: 49121

The posted public key is an X.509/SPKI key for NIST P-192 (aka secp192r1 or prime192v1). The signature is given in ASN.1 format. This can be verified most easily with an ASN.1 parser, e.g. here.

There is a bug in the code. In the line

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

dataStr.Length must be replaced by data.Length. Apart from that the code works.

Nevertheless, the verification fails for your data, i.e. data and signature are inconsistent for some reason (possibly public and private keys do not match, or the signature was created for other data, etc.).

The code itself is not the cause. I have successfully tested the (fixed) code using the following private (PKCS8) and public (X.509) keys (for 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-----

If the plaintext posted by you is signed with the private key above, you will get, for example, the following signature (Base64 encoded):

MDYCGQDrACykwYbQ6lQppw5PEcu5Bm7BuHjkVHoCGQDZ+RD3KvanoOzYj9bqQP2GHGhyrH6NOwA=

If this signature is verified with your (fixed) code and the public key above, the verification is successful!

By the way, it is easier to import the public key with:

using Org.BouncyCastle.Security;
...
//var param = GetPublicKeyParam(keyBytes);          // remove
var param = PublicKeyFactory.CreateKey(keyBytes);   // add

Upvotes: 1

Related Questions