4

This code relates to DKIM signature verification used in anti-spam efforts.

I have a byte[] from s1024._domainkey.yahoo.com that is ASN.1 encoded, but I don't know if that alone contains enough information to materialize a public key.

Based on this class, it appears I can convert an ASN.1 key into a X509Certificate Public key, but I need to supply an OID and some ASN.1-encoded parameters.

In this example I have metadata that the ASN1 key is:

  1. An RSA encoded key (ASN.1 DER-encoded [ITU-X660-1997] RSAPublicKey per RFC3447)
  2. Used with either sha1 sha256 hash algorithms
  3. Uses an OID relating to the following table from section A.2 of RFC3447 (though I don't know how to turn this information into a full OID)
/*
 * 1.2.840.113549.1
 * 
    MD2 md2WithRSAEncryption    ::= {pkcs-1 2}
    MD5 md5WithRSAEncryption    ::= {pkcs-1 4}
    SHA-1 sha1WithRSAEncryption   ::= {pkcs-1 5}
    SHA-256 sha256WithRSAEncryption ::= {pkcs-1 11}
    SHA-384 sha384WithRSAEncryption ::= {pkcs-1 12}
    SHA-512 sha512WithRSAEncryption ::= {pkcs-1 13}
 */

Code sample

string pubkey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB";
byte[] pubkeyByteArray = Convert.FromBase64String(pubkey);
AsnEncodedData aData = new AsnEncodedData(pubkeyByteArray);

// OID must not be null, but it is here.  What is it?
System.Security.Cryptography.X509Certificates.PublicKey pubKeyRdr = new System.Security.Cryptography.X509Certificates.PublicKey(aData.Oid, null, aData);

Question

  • What OID should I use?
  • What are examples of ASN.1 parameters?
jww
  • 97,681
  • 90
  • 411
  • 885
makerofthings7
  • 60,103
  • 53
  • 215
  • 448
  • It doesn't seem like `AsnEncodedData` actually parses the data. You'll have to set the oid through, `aData.Oid = new Oid("1.2.840.113549.1.1")`. After parsing the DER-TLV, you can use [oid tool](http://www.rtner.de/software/oid.html) to convert the hex data to string representation, *UNIVERSAL OID.1.2.840.113549.1.1.1*. – user845279 Jun 29 '12 at 23:40
  • 1
    Here's a related question that deals with just the key, a `CspParameters` and `RSACryptoServiceProvider`: [Load ASN.1/DER encoded RSA keypair in C#](http://stackoverflow.com/q/42175485/608639). I point it out because so many Stack overflow answers tell you do things with certificates or use BouncyCastle when all you are doing is trying to load a key. Also note... the pain point is due to .Net and their use of XML encoding from [RFC 3275](https://www.ietf.org/rfc/rfc3275.txt). .Net does not accept ASN.1/DER or PEM encoded keys. – jww Feb 11 '17 at 14:34

2 Answers2

6

Update

This is the data you have provided when it is parsed using the link @erickson provided:

SEQUENCE (2 elem)
    SEQUENCE (2 elem)
        OBJECT IDENTIFIER 1.2.840.113549.1.1.1
        NULL
    BIT STRING (1 elem)
        SEQUENCE (2 elem)
            INTEGER(1024 bit)
            INTEGER 65537

The reason the previous code throws a ASN1 bad tag value met. exception is because aData contains incorrect data (contains all the data above). From what I've seen, the is how the 3 arguments to System.Security.Cryptography.X509Certificates.PublicKey are broken down.

  1. The first parameter is the OID, which is the OBJECT IDENTIFIER above.
  2. The second parameter is the public key parameters. In the parsing above, you can see it is NULL.
  3. The third parameter is actual public key value. This is made up of the third sequence above. The sequence has 2 integers, a 1024-bit modulus followed by the public exponent.

I tested it using the code below. I couldn't find a built-in method to parse the data without writing a DER parser.

Oid oid = new Oid("1.2.840.113549.1.1.1");
AsnEncodedData keyValue = new AsnEncodedData(getBytes("30818902818100EB11E7B4462E09BB3F907E2598BA2FC4F541925DABBFD8FF0B8E74C3F15E149E7FB6140655184DE42F6DDBCDEA142D8BF83DE95E07781F98988324E294DCDB392F82890145078C5C0379BB7434FFAC04AD1529E4C04CBD98AFF4B76D3FF1872FB5C6D8F8464755EDF5714E7E7A2DBE2E7549F0BB12B85796F93DD38A8FFF97730203010001"));
AsnEncodedData keyParam = new AsnEncodedData(new byte[] {05, 00});
PublicKey pubKeyRdr = new System.Security.Cryptography.X509Certificates.PublicKey(oid, keyParam, keyValue);
System.Diagnostics.Debug.WriteLine(pubKeyRdr.Key.KeyExchangeAlgorithm);
System.Diagnostics.Debug.WriteLine(pubKeyRdr.Key.KeySize);

It outputs RSA-PKCS1-KeyEx and 1024.

user845279
  • 2,794
  • 1
  • 20
  • 38
  • How did you figure this out? What does `new byte[]{05, 00}` mean? – makerofthings7 Jun 30 '12 at 00:20
  • 2
    05, 00 is the ASN.1 encoding of `NULL`. – erickson Jun 30 '12 at 00:31
  • The data you provided can be parsed to get the data that was well described by @erickson, to figure out the oid. Like you mentioned, the oid is null originally. Anyway, the null part is mentionedin one of the rfc documents. The way I actually found format is by using `X509Certificates.Import()` on an existing blah.cer file. I noticed the key param is 0500. As you can see this is not a good explanation for the solution, as to why I didn't post it. – user845279 Jun 30 '12 at 01:20
  • 1
    Hopefully someone, possibly erickson will post a clear and concise answer with better reasoning. My question is, why doesn't `AsnEncodedData` parse the oid from the rawdata? Is it not possible for a general case ASN encoded data? – user845279 Jun 30 '12 at 01:28
  • 1
    I tried this code and noticed that `pubKeyRdr.Key` throws an exception. If I drill down, the parent classes say this is due to an `invalid ASN.1.` – makerofthings7 Jun 30 '12 at 04:54
  • Is GetBytes supposed to be Convert.FromBase64String? Does the value I'm converting (`30818...`) come from a DER parser that isn't in the Framework (or is marked internal?) – makerofthings7 Jul 02 '12 at 03:20
  • No, it just converts hexadecimal to byte[] array (it's a local function). The value comes from parsing it with a TLV parser, in this case by hand. – user845279 Jul 02 '12 at 05:08
1

What you have is a SubjectPublicKeyInfo structure. It looks like this:

Sequence {
  Sequence {
    Oid: 1.2.840.113549.1.1.1
    Parameters: null
  }
  KeyValue: blah blah
}

The oid for RSA keys is 1.2.840.113549.1.1.1.

For an RSA key, there are no parameters, so this is null.

However, I don't see any API on AsnEncodedData to parse apart the elements and get at what you need.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • How did you figure this out? From memory or previous work experience? – makerofthings7 Jun 30 '12 at 00:21
  • @makerofthings7 I remembered SubjectPublicKeyInfo structure from previous work experience. I determined what you are working with by decoding it [here.](http://lapo.it/asn1js/) – erickson Jun 30 '12 at 00:34
  • The parsing logic is in `PublicKey`'s private method `DecodePublicKeyObject()`. I don't really get why they couldn't have parsed out the ID and param value in the process but it's not too big a deal to do it manually and pass them in if needed. – DiskJunky Mar 23 '18 at 16:40