internal static bool TryReadX509Der(SafeBioHandle bio, out ICertificatePal fromBio) { SafeX509Handle cert = Interop.Crypto.ReadX509AsDerFromBio(bio); if (cert.IsInvalid) { cert.Dispose(); fromBio = null; return(false); } fromBio = new OpenSslX509CertificateReader(cert); return(true); }
internal static bool TryReadX509Pem(SafeBioHandle bio, out ICertificatePal certPal) { SafeX509Handle cert = Interop.Crypto.PemReadX509FromBio(bio); if (cert.IsInvalid) { cert.Dispose(); certPal = null; return(false); } certPal = new OpenSslX509CertificateReader(cert); return(true); }
internal static bool TryReadX509Der(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle = Interop.Crypto.DecodeX509(rawData, rawData.Length); if (certHandle.IsInvalid) { certHandle.Dispose(); certPal = null; return(false); } certPal = new OpenSslX509CertificateReader(certHandle); return(true); }
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); } } }
private static string GetCdpUrl(SafeX509Handle cert) { ArraySegment <byte> crlDistributionPoints = OpenSslX509CertificateReader.FindFirstExtension(cert, Oids.CrlDistributionPoints); if (crlDistributionPoints.Array == null) { return(null); } try { AsnValueReader reader = new AsnValueReader(crlDistributionPoints, AsnEncodingRules.DER); AsnValueReader sequenceReader = reader.ReadSequence(); reader.ThrowIfNotEmpty(); while (sequenceReader.HasData) { DistributionPointAsn.Decode(ref sequenceReader, crlDistributionPoints, out DistributionPointAsn distributionPoint); // Only distributionPoint is supported // Only fullName is supported, nameRelativeToCRLIssuer is for LDAP-based lookup. if (distributionPoint.DistributionPoint.HasValue && distributionPoint.DistributionPoint.Value.FullName != null) { foreach (GeneralNameAsn name in distributionPoint.DistributionPoint.Value.FullName) { if (name.Uri != null && Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri uri) && uri.Scheme == "http") { return(name.Uri); } } } } } catch (CryptographicException) { // Treat any ASN errors as if the extension was missing. } finally { // The data came from a certificate, so it's public. CryptoPool.Return(crlDistributionPoints.Array, clearSize: 0); } return(null); }
internal static bool TryReadX509PemNoAux(SafeBioHandle bio, [NotNullWhen(true)] out ICertificatePal?certPal) { SafeX509Handle cert = Interop.Crypto.PemReadX509FromBio(bio); if (cert.IsInvalid) { cert.Dispose(); certPal = null; Interop.Crypto.ErrClearError(); return(false); } certPal = new OpenSslX509CertificateReader(cert); return(true); }
internal static unsafe bool TryReadX509Der(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle = Interop.libcrypto.OpenSslD2I( (ptr, b, i) => Interop.libcrypto.d2i_X509(ptr, b, i), rawData, checkHandle: false); if (certHandle.IsInvalid) { certPal = null; return(false); } certPal = new OpenSslX509CertificateReader(certHandle); return(true); }
// Handles both DER and PEM internal static bool TryReadX509(ReadOnlySpan <byte> rawData, [NotNullWhen(true)] out ICertificatePal?handle) { handle = null; SafeX509Handle certHandle = Interop.AndroidCrypto.DecodeX509( ref MemoryMarshal.GetReference(rawData), rawData.Length); if (certHandle.IsInvalid) { certHandle.Dispose(); return(false); } handle = new OpenSslX509CertificateReader(certHandle); return(true); }
internal static bool TryReadX509Der(ReadOnlySpan <byte> rawData, out ICertificatePal certPal) { SafeX509Handle certHandle = Interop.Crypto.DecodeX509( ref MemoryMarshal.GetReference(rawData), rawData.Length); if (certHandle.IsInvalid) { certHandle.Dispose(); certPal = null; Interop.Crypto.ErrClearError(); return(false); } certPal = new OpenSslX509CertificateReader(certHandle); return(true); }
private static string GetCdpUrl(SafeX509Handle cert) { ArraySegment <byte> crlDistributionPoints = OpenSslX509CertificateReader.FindFirstExtension(cert, Oids.CrlDistributionPoints); if (crlDistributionPoints.Array == null) { return(null); } try { AsnReader reader = new AsnReader(crlDistributionPoints, AsnEncodingRules.DER); AsnReader sequenceReader = reader.ReadSequence(); reader.ThrowIfNotEmpty(); while (sequenceReader.HasData) { DistributionPointAsn.Decode(sequenceReader, out DistributionPointAsn distributionPoint); // Only distributionPoint is supported // Only fullName is supported, nameRelativeToCRLIssuer is for LDAP-based lookup. if (distributionPoint.DistributionPoint.HasValue && distributionPoint.DistributionPoint.Value.FullName != null) { foreach (GeneralNameAsn name in distributionPoint.DistributionPoint.Value.FullName) { if (name.Uri != null && Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri uri) && uri.Scheme == "http") { return(name.Uri); } } } } return(null); } finally { ArrayPool <byte> .Shared.Return(crlDistributionPoints.Array); } }
private static bool TryReadPkcs12( OpenSslPkcs12Reader pfx, SafePasswordHandle password, bool single, bool ephemeralSpecified, out ICertificatePal?readPal, out List <ICertificatePal>?readCerts) { pfx.Decrypt(password, ephemeralSpecified); if (single) { UnixPkcs12Reader.CertAndKey certAndKey = pfx.GetSingleCert(); OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)certAndKey.Cert !; if (certAndKey.Key != null) { pal.SetPrivateKey(OpenSslPkcs12Reader.GetPrivateKey(certAndKey.Key)); } readPal = pal; readCerts = null; return(true); } readPal = null; List <ICertificatePal> certs = new List <ICertificatePal>(pfx.GetCertCount()); foreach (UnixPkcs12Reader.CertAndKey certAndKey in pfx.EnumerateAll()) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)certAndKey.Cert !; if (certAndKey.Key != null) { pal.SetPrivateKey(OpenSslPkcs12Reader.GetPrivateKey(certAndKey.Key)); } certs.Add(pal); } readCerts = certs; return(true); }
internal static bool TryReadX509Pem(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle; using (SafeBioHandle bio = Interop.libcrypto.BIO_new(Interop.libcrypto.BIO_s_mem())) { Interop.Crypto.CheckValidOpenSslHandle(bio); Interop.libcrypto.BIO_write(bio, rawData, rawData.Length); certHandle = Interop.libcrypto.PEM_read_bio_X509_AUX(bio, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } if (certHandle.IsInvalid) { certPal = null; return(false); } certPal = new OpenSslX509CertificateReader(certHandle); return(true); }
public void Remove(ICertificatePal certPal) { OpenSslX509CertificateReader cert = (OpenSslX509CertificateReader)certPal; using (X509Certificate2 copy = new X509Certificate2(cert.DuplicateHandles())) { bool hadCandidates; string currentFilename = FindExistingFilename(copy, _storePath, out hadCandidates); if (currentFilename != null) { if (_readOnly) { // Windows compatibility, the readonly check isn't done until after a match is found. throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); } File.Delete(currentFilename); } } }
public void FindBySubjectKeyIdentifier(byte[] keyIdentifier) { FindCore( cert => { X509Extension ext = FindExtension(cert, Oids.SubjectKeyIdentifier); byte[] certKeyId; if (ext != null) { // The extension exposes the value as a hexadecimal string, or we can decode here. // Enough parsing has gone on, let's decode. certKeyId = OpenSslX509Encoder.DecodeX509SubjectKeyIdentifierExtension(ext.RawData); } else { // The Desktop/Windows version of this method use CertGetCertificateContextProperty // with a property ID of CERT_KEY_IDENTIFIER_PROP_ID. // // MSDN says that when there's no extension, this method takes the SHA-1 of the // SubjectPublicKeyInfo block, and returns that. // // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376079%28v=vs.85%29.aspx OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal; using (HashAlgorithm hash = SHA1.Create()) { byte[] publicKeyInfoBytes = Interop.Crypto.OpenSslEncode( Interop.Crypto.GetX509SubjectPublicKeyInfoDerSize, Interop.Crypto.EncodeX509SubjectPublicKeyInfo, certPal.SafeHandle); certKeyId = hash.ComputeHash(publicKeyInfoBytes); } } return(keyIdentifier.ContentsEqual(certKeyId)); }); }
public List <OpenSslX509CertificateReader> ReadCertificates() { var certs = new List <OpenSslX509CertificateReader>(); if (_caStackHandle != null && !_caStackHandle.IsInvalid) { int caCertCount = Interop.Crypto.GetX509StackFieldCount(_caStackHandle); for (int i = 0; i < caCertCount; i++) { IntPtr certPtr = Interop.Crypto.GetX509StackField(_caStackHandle, i); if (certPtr != IntPtr.Zero) { // The STACK_OF(X509) still needs to be cleaned up, so upref the handle out of it. certs.Add(new OpenSslX509CertificateReader(Interop.Crypto.X509UpRef(certPtr))); } } } if (_x509Handle != null && !_x509Handle.IsInvalid) { // The certificate and (if applicable) private key handles will be given over // to the OpenSslX509CertificateReader, and the fields here are thus nulled out to // prevent double-Dispose. OpenSslX509CertificateReader reader = new OpenSslX509CertificateReader(_x509Handle); _x509Handle = null; if (_evpPkeyHandle != null && !_evpPkeyHandle.IsInvalid) { reader.SetPrivateKey(_evpPkeyHandle); _evpPkeyHandle = null; } certs.Add(reader); } return(certs); }
private static string GetCachedCrlPath(X509Certificate2 cert, bool mkDir = false) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; string crlDir = PersistedFiles.GetUserFeatureDirectory( X509Persistence.CryptographyFeatureName, X509Persistence.CrlsSubFeatureName); // X509_issuer_name_hash returns "unsigned long", which is marshalled as UIntPtr. // But it only sets 32 bits worth of data, so force it down to uint just... in case. ulong persistentHashLong = Interop.libcrypto.X509_issuer_name_hash(pal.SafeHandle).ToUInt64(); uint persistentHash = unchecked ((uint)persistentHashLong); // OpenSSL's hashed filename algorithm is the 8-character hex version of the 32-bit value // of X509_issuer_name_hash (or X509_subject_name_hash, depending on the context). string localFileName = persistentHash.ToString("x8") + ".crl"; if (mkDir) { Directory.CreateDirectory(crlDir); } return(Path.Combine(crlDir, localFileName)); }
internal static bool TryReadX509Der(SafeBioHandle bio, out ICertificatePal fromBio) { SafeX509Handle cert = Interop.Crypto.ReadX509AsDerFromBio(bio); if (cert.IsInvalid) { cert.Dispose(); fromBio = null; return false; } fromBio = new OpenSslX509CertificateReader(cert); return true; }
internal static bool TryReadX509Pem(SafeBioHandle bio, out ICertificatePal certPal) { SafeX509Handle cert = Interop.Crypto.PemReadX509FromBio(bio); if (cert.IsInvalid) { cert.Dispose(); certPal = null; return false; } certPal = new OpenSslX509CertificateReader(cert); return true; }
internal static bool TryReadX509Der(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle = Interop.Crypto.DecodeX509(rawData, rawData.Length); if (certHandle.IsInvalid) { certHandle.Dispose(); certPal = null; return false; } certPal = new OpenSslX509CertificateReader(certHandle); return true; }
public static IChainPal BuildChain( X509Certificate2 leaf, HashSet <X509Certificate2> candidates, HashSet <X509Certificate2> downloaded, HashSet <X509Certificate2> systemTrusted, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, DateTime verificationTime, ref TimeSpan remainingDownloadTime) { X509ChainElement[] elements; List <X509ChainStatus> overallStatus = new List <X509ChainStatus>(); WorkingChain workingChain = new WorkingChain(); Interop.Crypto.X509StoreVerifyCallback workingCallback = workingChain.VerifyCallback; // An X509_STORE is more comparable to Cryptography.X509Certificate2Collection than to // Cryptography.X509Store. So read this with OpenSSL eyes, not CAPI/CNG eyes. // // (If you need to think of it as an X509Store, it's a volatile memory store) using (SafeX509StoreHandle store = Interop.Crypto.X509StoreCreate()) using (SafeX509StoreCtxHandle storeCtx = Interop.Crypto.X509StoreCtxCreate()) { Interop.Crypto.CheckValidOpenSslHandle(store); Interop.Crypto.CheckValidOpenSslHandle(storeCtx); bool lookupCrl = revocationMode != X509RevocationMode.NoCheck; foreach (X509Certificate2 cert in candidates) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; if (!Interop.Crypto.X509StoreAddCert(store, pal.SafeHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } if (lookupCrl) { CrlCache.AddCrlForCertificate( cert, store, revocationMode, verificationTime, ref remainingDownloadTime); // If we only wanted the end-entity certificate CRL then don't look up // any more of them. lookupCrl = revocationFlag != X509RevocationFlag.EndCertificateOnly; } } if (revocationMode != X509RevocationMode.NoCheck) { if (!Interop.Crypto.X509StoreSetRevocationFlag(store, revocationFlag)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } } SafeX509Handle leafHandle = ((OpenSslX509CertificateReader)leaf.Pal).SafeHandle; if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Interop.Crypto.X509StoreCtxSetVerifyCallback(storeCtx, workingCallback); Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime); int verify = Interop.Crypto.X509VerifyCert(storeCtx); if (verify < 0) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // Because our callback tells OpenSSL that every problem is ignorable, it should tell us that the // chain is just fine (unless it returned a negative code for an exception) Debug.Assert(verify == 1, "verify == 1"); using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); elements = new X509ChainElement[chainSize]; int maybeRootDepth = chainSize - 1; // The leaf cert is 0, up to (maybe) the root at chainSize - 1 for (int i = 0; i < chainSize; i++) { List <X509ChainStatus> status = new List <X509ChainStatus>(); List <Interop.Crypto.X509VerifyStatusCode> elementErrors = i < workingChain.Errors.Count ? workingChain.Errors[i] : null; if (elementErrors != null) { AddElementStatus(elementErrors, status, overallStatus); } IntPtr elementCertPtr = Interop.Crypto.GetX509StackField(chainStack, i); if (elementCertPtr == IntPtr.Zero) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // Duplicate the certificate handle X509Certificate2 elementCert = new X509Certificate2(elementCertPtr); // If the last cert is self signed then it's the root cert, do any extra checks. if (i == maybeRootDepth && IsSelfSigned(elementCert)) { // If the root certificate was downloaded or the system // doesn't trust it, it's untrusted. if (downloaded.Contains(elementCert) || !systemTrusted.Contains(elementCert)) { AddElementStatus( Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED, status, overallStatus); } } elements[i] = new X509ChainElement(elementCert, status.ToArray(), ""); } } } GC.KeepAlive(workingCallback); if ((certificatePolicy != null && certificatePolicy.Count > 0) || (applicationPolicy != null && applicationPolicy.Count > 0)) { List <X509Certificate2> certsToRead = new List <X509Certificate2>(); foreach (X509ChainElement element in elements) { certsToRead.Add(element.Certificate); } CertificatePolicyChain policyChain = new CertificatePolicyChain(certsToRead); bool failsPolicyChecks = false; if (certificatePolicy != null) { if (!policyChain.MatchesCertificatePolicies(certificatePolicy)) { failsPolicyChecks = true; } } if (applicationPolicy != null) { if (!policyChain.MatchesApplicationPolicies(applicationPolicy)) { failsPolicyChecks = true; } } if (failsPolicyChecks) { X509ChainElement leafElement = elements[0]; X509ChainStatus chainStatus = new X509ChainStatus { Status = X509ChainStatusFlags.InvalidPolicyConstraints, StatusInformation = SR.Chain_NoPolicyMatch, }; var elementStatus = new List <X509ChainStatus>(leafElement.ChainElementStatus.Length + 1); elementStatus.AddRange(leafElement.ChainElementStatus); AddUniqueStatus(elementStatus, ref chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); elements[0] = new X509ChainElement( leafElement.Certificate, elementStatus.ToArray(), leafElement.Information); } } return(new OpenSslX509ChainProcessor { ChainStatus = overallStatus.ToArray(), ChainElements = elements, }); }
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); }
public static ICertificatePal FromBlob(ReadOnlySpan <byte> rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { return(OpenSslX509CertificateReader.FromBlob(rawData, password, keyStorageFlags)); }
public static ICertificatePal FromHandle(IntPtr handle) { return(OpenSslX509CertificateReader.FromHandle(handle)); }
public void Add(ICertificatePal certPal) { if (_readOnly) { // Windows compatibility: Remove only throws when it needs to do work, add throws always. throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); } // This may well be the first time that we've added something to this store. Directory.CreateDirectory(_storePath); uint userId = Interop.Sys.GetEUid(); EnsureDirectoryPermissions(_storePath, userId); OpenSslX509CertificateReader cert = (OpenSslX509CertificateReader)certPal; using (X509Certificate2 copy = new X509Certificate2(cert.DuplicateHandles())) { string thumbprint = copy.Thumbprint; bool findOpenSlot; // The odds are low that we'd have a thumbprint collision, but check anyways. string existingFilename = FindExistingFilename(copy, _storePath, out findOpenSlot); if (existingFilename != null) { if (!copy.HasPrivateKey) { return; } try { using (X509Certificate2 fromFile = new X509Certificate2(existingFilename)) { if (fromFile.HasPrivateKey) { // We have a private key, the file has a private key, we're done here. return; } } } catch (CryptographicException) { // We can't read this file anymore, but a moment ago it was this certificate, // so go ahead and overwrite it. } } string destinationFilename; FileMode mode = FileMode.CreateNew; if (existingFilename != null) { destinationFilename = existingFilename; mode = FileMode.Create; } else if (findOpenSlot) { destinationFilename = FindOpenSlot(thumbprint); } else { destinationFilename = Path.Combine(_storePath, thumbprint + PfxExtension); } using (FileStream stream = new FileStream(destinationFilename, mode)) { EnsureFilePermissions(stream, userId); byte[] pkcs12 = copy.Export(X509ContentType.Pkcs12); stream.Write(pkcs12, 0, pkcs12.Length); } } }
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(); }
public List<OpenSslX509CertificateReader> ReadCertificates() { var certs = new List<OpenSslX509CertificateReader>(); if (_caStackHandle != null && !_caStackHandle.IsInvalid) { int caCertCount = Interop.Crypto.GetX509StackFieldCount(_caStackHandle); for (int i = 0; i < caCertCount; i++) { IntPtr certPtr = Interop.Crypto.GetX509StackField(_caStackHandle, i); if (certPtr != IntPtr.Zero) { // The STACK_OF(X509) still needs to be cleaned up, so upref the handle out of it. certs.Add(new OpenSslX509CertificateReader(Interop.Crypto.X509UpRef(certPtr))); } } } if (_x509Handle != null && !_x509Handle.IsInvalid) { // The certificate and (if applicable) private key handles will be given over // to the OpenSslX509CertificateReader, and the fields here are thus nulled out to // prevent double-Dispose. OpenSslX509CertificateReader reader = new OpenSslX509CertificateReader(_x509Handle); _x509Handle = null; if (_evpPkeyHandle != null && !_evpPkeyHandle.IsInvalid) { reader.SetPrivateKey(_evpPkeyHandle); _evpPkeyHandle = null; } certs.Add(reader); } return certs; }
internal Interop.Crypto.X509VerifyStatusCode FindChainViaAia( ref List <X509Certificate2> downloadedCerts) { IntPtr lastCert = IntPtr.Zero; SafeX509StoreCtxHandle storeCtx = _storeCtx; Interop.Crypto.X509VerifyStatusCode statusCode = Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; while (!IsCompleteChain(statusCode)) { using (SafeX509Handle currentCert = Interop.Crypto.X509StoreCtxGetCurrentCert(storeCtx)) { IntPtr currentHandle = currentCert.DangerousGetHandle(); // No progress was made, give up. if (currentHandle == lastCert) { break; } lastCert = currentHandle; ArraySegment <byte> authorityInformationAccess = OpenSslX509CertificateReader.FindFirstExtension( currentCert, Oids.AuthorityInformationAccess); if (authorityInformationAccess.Count == 0) { break; } X509Certificate2 downloaded = DownloadCertificate( authorityInformationAccess, ref _remainingDownloadTime); // The AIA record is contained in a public structure, so no need to clear it. CryptoPool.Return(authorityInformationAccess.Array, clearSize: 0); if (downloaded == null) { break; } if (downloadedCerts == null) { downloadedCerts = new List <X509Certificate2>(); } AddToStackAndUpRef(downloaded.Handle, _untrustedLookup); downloadedCerts.Add(downloaded); Interop.Crypto.X509StoreCtxRebuildChain(storeCtx); statusCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); } } if (statusCode == Interop.Crypto.X509VerifyStatusCode.X509_V_OK && downloadedCerts != null) { using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(_storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); Span <IntPtr> tempChain = stackalloc IntPtr[DefaultChainCapacity]; byte[] tempChainRent = null; if (chainSize <= tempChain.Length) { tempChain = tempChain.Slice(0, chainSize); } else { int targetSize = checked (chainSize * IntPtr.Size); tempChainRent = CryptoPool.Rent(targetSize); tempChain = MemoryMarshal.Cast <byte, IntPtr>(tempChainRent.AsSpan(0, targetSize)); } for (int i = 0; i < chainSize; i++) { tempChain[i] = Interop.Crypto.GetX509StackField(chainStack, i); } // In the average case we never made it here. // // Given that we made it here, in the average remaining case // we are doing a one item for which will match in the second position // of an (on-average) 3 item collection. // // The only case where this loop really matters is if downloading the // certificate made an alternate chain better, which may have resulted in // an extra download and made the first one not be involved any longer. In // that case, it's a 2 item for loop matching against a three item set. // // So N*M is well contained. for (int i = downloadedCerts.Count - 1; i >= 0; i--) { X509Certificate2 downloadedCert = downloadedCerts[i]; if (!tempChain.Contains(downloadedCert.Handle)) { downloadedCert.Dispose(); downloadedCerts.RemoveAt(i); } } if (downloadedCerts.Count == 0) { downloadedCerts = null; } if (tempChainRent != null) { CryptoPool.Return(tempChainRent); } } } return(statusCode); }
protected override X509Certificate2 CloneCertificate(X509Certificate2 cert) { OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal; return(new X509Certificate2(certPal.DuplicateHandles())); }
public void Add(ICertificatePal certPal) { if (_readOnly) { // Windows compatibility: Remove only throws when it needs to do work, add throws always. throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); } // Save the collection to a local so it's consistent for the whole method List <X509Certificate2> certificates = _certificates; OpenSslX509CertificateReader cert = (OpenSslX509CertificateReader)certPal; using (X509Certificate2 copy = new X509Certificate2(cert.DuplicateHandles())) { // certificates will be null if anything has changed since the last call to // get_Certificates; including Add being called without get_Certificates being // called at all. if (certificates != null) { foreach (X509Certificate2 inCollection in certificates) { if (inCollection.Equals(copy)) { if (!copy.HasPrivateKey || inCollection.HasPrivateKey) { // If the existing store only knows about a public key, but we're // adding a public+private pair, continue with the add. // // So, therefore, if we aren't adding a private key, or already have one, // we don't need to do anything. return; } } } } // This may well be the first time that we've added something to this store. Directory.CreateDirectory(_storePath); uint userId = Interop.Sys.GetEUid(); EnsureDirectoryPermissions(_storePath, userId); string thumbprint = copy.Thumbprint; bool findOpenSlot; // The odds are low that we'd have a thumbprint collision, but check anyways. string existingFilename = FindExistingFilename(copy, _storePath, out findOpenSlot); if (existingFilename != null) { bool dataExistsAlready = false; // If the file on disk is just a public key, but we're trying to add a private key, // we'll want to overwrite it. if (copy.HasPrivateKey) { try { using (X509Certificate2 fromFile = new X509Certificate2(existingFilename)) { if (fromFile.HasPrivateKey) { // We have a private key, the file has a private key, we're done here. dataExistsAlready = true; } } } catch (CryptographicException) { // We can't read this file anymore, but a moment ago it was this certificate, // so go ahead and overwrite it. } } else { // If we're just a public key then the file has at least as much data as we do. dataExistsAlready = true; } if (dataExistsAlready) { // The file was added but our collection hasn't resynced yet. // Set _certificates to null to force a resync. _certificates = null; return; } } string destinationFilename; FileMode mode = FileMode.CreateNew; if (existingFilename != null) { destinationFilename = existingFilename; mode = FileMode.Create; } else if (findOpenSlot) { destinationFilename = FindOpenSlot(thumbprint); } else { destinationFilename = Path.Combine(_storePath, thumbprint + PfxExtension); } using (FileStream stream = new FileStream(destinationFilename, mode)) { EnsureFilePermissions(stream, userId); byte[] pkcs12 = copy.Export(X509ContentType.Pkcs12); stream.Write(pkcs12, 0, pkcs12.Length); } } // Null out _certificates so the next call to get_Certificates causes a re-scan. _certificates = null; }
internal static unsafe bool TryReadX509Der(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle = Interop.libcrypto.OpenSslD2I( (ptr, b, i) => Interop.libcrypto.d2i_X509(ptr, b, i), rawData, checkHandle: false); if (certHandle.IsInvalid) { certPal = null; return false; } certPal = new OpenSslX509CertificateReader(certHandle); return true; }
internal OpenSslX509CertificateReader DuplicateHandles() { SafeX509Handle certHandle = Interop.Crypto.X509Duplicate(_cert); OpenSslX509CertificateReader duplicate = new OpenSslX509CertificateReader(certHandle); if (_privateKey != null) { SafeEvpPKeyHandle keyHandle = _privateKey.DuplicateHandle(); duplicate.SetPrivateKey(keyHandle); } return duplicate; }
internal static bool TryReadX509Pem(byte[] rawData, out ICertificatePal certPal) { SafeX509Handle certHandle; using (SafeBioHandle bio = Interop.libcrypto.BIO_new(Interop.libcrypto.BIO_s_mem())) { Interop.libcrypto.CheckValidOpenSslHandle(bio); Interop.libcrypto.BIO_write(bio, rawData, rawData.Length); certHandle = Interop.libcrypto.PEM_read_bio_X509_AUX(bio, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } if (certHandle.IsInvalid) { certPal = null; return false; } certPal = new OpenSslX509CertificateReader(certHandle); return true; }
public static ICertificatePal FromOtherCert(X509Certificate cert) { return(OpenSslX509CertificateReader.FromOtherCert(cert)); }
internal static bool TryReadX509Pem(SafeBioHandle bio, out ICertificatePal certPal) { SafeX509Handle cert = Interop.libcrypto.PEM_read_bio_X509_AUX(bio, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (cert.IsInvalid) { certPal = null; return false; } certPal = new OpenSslX509CertificateReader(cert); return true; }
public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { return(OpenSslX509CertificateReader.FromFile(fileName, password, keyStorageFlags)); }
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); }
private byte[] ExportPfx(string password) { using (SafeX509StackHandle publicCerts = Interop.Crypto.NewX509Stack()) { X509Certificate2 privateCert = null; // Walk the collection backwards, because we're pushing onto a stack. // This will cause the read order later to be the same as it was now. for (int i = _certs.Length - 1; i >= 0; --i) { X509Certificate2 cert = _certs[i]; if (cert.HasPrivateKey) { if (privateCert != null) { // OpenSSL's PKCS12 accelerator (PKCS12_create) only supports one // private key. The data structure supports more than one, but // being able to use that functionality requires a lot more code for // a low-usage scenario. throw new PlatformNotSupportedException(SR.NotSupported_Export_MultiplePrivateCerts); } privateCert = cert; } else { using (SafeX509Handle certHandle = Interop.libcrypto.X509_dup(cert.Handle)) { if (!Interop.Crypto.PushX509StackField(publicCerts, certHandle)) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } // The handle ownership has been transferred into the STACK_OF(X509). certHandle.SetHandleAsInvalid(); } } } SafeX509Handle privateCertHandle; SafeEvpPKeyHandle privateCertKeyHandle; if (privateCert != null) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)privateCert.Pal; privateCertHandle = pal.SafeHandle; privateCertKeyHandle = pal.PrivateKeyHandle ?? InvalidPKeyHandle; } else { privateCertHandle = SafeX509Handle.InvalidHandle; privateCertKeyHandle = InvalidPKeyHandle; } using (SafePkcs12Handle pkcs12 = Interop.libcrypto.PKCS12_create( password, null, privateCertKeyHandle, privateCertHandle, publicCerts, Interop.libcrypto.NID_undef, Interop.libcrypto.NID_undef, Interop.libcrypto.PKCS12_DEFAULT_ITER, Interop.libcrypto.PKCS12_DEFAULT_ITER, 0)) { if (pkcs12.IsInvalid) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } unsafe { return(Interop.libcrypto.OpenSslI2D( (handle, b) => Interop.libcrypto.i2d_PKCS12(handle, b), pkcs12)); } } } }
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; }