private static void AddX509Names(SafeX509NameStackHandle nameStack, StoreLocation storeLocation, HashSet <string> issuerNameHashSet) { using (var store = new X509Store(StoreName.Root, storeLocation)) { store.Open(OpenFlags.ReadOnly); foreach (var certificate in store.Certificates) { //Check if issuer name is already present //Avoiding duplicate names if (!issuerNameHashSet.Add(certificate.Issuer)) { continue; } using (SafeX509Handle certHandle = Crypto.X509Duplicate(certificate.Handle)) { using (SafeX509NameHandle nameHandle = Crypto.DuplicateX509Name(Crypto.X509GetIssuerName(certHandle))) { if (Crypto.PushX509NameStackField(nameStack, nameHandle)) { // The handle ownership has been transferred into the STACK_OF(X509_NAME). nameHandle.SetHandleAsInvalid(); } else { throw new CryptographicException(SR.net_ssl_x509Name_push_failed_error); } } } } } }
internal static unsafe string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, OpenSslX09NameFormatFlags nativeFlags) { using (SafeX509NameHandle x509Name = Interop.libcrypto.OpenSslD2I(Interop.libcrypto.d2i_X509_NAME, encodedDistinguishedName)) { Interop.libcrypto.CheckValidOpenSslHandle(x509Name); using (SafeBioHandle bioHandle = Interop.libcrypto.BIO_new(Interop.libcrypto.BIO_s_mem())) { Interop.libcrypto.CheckValidOpenSslHandle(bioHandle); int written = Interop.libcrypto.X509_NAME_print_ex( bioHandle, x509Name, 0, new UIntPtr((uint)nativeFlags)); // X509_NAME_print_ex returns how many bytes were written into the buffer. // BIO_gets wants to ensure that the response is NULL-terminated. // So add one to leave space for the NULL. StringBuilder builder = new StringBuilder(written + 1); int read = Interop.libcrypto.BIO_gets(bioHandle, builder, builder.Capacity); if (read < 0) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } return(builder.ToString()); } } }
internal static SafeSharedX509NameEntryHandle GetX509NameEntry(SafeX509NameHandle x509Name, int loc) { CheckValidOpenSslHandle(x509Name); return(SafeInteriorHandle.OpenInteriorHandle( (nameHandle, i) => GetX509NameEntry_private(nameHandle, i), x509Name, loc)); }
internal static SafeSharedX509NameEntryHandle GetX509NameEntry(SafeX509NameHandle x509Name, int loc) { CheckValidOpenSslHandle(x509Name); SafeSharedX509NameEntryHandle handle = GetX509NameEntry_private(x509Name, loc); if (!handle.IsInvalid) { handle.SetParent(x509Name); } return(handle); }
internal static extern int X509_NAME_print_ex(SafeBioHandle @out, SafeX509NameHandle nm, int indent, NativeULong flags);
internal static extern int GetX509NameEntryCount(SafeX509NameHandle x509Name);
internal static extern bool PushX509NameStackField(SafeX509NameStackHandle stack, SafeX509NameHandle x509_Name);
private static extern SafeSharedX509NameEntryHandle GetX509NameEntry_private(SafeX509NameHandle x509Name, int loc);
internal static string X500DistinguishedNameDecode( byte[] encodedName, bool printOid, X500DistinguishedNameFlags flags) { bool reverse = (flags & X500DistinguishedNameFlags.Reversed) == X500DistinguishedNameFlags.Reversed; bool quoteIfNeeded = (flags & X500DistinguishedNameFlags.DoNotUseQuotes) != X500DistinguishedNameFlags.DoNotUseQuotes; string dnSeparator; if ((flags & X500DistinguishedNameFlags.UseSemicolons) == X500DistinguishedNameFlags.UseSemicolons) { dnSeparator = "; "; } else if ((flags & X500DistinguishedNameFlags.UseNewLines) == X500DistinguishedNameFlags.UseNewLines) { dnSeparator = Environment.NewLine; } else { // This is matching Windows (native) behavior, UseCommas does not need to be asserted, // it is just what happens if neither UseSemicolons nor UseNewLines is specified. dnSeparator = ", "; } using (SafeX509NameHandle x509Name = Interop.Crypto.DecodeX509Name(encodedName, encodedName.Length)) { if (x509Name.IsInvalid) { return(""); } // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting // too much space in the average case. // // So, let's look at an example of what our output might be. // // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs): // businessCategory=Private Organization // 1.3.6.1.4.1.311.60.2.1.3=US // 1.3.6.1.4.1.311.60.2.1.2=Delaware // serialNumber=5157550 // street=548 4th Street // postalCode=94107 // C=US // ST=California // L=San Francisco // O=GitHub, Inc. // CN=github.com // // Which comes out to 228 characters using OpenSSL's default pretty-print // (openssl x509 -in github.cer -text -noout) // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values // and round that up to the next programmer number, and you get that 512 should avoid reallocations // in all but the most dire of cases. StringBuilder decodedName = new StringBuilder(512); int entryCount = Interop.Crypto.GetX509NameEntryCount(x509Name); bool printSpacing = false; for (int i = 0; i < entryCount; i++) { int loc = reverse ? entryCount - i - 1 : i; using (SafeSharedX509NameEntryHandle nameEntry = Interop.Crypto.GetX509NameEntry(x509Name, loc)) { Interop.Crypto.CheckValidOpenSslHandle(nameEntry); string thisOidValue; using (SafeSharedAsn1ObjectHandle oidHandle = Interop.Crypto.GetX509NameEntryOid(nameEntry)) { thisOidValue = Interop.Crypto.GetOidValue(oidHandle); } if (printSpacing) { decodedName.Append(dnSeparator); } else { printSpacing = true; } if (printOid) { AppendOid(decodedName, thisOidValue); } string rdnValue; using (SafeSharedAsn1StringHandle valueHandle = Interop.Crypto.GetX509NameEntryData(nameEntry)) { rdnValue = Interop.Crypto.Asn1StringToManagedString(valueHandle); } bool quote = quoteIfNeeded && NeedsQuoting(rdnValue); if (quote) { decodedName.Append('"'); // If the RDN itself had a quote within it, that quote needs to be escaped // with another quote. rdnValue = rdnValue.Replace("\"", "\"\""); } decodedName.Append(rdnValue); if (quote) { decodedName.Append('"'); } } } return(decodedName.ToString()); } }