internal static partial int CryptoNative_BioTell(SafeBioHandle bio);
private static partial int BioWrite(SafeBioHandle b, ref byte data, int len);
internal static partial int BioCtrlPending(SafeBioHandle bio);
public X509ContentType GetCertContentType(string fileName) { // If we can't open the file, fail right away. using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(fileName, "rb")) { Interop.Crypto.CheckValidOpenSslHandle(fileBio); int bioPosition = Interop.Crypto.BioTell(fileBio); Debug.Assert(bioPosition >= 0); // X509ContentType.Cert { ICertificatePal certPal; if (OpenSslX509CertificateReader.TryReadX509Der(fileBio, out certPal)) { certPal.Dispose(); return(X509ContentType.Cert); } OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); if (OpenSslX509CertificateReader.TryReadX509Pem(fileBio, out certPal)) { certPal.Dispose(); return(X509ContentType.Cert); } OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); } // X509ContentType.Pkcs7 { if (PkcsFormatReader.IsPkcs7Der(fileBio)) { return(X509ContentType.Pkcs7); } OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); if (PkcsFormatReader.IsPkcs7Pem(fileBio)) { return(X509ContentType.Pkcs7); } OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); } // X509ContentType.Pkcs12 (aka PFX) { OpenSslPkcs12Reader pkcs12Reader; if (OpenSslPkcs12Reader.TryRead(fileBio, out pkcs12Reader)) { pkcs12Reader.Dispose(); return(X509ContentType.Pkcs12); } OpenSslX509CertificateReader.RewindBio(fileBio, bioPosition); } } // Unsupported format. // Windows throws new CryptographicException(CRYPT_E_NO_MATCH) throw new CryptographicException(); }
internal static partial int BioWrite(SafeBioHandle b, byte[] data, int len);
internal static extern SafePkcs12Handle d2i_PKCS12_bio(SafeBioHandle bio, IntPtr zero);
internal static partial SafePkcs7Handle PemReadBioPkcs7(SafeBioHandle bp);
private static Tuple <SafeX509StackHandle, SafeX509StackHandle> LoadMachineStores( DirectoryInfo rootStorePath, FileInfo rootStoreFile) { Debug.Assert( Monitor.IsEntered(s_recheckStopwatch), "LoadMachineStores assumes a lock(s_recheckStopwatch)"); SafeX509StackHandle rootStore = Interop.Crypto.NewX509Stack(); Interop.Crypto.CheckValidOpenSslHandle(rootStore); SafeX509StackHandle intermedStore = Interop.Crypto.NewX509Stack(); Interop.Crypto.CheckValidOpenSslHandle(intermedStore); DateTime newFileTime = default; DateTime newDirTime = default; var uniqueRootCerts = new HashSet <X509Certificate2>(); var uniqueIntermediateCerts = new HashSet <X509Certificate2>(); if (rootStoreFile != null && rootStoreFile.Exists) { newFileTime = rootStoreFile.LastWriteTimeUtc; ProcessFile(rootStoreFile); } if (rootStorePath != null && rootStorePath.Exists) { newDirTime = rootStorePath.LastWriteTimeUtc; foreach (FileInfo file in rootStorePath.EnumerateFiles()) { ProcessFile(file); } } void ProcessFile(FileInfo file) { using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb")) { // The handle may be invalid, for example when we don't have read permission for the file. if (fileBio.IsInvalid) { Interop.Crypto.ErrClearError(); return; } // Some distros ship with two variants of the same certificate. // One is the regular format ('BEGIN CERTIFICATE') and the other // contains additional AUX-data ('BEGIN TRUSTED CERTIFICATE'). // The additional data contains the appropriate usage (e.g. emailProtection, serverAuth, ...). // Because corefx doesn't validate for a specific usage, derived certificates are rejected. // For now, we skip the certificates with AUX data and use the regular certificates. ICertificatePal pal; while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal) || OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal)) { X509Certificate2 cert = new X509Certificate2(pal); // The HashSets are just used for uniqueness filters, they do not survive this method. if (StringComparer.Ordinal.Equals(cert.Subject, cert.Issuer)) { if (uniqueRootCerts.Add(cert)) { using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle)) { if (!Interop.Crypto.PushX509StackField(rootStore, tmp)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // The ownership has been transferred to the stack tmp.SetHandleAsInvalid(); } continue; } } else { if (uniqueIntermediateCerts.Add(cert)) { using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle)) { if (!Interop.Crypto.PushX509StackField(intermedStore, tmp)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // The ownership has been transferred to the stack tmp.SetHandleAsInvalid(); } continue; } } // There's a good chance we'll encounter duplicates on systems that have both one-cert-per-file // and one-big-file trusted certificate stores. Anything that wasn't unique will end up here. cert.Dispose(); } } } foreach (X509Certificate2 cert in uniqueRootCerts) { cert.Dispose(); } foreach (X509Certificate2 cert in uniqueIntermediateCerts) { cert.Dispose(); } Tuple <SafeX509StackHandle, SafeX509StackHandle> newCollections = Tuple.Create(rootStore, intermedStore); Debug.Assert( Monitor.IsEntered(s_recheckStopwatch), "LoadMachineStores assumes a lock(s_recheckStopwatch)"); // The existing collections are not Disposed here, intentionally. // They could be in the gap between when they are returned from this method and not yet used // in a P/Invoke, which would result in exceptions being thrown. // In order to maintain "finalization-free" the GetNativeCollections method would need to // DangerousAddRef, and the callers would need to DangerousRelease, adding more interlocked operations // on every call. Volatile.Write(ref s_nativeCollections, newCollections); s_directoryCertsLastWrite = newDirTime; s_fileCertsLastWrite = newFileTime; s_recheckStopwatch.Restart(); return(newCollections); }
internal static extern SafePkcs7Handle PemReadBioPkcs7(SafeBioHandle bp);
internal static extern SafeX509CrlHandle PemReadBioX509Crl(SafeBioHandle bio);
internal static extern SafeX509Handle PemReadX509FromBio(SafeBioHandle bio);
internal static extern int PemWriteBioX509Crl(SafeBioHandle bio, SafeX509CrlHandle crl);
internal static partial SafeX509Handle ReadX509AsDerFromBio(SafeBioHandle bio);
internal static partial int BioSeek(SafeBioHandle bio, int pos);
internal static extern int BioCtrlPending(SafeBioHandle bio);
internal static extern SafePkcs7Handle D2IPkcs7Bio(SafeBioHandle bp);
private static void LoadMachineStores() { Debug.Assert( Monitor.IsEntered(s_machineIntermediateStore), "LoadMachineStores assumes a lock(s_machineIntermediateStore)"); X509Certificate2Collection rootStore = new X509Certificate2Collection(); DirectoryInfo rootStorePath = null; IEnumerable <FileInfo> trustedCertFiles; try { rootStorePath = new DirectoryInfo(Interop.Crypto.GetX509RootStorePath()); } catch (ArgumentException) { // If SSL_CERT_DIR is set to the empty string, or anything else which gives // "The path is not of a legal form", then the GetX509RootStorePath value is ignored. } if (rootStorePath != null && rootStorePath.Exists) { trustedCertFiles = rootStorePath.EnumerateFiles(); } else { trustedCertFiles = Array.Empty <FileInfo>(); } FileInfo rootStoreFile = null; try { rootStoreFile = new FileInfo(Interop.Crypto.GetX509RootStoreFile()); } catch (ArgumentException) { // If SSL_CERT_FILE is set to the empty string, or anything else which gives // "The path is not of a legal form", then the GetX509RootStoreFile value is ignored. } if (rootStoreFile != null && rootStoreFile.Exists) { trustedCertFiles = Append(trustedCertFiles, rootStoreFile); } HashSet <X509Certificate2> uniqueRootCerts = new HashSet <X509Certificate2>(); HashSet <X509Certificate2> uniqueIntermediateCerts = new HashSet <X509Certificate2>(); foreach (FileInfo file in trustedCertFiles) { using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb")) { ICertificatePal pal; while (CertificatePal.TryReadX509Pem(fileBio, out pal) || CertificatePal.TryReadX509Der(fileBio, out pal)) { X509Certificate2 cert = new X509Certificate2(pal); // The HashSets are just used for uniqueness filters, they do not survive this method. if (StringComparer.Ordinal.Equals(cert.Subject, cert.Issuer)) { if (uniqueRootCerts.Add(cert)) { rootStore.Add(cert); continue; } } else { if (uniqueIntermediateCerts.Add(cert)) { s_machineIntermediateStore.Add(cert); continue; } } // There's a good chance we'll encounter duplicates on systems that have both one-cert-per-file // and one-big-file trusted certificate stores. Anything that wasn't unique will end up here. cert.Dispose(); } } } s_machineRootStore = rootStore; }
private static bool AddCachedCrl(string crlFileName, SafeX509StoreHandle store, DateTime verificationTime) { string crlFile = GetCachedCrlPath(crlFileName); using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "rb")) { if (bio.IsInvalid) { Interop.Crypto.ErrClearError(); return(false); } // X509_STORE_add_crl will increase the refcount on the CRL object, so we should still // dispose our copy. using (SafeX509CrlHandle crl = Interop.Crypto.PemReadBioX509Crl(bio)) { if (crl.IsInvalid) { Interop.Crypto.ErrClearError(); return(false); } // If crl.LastUpdate is in the past, downloading a new version isn't really going // to help, since we can't rewind the Internet. So this is just going to fail, but // at least it can fail without using the network. // // If crl.NextUpdate is in the past, try downloading a newer version. IntPtr nextUpdatePtr = Interop.Crypto.GetX509CrlNextUpdate(crl); DateTime nextUpdate; // If there is no crl.NextUpdate, this indicates that the CA is not providing // any more updates to the CRL, or they made a mistake not providing a NextUpdate. // We'll cache it for a few days to cover the case it was a mistake. if (nextUpdatePtr == IntPtr.Zero) { try { nextUpdate = File.GetLastWriteTime(crlFile).AddDays(3); } catch { // We couldn't determine when the CRL was last written to, // so consider it expired. Debug.Fail("Failed to get the last write time of the CRL file"); return(false); } } else { nextUpdate = OpenSslX509CertificateReader.ExtractValidityDateTime(nextUpdatePtr); } // OpenSSL is going to convert our input time to universal, so we should be in Local or // Unspecified (local-assumed). Debug.Assert( verificationTime.Kind != DateTimeKind.Utc, "UTC verificationTime should have been normalized to Local"); // In the event that we're to-the-second accurate on the match, OpenSSL will consider this // to be already expired. if (nextUpdate <= verificationTime) { return(false); } if (!Interop.Crypto.X509StoreAddCrl(store, crl)) { // Ignore error "cert already in store", throw on anything else. In any case the error queue will be cleared. if (X509_R_CERT_ALREADY_IN_HASH_TABLE == Interop.Crypto.ErrPeekLastError()) { Interop.Crypto.ErrClearError(); } else { throw Interop.Crypto.CreateOpenSslCryptographicException(); } } return(true); } } }
public static unsafe ICertificatePal FromBlob(byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) { // If we can see a hyphen, assume it's PEM. Otherwise try DER-X509, then fall back to DER-PKCS12. SafeX509Handle cert; // PEM if (rawData[0] == '-') { using (SafeBioHandle bio = Interop.libcrypto.BIO_new(Interop.libcrypto.BIO_s_mem())) { Interop.libcrypto.CheckValidOpenSslHandle(bio); Interop.libcrypto.BIO_write(bio, rawData, rawData.Length); cert = Interop.libcrypto.PEM_read_bio_X509_AUX(bio, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } Interop.libcrypto.CheckValidOpenSslHandle(cert); return(new OpenSslX509CertificateReader(cert)); } // DER-X509 cert = Interop.libcrypto.OpenSslD2I((ptr, b, i) => Interop.libcrypto.d2i_X509(ptr, b, i), rawData, checkHandle: false); if (!cert.IsInvalid) { return(new OpenSslX509CertificateReader(cert)); } // DER-PKCS12 OpenSslPkcs12Reader pfx; if (OpenSslPkcs12Reader.TryRead(rawData, out pfx)) { using (pfx) { pfx.Decrypt(password); ICertificatePal first = null; foreach (OpenSslX509CertificateReader certPal in pfx.ReadCertificates()) { // When requesting an X509Certificate2 from a PFX only the first entry is // returned. Other entries should be disposed. if (first == null) { first = certPal; } else { certPal.Dispose(); } } if (first == null) { throw new CryptographicException(); } return(first); } } // Unsupported throw Interop.libcrypto.CreateOpenSslCryptographicException(); }
internal static extern void SslSetBio(SafeSslHandle ssl, SafeBioHandle rbio, SafeBioHandle wbio);
internal static partial SafePkcs7Handle D2IPkcs7Bio(SafeBioHandle bp);
internal static extern int BioGets(SafeBioHandle b, byte[] buf, int size);
internal static partial int BioGets(SafeBioHandle b, byte[] buf, int size);
internal static extern int BioWrite(SafeBioHandle b, byte[] data, int len);
internal static int BioWrite(SafeBioHandle b, ReadOnlySpan <byte> data) => BioWrite(b, ref MemoryMarshal.GetReference(data), data.Length);
private static extern int BioWrite(SafeBioHandle b, ref byte data, int len);
internal static partial int GetMemoryBioSize(SafeBioHandle bio);
internal static extern int GetMemoryBioSize(SafeBioHandle bio);
internal static extern unsafe int BioWrite(SafeBioHandle b, byte *data, int len);
private static extern int Asn1StringPrintEx(SafeBioHandle bio, SafeSharedAsn1StringHandle str, Asn1StringPrintFlags flags);