Decode ASN.1 data from CAC / x509 certificate extension (subject directory attributes> country of citizenship)

I need to read a property x509 not published via the X509Certificate2 class. So I need to read its OID and decode the ASN.1 data myself.

In particular, I need to read OID 2.5.29.9 and "1.3.1.1.5.5.7.9.4" respectively "Topic Directory Attributes"> "Country of Citizenship". Keep in mind that Subject Directory attributes are a collection and I do believe after Citizenship.

Right now I can get ASN.1 data and execute it through this javascript decoder and see OID (1.3.6.1.5.5.7.9.4) and the value I get after (US) but I can "t figure out how decode data in C # and target citizenship OID even more.Here is the code below:

var citizenship = (
    from X509Extension ext in x509.Extensions
    where ext.Oid.Value == "2.5.29.9"
    select new AsnEncodedData(ext.Oid, ext.RawData).Format(true)
);

      

And RawData in hex "30 12 30 10 06 08 2b 06 01 05 05 07 09 04 31 04 13 02 55 53"

[EDIT] I am looking to read this value in a web page and serialization to disk doesn't really suit my needs.

+3


source to share


2 answers


You can use Asn1Net.Reader . When you open "RawData" (but saved as binary) in the ASN.1 editor or Asn1Viewer, you can see the ASN.1 data structure. Below is a snapshot from the ASN.1 editor.

Structure of you citizenship ASN.1 object

Then with Asn1Net.Reader (nuget here ) you can parse the value with this code (validation failed; I am using nunit test here)

[Test]
[TestCase("30 12 30 10 06 08 2B 06 01 05 05 07 09 04 31 04 13 02 55 53")]
public void ReadCitizenship(string example)
{
    // translates hex to byte[]
    var encoded = Helpers.GetExampleBytes(example);

    // initialize reader
    var reader = Helpers.ReaderFromData(encoded);

    // parse ASN.1 object to the end and read value as byte[]
    var subjDirAttributes = reader.ReadToEnd(true);

    // NO Checking for null has been done
    //                                  sequence      sequence      set           printable string
    var citizenship = subjDirAttributes.ChildNodes[0].ChildNodes[0].ChildNodes[1].ChildNodes[0];

    var value = citizenship.ReadContentAsPrintableString();
    Assert.IsTrue(value == "US");
}

      

You can parse any ASN.1 object with this reader, and if you know the structure, you can read any value from it.



UPDATE: Per RFC 3739 Subject Directory Attributes are defined in section 3.2.2 . Below is an example in Appendix C.3 . The following are the attribute attributes of the specified certificates.

Subject directory attributes example from RFC 3739

According to the structure defined in RFC 3739, this code should parse all values โ€‹โ€‹of citizenship.

