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.
source to share
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.
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.
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);
}
source to share
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;
}
}
source to share