private static bool AddCachedCrl(X509Certificate2 cert, SafeX509StoreHandle store, DateTime verificationTime) { string crlFile = GetCachedCrlPath(cert); 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. DateTime nextUpdate = OpenSslX509CertificateReader.ExtractValidityDateTime( Interop.Crypto.GetX509CrlNextUpdate(crl)); // 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); } } }
internal static SafeX509CrlHandle DownloadCrl(string uri, ref TimeSpan remainingDownloadTime) { byte[] data = DownloadAsset(uri, ref remainingDownloadTime); if (data == null) { return(null); } // DER-encoded CRL seems to be the most common off of some random spot-checking, so try DER first. SafeX509CrlHandle handle = Interop.Crypto.DecodeX509Crl(data, data.Length); if (!handle.IsInvalid) { return(handle); } using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio()) { Interop.Crypto.BioWrite(bio, data, data.Length); handle = Interop.Crypto.PemReadBioX509Crl(bio); if (!handle.IsInvalid) { return(handle); } } return(null); }
private static void DownloadAndAddCrl( X509Certificate2 cert, SafeX509StoreHandle store, ref TimeSpan remainingDownloadTime) { string url = GetCdpUrl(cert); if (url == null) { return; } // X509_STORE_add_crl will increase the refcount on the CRL object, so we should still // dispose our copy. using (SafeX509CrlHandle crl = CertificateAssetDownloader.DownloadCrl(url, ref remainingDownloadTime)) { // null is a valid return (e.g. no remainingDownloadTime) if (crl != null && !crl.IsInvalid) { 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(); } } // Saving the CRL to the disk is just a performance optimization for later requests to not // need to use the network again, so failure to save shouldn't throw an exception or mark // the chain as invalid. try { string crlFile = GetCachedCrlPath(cert, mkDir: true); using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "wb")) { if (bio.IsInvalid || Interop.Crypto.PemWriteBioX509Crl(bio, crl) == 0) { // No bio, or write failed Interop.Crypto.ErrClearError(); } } } catch (UnauthorizedAccessException) { } catch (IOException) { } } } }
private static bool AddCachedCrl(X509Certificate2 cert, SafeX509StoreHandle store, DateTime verificationTime) { string crlFile = GetCachedCrlPath(cert); using (SafeBioHandle bio = Interop.libcrypto.BIO_new_file(crlFile, "rb")) { if (bio.IsInvalid) { 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.libcrypto.PEM_read_bio_X509_CRL(bio)) { if (crl.IsInvalid) { 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. DateTime nextUpdate = OpenSslX509CertificateReader.ExtractValidityDateTime( Interop.Crypto.GetX509CrlNextUpdate(crl)); // 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); } // TODO (#3063): Check the return value of X509_STORE_add_crl, and throw on any error other // than X509_R_CERT_ALREADY_IN_HASH_TABLE Interop.libcrypto.X509_STORE_add_crl(store, crl); return(true); } } }
internal static SafeX509CrlHandle?DownloadCrl(string uri, TimeSpan downloadTimeout) { byte[]? data = DownloadAsset(uri, downloadTimeout); if (data == null) { return(null); } // DER-encoded CRL seems to be the most common off of some random spot-checking, so try DER first. SafeX509CrlHandle handle = Interop.Crypto.DecodeX509Crl(data, data.Length); if (!handle.IsInvalid) { return(handle); } handle.Dispose(); using (SafeBioHandle bio = Interop.Crypto.CreateMemoryBio()) { Interop.Crypto.CheckValidOpenSslHandle(bio); Interop.Crypto.BioWrite(bio, data, data.Length); handle = Interop.Crypto.PemReadBioX509Crl(bio); // DecodeX509Crl failed, so we need to clear its error. // If PemReadBioX509Crl failed, clear that too. Interop.Crypto.ErrClearError(); if (!handle.IsInvalid) { return(handle); } handle.Dispose(); } if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.InvalidDownloadedCrl(); } return(null); }
private static void DownloadAndAddCrl( X509Certificate2 cert, SafeX509StoreHandle store, ref TimeSpan remainingDownloadTime) { string url = GetCdpUrl(cert); if (url == null) { return; } // X509_STORE_add_crl will increase the refcount on the CRL object, so we should still // dispose our copy. using (SafeX509CrlHandle crl = CertificateAssetDownloader.DownloadCrl(url, ref remainingDownloadTime)) { // null is a valid return (e.g. no remainingDownloadTime) if (crl != null && !crl.IsInvalid) { // TODO (#3063): Check the return value of X509_STORE_add_crl, and throw on any error other // than X509_R_CERT_ALREADY_IN_HASH_TABLE Interop.libcrypto.X509_STORE_add_crl(store, crl); // Saving the CRL to the disk is just a performance optimization for later requests to not // need to use the network again, so failure to save shouldn't throw an exception or mark // the chain as invalid. try { string crlFile = GetCachedCrlPath(cert, mkDir: true); using (SafeBioHandle bio = Interop.libcrypto.BIO_new_file(crlFile, "wb")) { if (!bio.IsInvalid) { Interop.libcrypto.PEM_write_bio_X509_CRL(bio, crl); } } } catch (IOException) { } } } }
internal static extern int PemWriteBioX509Crl(SafeBioHandle bio, SafeX509CrlHandle crl);
internal static extern bool X509StoreAddCrl(SafeX509StoreHandle ctx, SafeX509CrlHandle x);
internal static extern bool X509_STORE_add_crl(SafeX509StoreHandle ctx, SafeX509CrlHandle x);
internal static extern int PEM_write_bio_X509_CRL(SafeBioHandle bio, SafeX509CrlHandle crl);
internal static extern IntPtr GetX509CrlNextUpdate(SafeX509CrlHandle crl);
internal static partial int PemWriteBioX509Crl(SafeBioHandle bio, SafeX509CrlHandle crl);
internal static partial bool X509StoreAddCrl(SafeX509StoreHandle ctx, SafeX509CrlHandle x);
internal static partial IntPtr GetX509CrlNextUpdate(SafeX509CrlHandle crl);
private static bool AddCachedCrlCore(string crlFile, SafeX509StoreHandle store, DateTime verificationTime) { using (SafeBioHandle bio = Interop.Crypto.BioNewFile(crlFile, "rb")) { if (bio.IsInvalid) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.CrlCacheOpenError(); } 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) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.CrlCacheDecodeError(); } 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) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.CrlCacheFileBasedExpiry(); } 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) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.CrlCacheExpired(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(); } } if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.CrlCacheAcceptedFile(nextUpdate); } return(true); } } }