public static ILoaderPal FromBlob(byte[] rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); ICertificatePal singleCert; if (OpenSslX509CertificateReader.TryReadX509Der(rawData, out singleCert) || OpenSslX509CertificateReader.TryReadX509Pem(rawData, out singleCert)) { // The single X509 structure methods shouldn't return true and out null, only empty // collections have that behavior. Debug.Assert(singleCert != null); return(SingleCertToLoaderPal(singleCert)); } List <ICertificatePal> certPals; Exception openSslException; if (PkcsFormatReader.TryReadPkcs7Der(rawData, out certPals) || PkcsFormatReader.TryReadPkcs7Pem(rawData, out certPals) || PkcsFormatReader.TryReadPkcs12(rawData, password, out certPals, out openSslException)) { Debug.Assert(certPals != null); return(ListToLoaderPal(certPals)); } Debug.Assert(openSslException != null); throw openSslException; }
public X509ContentType GetCertContentType(byte[] rawData) { { ICertificatePal certPal; if (OpenSslX509CertificateReader.TryReadX509Der(rawData, out certPal) || OpenSslX509CertificateReader.TryReadX509Pem(rawData, out certPal)) { certPal.Dispose(); return(X509ContentType.Cert); } } if (PkcsFormatReader.IsPkcs7(rawData)) { return(X509ContentType.Pkcs7); } { OpenSslPkcs12Reader pfx; if (OpenSslPkcs12Reader.TryRead(rawData, out pfx)) { pfx.Dispose(); return(X509ContentType.Pkcs12); } } // Unsupported format. // Windows throws new CryptographicException(CRYPT_E_NO_MATCH) throw new CryptographicException(); }
protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory <byte> data) { if (OpenSslX509CertificateReader.TryReadX509Der(data.Span, out ICertificatePal? ret)) { return(ret); } throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); }
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 we don'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); }
private static ILoaderPal FromBio(SafeBioHandle bio, SafePasswordHandle password) { int bioPosition = Interop.Crypto.BioTell(bio); Debug.Assert(bioPosition >= 0); ICertificatePal singleCert; if (OpenSslX509CertificateReader.TryReadX509Pem(bio, out singleCert)) { return(SingleCertToLoaderPal(singleCert)); } // Rewind, try again. OpenSslX509CertificateReader.RewindBio(bio, bioPosition); if (OpenSslX509CertificateReader.TryReadX509Der(bio, out singleCert)) { return(SingleCertToLoaderPal(singleCert)); } // Rewind, try again. OpenSslX509CertificateReader.RewindBio(bio, bioPosition); List <ICertificatePal> certPals; if (PkcsFormatReader.TryReadPkcs7Pem(bio, out certPals)) { return(ListToLoaderPal(certPals)); } // Rewind, try again. OpenSslX509CertificateReader.RewindBio(bio, bioPosition); if (PkcsFormatReader.TryReadPkcs7Der(bio, out certPals)) { return(ListToLoaderPal(certPals)); } // Rewind, try again. OpenSslX509CertificateReader.RewindBio(bio, bioPosition); // Capture the exception so in case of failure, the call to BioSeek does not override it. Exception openSslException; if (PkcsFormatReader.TryReadPkcs12(bio, password, out certPals, out openSslException)) { return(ListToLoaderPal(certPals)); } // Since we aren't going to finish reading, leaving the buffer where it was when we got // it seems better than leaving it in some arbitrary other position. // // Use BioSeek directly for the last seek attempt, because any failure here should instead // report the already created (but not yet thrown) exception. if (Interop.Crypto.BioSeek(bio, bioPosition) < 0) { Interop.Crypto.ErrClearError(); } Debug.Assert(openSslException != null); throw openSslException; }
private static void LoadMachineStores() { Debug.Assert( Monitor.IsEntered(s_machineLoadLock), "LoadMachineStores assumes a lock(s_machineLoadLock)"); var rootStore = new List <X509Certificate2>(); var intermedStore = new List <X509Certificate2>(); 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 = trustedCertFiles.Prepend(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")) { // The handle may be invalid, for example when we don't have read permission for the file. if (fileBio.IsInvalid) { Interop.Crypto.ErrClearError(); continue; } ICertificatePal pal; // 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. 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)) { rootStore.Add(cert); continue; } } else { if (uniqueIntermediateCerts.Add(cert)) { intermedStore.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(); } } } var rootStorePal = new CollectionBackedStoreProvider(rootStore); s_machineIntermediateStore = new CollectionBackedStoreProvider(intermedStore); // s_machineRootStore's nullarity is the loaded-state sentinel, so write it with Volatile. Debug.Assert(Monitor.IsEntered(s_machineLoadLock), "LoadMachineStores assumes a lock(s_machineLoadLock)"); Volatile.Write(ref s_machineRootStore, rootStorePal); }
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(); }