解码来自CAC / x509证书扩展的ASN.1数据(主题目录属性>公民身份)

时间:2015-04-30 17:42:43

标签: c# asp.net x509 asn.1 x509certificate2

我需要读取未通过X509Certificate2类发布的x509属性。因此,我需要读取它的OID并自己解码ASN.1数据。

具体来说,我需要阅读“主题目录属性”> “公民身份”分别为OID 2.5.29.9和1.3.6.1.5.5.7.9.4。请记住,主题目录属性是一个集合,我只是真正追随公民身份。

现在我可以获取ASN.1数据并可以运行它 this javascript decoder并查看OID(1.3.6.1.5.5.7.9.4)和我之后的值(美国),但我无法弄清楚如何解码C#中的数据并进一步针对Citizenship OID 。以下是我到目前为止的以下代码:

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)
);

以十六进制表示的RawData是“30 12 30 10 06 08 2b 06 01 05 05 07 09 04 31 04 13 02 55 53”

[编辑]我希望在网页中读取这个值,序列化到磁盘并不能满足我的需求。

2 个答案:

答案 0 :(得分:2)

您可以使用Asn1Net.Reader。当你打开" RawData" (但在文件中保存为二进制文件)在ASN.1 editorAsn1Viewer中,您可以看到ASN.1数据的结构。以下是ASN.1编辑的图片。

Structure of you citizenship ASN.1 object

然后使用Asn1Net.Reader(nuget here),您可以使用此代码解析值(不是没有进行空检查;我在这里使用nunit测试)

[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");
}

您可以使用此阅读器解析任何ASN.1对象,如果您知道结构,则可以从中读取任何值。

<强>更新 根据{{​​3}}主题目录属性在RFC 3739中定义。 section 3.2.2中有一个例子。给定证书的主题目录属性如下所示。

Subject directory attributes example from RFC 3739

根据RFC 3739中定义的结构,此代码应解析所有公民身份值。

[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);
}

答案 1 :(得分:0)

我已经使用自定义通用函数来解析证书主题属性。它会对你有所帮助!

1)cteate自定义模型

    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)从证书中解析主题值

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

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

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

3)创建函数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)创建解析器功能

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