static AsnElt Duplicate(AsnElt ae) { if (ae.Constructed) { int n = ae.Sub.Length; AsnElt[] ss = new AsnElt[n]; for (int i = 0; i < n; i++) { ss[i] = Duplicate(ae.Sub[i]); } return(AsnElt.Make(ae.TagClass, ae.TagValue, ss)); } if (ae.TagClass == AsnElt.UNIVERSAL) { switch (ae.TagValue) { case AsnElt.BOOLEAN: return(ae.GetBoolean() ? AsnElt.BOOL_TRUE : AsnElt.BOOL_FALSE); case AsnElt.INTEGER: return(AsnElt.MakeIntegerSigned( ae.CopyValue())); case AsnElt.OBJECT_IDENTIFIER: return(AsnElt.MakeOID(ae.GetOID())); case AsnElt.NumericString: case AsnElt.PrintableString: case AsnElt.IA5String: case AsnElt.TeletexString: case AsnElt.UTF8String: case AsnElt.BMPString: case AsnElt.UniversalString: case AsnElt.UTCTime: case AsnElt.GeneralizedTime: return(AsnElt.MakeString( ae.TagValue, ae.GetString())); } } /* * All other primitive types will be treated as blobs. * We still need to duplicate them in order to avoid * variants in tag/length encoding. */ return(AsnElt.MakePrimitive(ae.TagClass, ae.TagValue, ae.CopyValue())); }
/* * Create an instance by decoding the provided object. * This constructor assumes ASN.1 DER encoding (not Base64, * not PEM). * * On decoding error, an AsnException is thrown. */ public X509Cert(byte[] cert) { /* * Compute thumbprint. */ thumbprint = M.DoSHA1(cert).ToUpperInvariant(); /* * Outer layer decoding and extraction of the signature * hash algorithm. */ AsnElt ac = AsnElt.Decode(cert); ac.CheckTag(AsnElt.SEQUENCE); ac.CheckNumSub(3); hashAlgorithm = GetSignHashName( new AlgorithmIdentifier(ac.GetSub(1))); /* * TBS exploration. First field is optional; if present, * it contains the certificate version. */ AsnElt atbs = ac.GetSub(0); atbs.CheckNumSubMin(6); atbs.CheckNumSubMax(10); int off = 0; if (atbs.GetSub(0).TagValue == 0) { off++; } /* * Serial numer: nominally an INTEGER, we extract the * raw bytes, because some CA wrongly use unsigned * encoding. */ AsnElt aserial = atbs.GetSub(off); aserial.CheckTag(AsnElt.INTEGER); byte[] sv = aserial.CopyValue(); int svk = 0; while (svk < sv.Length && sv[svk] == 0) { svk++; } if (svk == sv.Length) { serialHex = "00"; } else { StringBuilder sb = new StringBuilder(); while (svk < sv.Length) { sb.AppendFormat("{0:X2}", sv[svk++]); } serialHex = sb.ToString(); } /* * Issuer and subject DN. */ issuerDN = new X500Name(atbs.GetSub(off + 2)); subjectDN = new X500Name(atbs.GetSub(off + 4)); /* * Validity dates. */ AsnElt adates = atbs.GetSub(off + 3); adates.CheckTag(AsnElt.SEQUENCE); adates.CheckNumSub(2); validFrom = adates.GetSub(0).GetTime(); validTo = adates.GetSub(1).GetTime(); /* * Public key. */ AsnElt aspki = atbs.GetSub(off + 5); aspki.CheckTag(AsnElt.SEQUENCE); aspki.CheckNumSub(2); AlgorithmIdentifier kt = new AlgorithmIdentifier(aspki.GetSub(0)); AsnElt aktp = kt.Parameters; AsnElt apkv = aspki.GetSub(1); apkv.CheckTag(AsnElt.BIT_STRING); byte[] kv = apkv.GetBitString(); curveOID = null; keyType = "UNKNOWN"; keySize = 0; switch (kt.OID) { /* * RSA public keys should use the 'rsaEncryption' OID, * but some are tagged with the OAEP or the PSS OID, * to somehow specify that the RSA key should be used * only with OAEP or PSS. */ case "1.2.840.113549.1.1.1": case "1.2.840.113549.1.1.7": case "1.2.840.113549.1.1.10": keyType = "RSA"; keySize = GetRSAPublicKeySize(kv); break; /* * All DSA public keys should use that OID. */ case "1.2.840.10040.4.1": keyType = "DSA"; keySize = GetDSAPublicKeySize(aktp); break; /* * Elliptic curve keys. * We only support "normal" elliptic curve keys, not * restricted keys. * We only supported named curves (RFC 5480 forbids * explicit curve parameters). */ case "1.2.840.10045.2.1": if (aktp == null) { break; } if (aktp.TagClass != AsnElt.UNIVERSAL || aktp.TagValue != AsnElt.OBJECT_IDENTIFIER) { break; } keyType = "EC"; curveOID = aktp.GetOID(); keySize = GetCurveSize(curveOID); break; /* TODO: GOST R 34.10-94 and GOST R 34.10-2001 */ } /* * If there are extensions, process them. * extract the dNSNames. */ serverNames = null; extensions = new SortedDictionary <string, Extension>( StringComparer.Ordinal); for (int i = off + 6; i < atbs.Sub.Length; i++) { AsnElt aexts = atbs.GetSub(i); if (aexts.TagClass != AsnElt.CONTEXT || aexts.TagValue != 3) { continue; } aexts.CheckNumSub(1); aexts = aexts.GetSub(0); aexts.CheckTag(AsnElt.SEQUENCE); foreach (AsnElt aext in aexts.Sub) { aext.CheckTag(AsnElt.SEQUENCE); aext.CheckNumSubMin(2); aext.CheckNumSubMax(3); AsnElt aoid = aext.GetSub(0); aoid.CheckTag(AsnElt.OBJECT_IDENTIFIER); string oid = aoid.GetOID(); AsnElt av; bool critical = false; if (aext.Sub.Length == 2) { av = aext.GetSub(1); } else { AsnElt acrit = aext.GetSub(1); acrit.CheckTag(AsnElt.BOOLEAN); critical = acrit.GetBoolean(); av = aext.GetSub(2); } av.CheckTag(AsnElt.OCTET_STRING); Extension ext = new Extension( oid, critical, av.CopyValue()); if (extensions.ContainsKey(oid)) { throw new AsnException( "duplicate extension " + oid); } extensions[oid] = ext; ProcessExtension(ext); } } /* * If there was no SAN, or no dNSName in the SAN, then * get the Common Name from the subjectDN. */ string cn = null; foreach (DNPart dnp in subjectDN.Parts) { if (dnp.FriendlyType == DNPart.COMMON_NAME) { if (cn != null) { throw new AsnException( "multiple CN in subject DN"); } cn = dnp.Value; } } if (serverNames == null) { if (cn == null) { serverNames = new string[0]; } else { serverNames = new string[] { cn }; } } }
static void Parse(TextWriter tw, int depth, AsnElt ae) { Indent(tw, depth); tw.Write("("); switch (ae.TagClass) { case AsnElt.APPLICATION: tw.Write("[application " + ae.TagValue + "]"); break; case AsnElt.CONTEXT: tw.Write("[" + ae.TagValue + "]"); break; case AsnElt.PRIVATE: tw.Write("[private " + ae.TagValue + "]"); break; default: switch (ae.TagValue) { case AsnElt.BOOLEAN: tw.Write("bool " + ae.GetBoolean() + ")"); return; case AsnElt.INTEGER: tw.Write("int " + ae.GetIntegerHex() + ")"); return; case AsnElt.BIT_STRING: int bitLen; byte[] bs = ae.GetBitString(out bitLen); tw.Write("bits " + (bs.Length * 8 - bitLen)); PrintBytes(tw, depth, bs); tw.Write(")"); return; case AsnElt.OCTET_STRING: tw.Write("blob"); PrintBytes(tw, depth, ae.CopyValue()); tw.Write(")"); return; case AsnElt.NULL: ae.CheckNull(); tw.Write("null)"); return; case AsnElt.OBJECT_IDENTIFIER: string oid = ae.GetOID(); if (!numOID) { oid = AsnOID.ToName(oid); } tw.Write("oid " + oid + ")"); return; case AsnElt.NumericString: tw.Write("numeric " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.PrintableString: tw.Write("printable " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.IA5String: tw.Write("ia5 " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.TeletexString: tw.Write("teletex " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.UTF8String: tw.Write("utf8 " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.BMPString: tw.Write("bmp " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.UniversalString: tw.Write("utf32 " + EscapeString(ae.GetString()) + ")"); return; case AsnElt.UTCTime: tw.Write("utc " + EscapeString(ae.GetString()) + " {" + ae.GetTime() + "} )"); return; case AsnElt.GeneralizedTime: tw.Write("gentime " + EscapeString(ae.GetString()) + " {" + ae.GetTime() + "} )"); return; case AsnElt.SEQUENCE: if (!ae.Constructed) { throw new AsnException( "Non-constructed SEQUENCE"); } tw.Write("sequence"); ParseSubs(tw, depth, ae); tw.Write(")"); return; case AsnElt.SET: if (!ae.Constructed) { throw new AsnException( "Non-constructed SET"); } tw.Write("set"); ParseSubs(tw, depth, ae); tw.Write(")"); return; default: tw.Write( "[universal " + ae.TagValue + "]"); break; } break; } if (ae.Constructed) { tw.Write("sequence"); ParseSubs(tw, depth, ae); tw.Write(")"); } else { tw.Write("blob"); PrintBytes(tw, depth, ae.CopyValue()); tw.Write(")"); } }