[Test]
public void ReadCitizenship()
{
    var exampleCert = @"MIIDEDCCAnmgAwIBAgIESZYC0jANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJE
RTE5MDcGA1UECgwwR01EIC0gRm9yc2NodW5nc3plbnRydW0gSW5mb3JtYXRpb25z
dGVjaG5payBHbWJIMB4XDTA0MDIwMTEwMDAwMFoXDTA4MDIwMTEwMDAwMFowZTEL
MAkGA1UEBhMCREUxNzA1BgNVBAoMLkdNRCBGb3JzY2h1bmdzemVudHJ1bSBJbmZv
cm1hdGlvbnN0ZWNobmlrIEdtYkgxHTAMBgNVBCoMBVBldHJhMA0GA1UEBAwGQmFy
emluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDc50zVodVa6wHPXswg88P8
p4fPy1caIaqKIK1d/wFRMN5yTl7T+VOS57sWxKcdDzGzqZJqjwjqAP3DqPK7AW3s
o7lBG6JZmiqMtlXG3+olv+3cc7WU+qDv5ZXGEqauW4x/DKGc7E/nq2BUZ2hLsjh9
Xy9+vbw+8KYE9rQEARdpJQIDAQABo4HpMIHmMGQGA1UdCQRdMFswEAYIKwYBBQUH
CQQxBBMCREUwDwYIKwYBBQUHCQMxAxMBRjAdBggrBgEFBQcJATERGA8xOTcxMTAx
NDEyMDAwMFowFwYIKwYBBQUHCQIxCwwJRGFybXN0YWR0MA4GA1UdDwEB/wQEAwIG
QDASBgNVHSAECzAJMAcGBSskCAEBMB8GA1UdIwQYMBaAFAABAgMEBQYHCAkKCwwN
Dg/+3LqYMDkGCCsGAQUFBwEDBC0wKzApBggrBgEFBQcLAjAdMBuBGW11bmljaXBh
bGl0eUBkYXJtc3RhZHQuZGUwDQYJKoZIhvcNAQEFBQADgYEAj4yAu7LYa3X04h+C
7+DyD2xViJCm5zEYg1m5x4znHJIMZsYAU/vJJIJQkPKVsIgm6vP/H1kXyAu0g2Ep
z+VWPnhZK1uw+ay1KRXw8rw2mR8hQ2Ug6QZHYdky2HH3H/69rWSPp888G8CW8RLU
uIKzn+GhapCuGoC4qWdlGLWqfpc=";

    var cer = new X509Certificate2(Convert.FromBase64String(exampleCert));
    var ext = cer.Extensions.OfType<X509Extension>().FirstOrDefault(p => p.Oid.Value == "2.5.29.9");

    var citizenshipValues = new List<string>();

    // initialize reader
    var reader = Helpers.ReaderFromData(ext.RawData);

    // parse ASN.1 object to the end and read value as byte[]
    var subjDirAttributes = reader.ReadToEnd(true);


    var citizenshipAsn1Object = subjDirAttributes.ChildNodes[0];

    foreach (var node in citizenshipAsn1Object.ChildNodes)
    {
        var shouldBeOidNode = node.ChildNodes[0];

        if (shouldBeOidNode.Identifier.Tag != Asn1Type.ObjectIdentifier) // should be oid
            throw new FormatException("Invalid structure of Subject Directory Attributes");

        var oidValue = shouldBeOidNode.ReadContentAsObjectIdentifier();

        if (oidValue != "1.3.6.1.5.5.7.9.4")
            continue;

        // found it

        var setNode = node.ChildNodes[1];
        if (setNode.Identifier.Tag != Asn1Type.Set)
            throw new FormatException("Invalid structure of Subject Directory Attributes");

        foreach (var internalNode in setNode.ChildNodes)
        {
            var valueOfCitizenship = internalNode.ReadContentAsPrintableString();
            if (valueOfCitizenship.Length != 2)
                throw new FormatException("Invalid value in countryOfCitizenship. Length should be exactly 2 characters.");

            citizenshipValues.Add(valueOfCitizenship);
        }

        // found all values, lets break
        break;
    }

    Assert.IsTrue(citizenshipValues.Count == 1);
}

      

+2


source


I have a special generic ctated function for parsing the subject attributes of a certificate. This will help you!

1) custom cteate model

    public class ModelCertParams
    {
        public string CN;
        public string S;
        public string E;
        public string L;
        public string O;
        public string C;
        public string STREET;
        public string OU;
        public string TIN = String.Empty;
        public string PINFL = String.Empty;
}

      

2) Research the meaning of the item from the certificate

 X509Certificate2 myCert = new X509Certificate2(cert, "pkcs12password");

String issureName = myCert.GetIssuerName().ToString();
String ClientName = myCert.Subject;

ModelCertParams IssureModel = getExtentionSubject(issureName);
ModelCertParams CertParams = getExtentionSubject(ClientName);

      



3) Create function getExtentionSubject ()

 public static ModelCertParams getExtentionSubject(String data)
        {
            ModelCertParams mcert = new ModelCertParams();
            mcert.C = ExtParse(data, "C");
            mcert.CN = ExtParse(data, "CN");
            mcert.O = ExtParse(data, "O");
            mcert.OU = ExtParse(data, "OU");
            mcert.L = ExtParse(data, "L");
            mcert.S = ExtParse(data, "S");
            mcert.TIN = ExtParse(data, "TIN") == "" ? ExtParse(data, "OID.1.2.860.3.16.1.1") : "";
            mcert.PINFL = ExtParse(data, "PINFL") == "" ? ExtParse(data, "OID.1.2.860.3.16.1.2") : "";
            mcert.E = ExtParse(data, "E");
            mcert.STREET = ExtParse(data, "STREET");

            return mcert;
        }

      

4) create a parser function

 public static string ExtParse(string data, string delimiter)
        {

            string result = String.Empty;
            try
            {
                if (data == null || data == "") return result;

                if (!delimiter.EndsWith("=")) delimiter = delimiter + "=";

                //data = data.ToUpper(); // if you need
                if (!data.Contains(delimiter)) return result;

                int start = data.IndexOf(delimiter) + delimiter.Length;

                string e = data.Substring(start, data.IndexOf('=', start) == -1 ? data.Length - start : data.IndexOf('=', start) - start);
                int tt = e.Length - e.LastIndexOf(", ");

                int length = data.IndexOf('=', start) == -1 ? data.Length - start : data.IndexOf('=', start) - start - tt;

                if (length == 0) return result; 
                if (length > 0)
                {
                    result = data.Substring(start, length);

                }
                else
                {
                    result = data.Substring(start);
                }
                return result;

            }
            catch (Exception)
            {
                return result;
            }
        }

      

0


source







All Articles