private bool AcquireServerCredentials(ref byte[] thumbPrint) { X509Certificate serverCertificate = null; if (this.m_CertSelectionDelegate != null) { X509CertificateCollection localCertificates = new X509CertificateCollection(); localCertificates.Add(this.m_ServerCertificate); serverCertificate = this.m_CertSelectionDelegate(string.Empty, localCertificates, null, new string[0]); } else { serverCertificate = this.m_ServerCertificate; } if (serverCertificate == null) { throw new NotSupportedException(SR.GetString("net_ssl_io_no_server_cert")); } X509Certificate2 certificate2 = this.EnsurePrivateKey(serverCertificate); if (certificate2 == null) { throw new NotSupportedException(SR.GetString("net_ssl_io_no_server_cert")); } byte[] certHash = certificate2.GetCertHash(); try { SafeFreeCredentials credentials = SslSessionsCache.TryCachedCredential(certHash, this.m_ProtocolFlags, this.m_EncryptionPolicy); if (credentials != null) { this.m_CredentialsHandle = credentials; this.m_ServerCertificate = serverCertificate; return(true); } SecureCredential secureCredential = new SecureCredential(4, certificate2, SecureCredential.Flags.Zero, this.m_ProtocolFlags, this.m_EncryptionPolicy); this.m_CredentialsHandle = this.AcquireCredentialsHandle(CredentialUse.Inbound, ref secureCredential); thumbPrint = certHash; this.m_ServerCertificate = serverCertificate; } finally { if (serverCertificate != certificate2) { certificate2.Reset(); } } return(false); }
private bool AcquireClientCredentials(ref byte[] thumbPrint) { X509Certificate certificate = null; ArrayList list = new ArrayList(); string[] acceptableIssuers = null; bool flag = false; if (this.m_CertSelectionDelegate != null) { if (acceptableIssuers == null) { acceptableIssuers = this.GetIssuers(); } X509Certificate2 remoteCertificate = null; try { X509Certificate2Collection certificates; remoteCertificate = this.GetRemoteCertificate(out certificates); certificate = this.m_CertSelectionDelegate(this.m_HostName, this.ClientCertificates, remoteCertificate, acceptableIssuers); } finally { if (remoteCertificate != null) { remoteCertificate.Reset(); } } if (certificate != null) { if (this.m_CredentialsHandle == null) { flag = true; } list.Add(certificate); if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_got_certificate_from_delegate")); } } else if (this.ClientCertificates.Count == 0) { if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_no_delegate_and_have_no_client_cert")); } flag = true; } else if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_no_delegate_but_have_client_cert")); } } else if (((this.m_CredentialsHandle == null) && (this.m_ClientCertificates != null)) && (this.m_ClientCertificates.Count > 0)) { certificate = this.ClientCertificates[0]; flag = true; if (certificate != null) { list.Add(certificate); } if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_attempting_restart_using_cert", new object[] { (certificate == null) ? "null" : certificate.ToString(true) })); } } else if ((this.m_ClientCertificates != null) && (this.m_ClientCertificates.Count > 0)) { if (acceptableIssuers == null) { acceptableIssuers = this.GetIssuers(); } if (Logging.On) { if ((acceptableIssuers == null) || (acceptableIssuers.Length == 0)) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_no_issuers_try_all_certs")); } else { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_server_issuers_look_for_matching_certs", new object[] { acceptableIssuers.Length })); } } for (int j = 0; j < this.m_ClientCertificates.Count; j++) { if ((acceptableIssuers != null) && (acceptableIssuers.Length != 0)) { X509Certificate2 certificate3 = null; X509Chain chain = null; try { certificate3 = MakeEx(this.m_ClientCertificates[j]); if (certificate3 == null) { continue; } chain = new X509Chain { ChainPolicy = { RevocationMode = X509RevocationMode.NoCheck, VerificationFlags = X509VerificationFlags.IgnoreInvalidName } }; chain.Build(certificate3); bool flag2 = false; if (chain.ChainElements.Count > 0) { for (int k = 0; k < chain.ChainElements.Count; k++) { string issuer = chain.ChainElements[k].Certificate.Issuer; flag2 = Array.IndexOf <string>(acceptableIssuers, issuer) != -1; if (flag2) { break; } } } if (!flag2) { continue; } } finally { if (chain != null) { chain.Reset(); } if ((certificate3 != null) && (certificate3 != this.m_ClientCertificates[j])) { certificate3.Reset(); } } } if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_selected_cert", new object[] { this.m_ClientCertificates[j].ToString(true) })); } list.Add(this.m_ClientCertificates[j]); } } X509Certificate2 certificate4 = null; certificate = null; if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_n_certs_after_filtering", new object[] { list.Count })); if (list.Count != 0) { Logging.PrintInfo(Logging.Web, this, SR.GetString("net_log_finding_matching_certs")); } } for (int i = 0; i < list.Count; i++) { certificate = list[i] as X509Certificate; certificate4 = this.EnsurePrivateKey(certificate); if (certificate4 != null) { break; } certificate = null; certificate4 = null; } try { byte[] buffer = (certificate4 == null) ? null : certificate4.GetCertHash(); SafeFreeCredentials credentials = SslSessionsCache.TryCachedCredential(buffer, this.m_ProtocolFlags, this.m_EncryptionPolicy); if ((flag && (credentials == null)) && (certificate4 != null)) { if (certificate != certificate4) { certificate4.Reset(); } buffer = null; certificate4 = null; certificate = null; } if (credentials != null) { if (Logging.On) { Logging.PrintInfo(Logging.Web, SR.GetString("net_log_using_cached_credential")); } this.m_CredentialsHandle = credentials; this.m_SelectedClientCertificate = certificate; return(true); } SecureCredential secureCredential = new SecureCredential(4, certificate4, SecureCredential.Flags.NoDefaultCred | SecureCredential.Flags.ValidateManual, this.m_ProtocolFlags, this.m_EncryptionPolicy); this.m_CredentialsHandle = this.AcquireCredentialsHandle(CredentialUse.Outbound, ref secureCredential); thumbPrint = buffer; this.m_SelectedClientCertificate = certificate; } finally { if ((certificate4 != null) && (certificate != certificate4)) { certificate4.Reset(); } } return(false); }
private SecurityStatus GenerateToken(byte[] input, int offset, int count, ref byte[] output) { if ((offset < 0) || (offset > ((input == null) ? 0 : input.Length))) { throw new ArgumentOutOfRangeException("offset"); } if ((count < 0) || (count > ((input == null) ? 0 : (input.Length - offset)))) { throw new ArgumentOutOfRangeException("count"); } SecurityBuffer inputBuffer = null; SecurityBuffer[] inputBuffers = null; if (input != null) { inputBuffer = new SecurityBuffer(input, offset, count, BufferType.Token); inputBuffers = new SecurityBuffer[] { inputBuffer, new SecurityBuffer(null, 0, 0, BufferType.Empty) }; } SecurityBuffer outputBuffer = new SecurityBuffer(null, BufferType.Token); int num = 0; bool flag = false; byte[] thumbPrint = null; try { do { thumbPrint = null; if (this.m_RefreshCredentialNeeded) { flag = this.m_ServerMode ? this.AcquireServerCredentials(ref thumbPrint) : this.AcquireClientCredentials(ref thumbPrint); } if (this.m_ServerMode) { num = SSPIWrapper.AcceptSecurityContext(GlobalSSPI.SSPISecureChannel, ref this.m_CredentialsHandle, ref this.m_SecurityContext, (ContextFlags.AcceptStream | ContextFlags.AllocateMemory | ContextFlags.Confidentiality | ContextFlags.SequenceDetect | ContextFlags.ReplayDetect) | (this.m_RemoteCertRequired ? ContextFlags.MutualAuth : ContextFlags.Zero), Endianness.Native, inputBuffer, outputBuffer, ref this.m_Attributes); } else if (inputBuffer == null) { num = SSPIWrapper.InitializeSecurityContext(GlobalSSPI.SSPISecureChannel, ref this.m_CredentialsHandle, ref this.m_SecurityContext, this.m_Destination, ContextFlags.AcceptIdentify | ContextFlags.AllocateMemory | ContextFlags.Confidentiality | ContextFlags.SequenceDetect | ContextFlags.ReplayDetect, Endianness.Native, inputBuffer, outputBuffer, ref this.m_Attributes); } else { num = SSPIWrapper.InitializeSecurityContext(GlobalSSPI.SSPISecureChannel, this.m_CredentialsHandle, ref this.m_SecurityContext, this.m_Destination, ContextFlags.AcceptIdentify | ContextFlags.AllocateMemory | ContextFlags.Confidentiality | ContextFlags.SequenceDetect | ContextFlags.ReplayDetect, Endianness.Native, inputBuffers, outputBuffer, ref this.m_Attributes); } }while (flag && (this.m_CredentialsHandle == null)); } finally { if (this.m_RefreshCredentialNeeded) { this.m_RefreshCredentialNeeded = false; if (this.m_CredentialsHandle != null) { this.m_CredentialsHandle.Close(); } if ((!flag && (this.m_SecurityContext != null)) && (!this.m_SecurityContext.IsInvalid && !this.m_CredentialsHandle.IsInvalid)) { SslSessionsCache.CacheCredential(this.m_CredentialsHandle, thumbPrint, this.m_ProtocolFlags, this.m_EncryptionPolicy); } } } output = outputBuffer.token; return((SecurityStatus)num); }
/*++ * GenerateToken - Called after each successive state * in the Client - Server handshake. This function * generates a set of bytes that will be sent next to * the server. The server responds, each response, * is pass then into this function, again, and the cycle * repeats until successful connection, or failure. * * Input: * input - bytes from the wire * output - ref to byte [], what we will send to the * server in response * Return: * errorCode - an SSPI error code * --*/ private SecurityStatusPal GenerateToken(byte[] input, int offset, int count, ref byte[] output) { #if TRACE_VERBOSE GlobalLog.Enter("SecureChannel#" + Logging.HashString(this) + "::GenerateToken, _refreshCredentialNeeded = " + _refreshCredentialNeeded); #endif if (offset < 0 || offset > (input == null ? 0 : input.Length)) { GlobalLog.Assert(false, "SecureChannel#" + Logging.HashString(this) + "::GenerateToken", "Argument 'offset' out of range."); throw new ArgumentOutOfRangeException("offset"); } if (count < 0 || count > (input == null ? 0 : input.Length - offset)) { GlobalLog.Assert(false, "SecureChannel#" + Logging.HashString(this) + "::GenerateToken", "Argument 'count' out of range."); throw new ArgumentOutOfRangeException("count"); } SecurityBuffer incomingSecurity = null; SecurityBuffer[] incomingSecurityBuffers = null; if (input != null) { incomingSecurity = new SecurityBuffer(input, offset, count, SecurityBufferType.Token); incomingSecurityBuffers = new SecurityBuffer[] { incomingSecurity, new SecurityBuffer(null, 0, 0, SecurityBufferType.Empty) }; } SecurityBuffer outgoingSecurity = new SecurityBuffer(null, SecurityBufferType.Token); SecurityStatusPal errorCode = 0; bool cachedCreds = false; byte[] thumbPrint = null; // // Looping through ASC or ISC with potentially cached credential that could have been // already disposed from a different thread before ISC or ASC dir increment a cred ref count. // try { do { thumbPrint = null; if (_refreshCredentialNeeded) { cachedCreds = _serverMode ? AcquireServerCredentials(ref thumbPrint) : AcquireClientCredentials(ref thumbPrint); } if (_serverMode) { errorCode = SslStreamPal.AcceptSecurityContext( ref _credentialsHandle, ref _securityContext, incomingSecurity, outgoingSecurity, _remoteCertRequired); } else { if (incomingSecurity == null) { errorCode = SslStreamPal.InitializeSecurityContext( ref _credentialsHandle, ref _securityContext, _destination, incomingSecurity, outgoingSecurity); } else { errorCode = SslStreamPal.InitializeSecurityContext( _credentialsHandle, ref _securityContext, _destination, incomingSecurityBuffers, outgoingSecurity); } } } while (cachedCreds && _credentialsHandle == null); } finally { if (_refreshCredentialNeeded) { _refreshCredentialNeeded = false; // // Assuming the ISC or ASC has referenced the credential, // we want to call dispose so to decrement the effective ref count. // if (_credentialsHandle != null) { _credentialsHandle.Dispose(); } // // This call may bump up the credential reference count further. // Note that thumbPrint is retrieved from a safe cert object that was possible cloned from the user passed cert. // if (!cachedCreds && _securityContext != null && !_securityContext.IsInvalid && _credentialsHandle != null && !_credentialsHandle.IsInvalid) { SslSessionsCache.CacheCredential(_credentialsHandle, thumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); } } } output = outgoingSecurity.token; #if TRACE_VERBOSE GlobalLog.Leave("SecureChannel#" + Logging.HashString(this) + "::GenerateToken()", Interop.MapSecurityStatus((uint)errorCode)); #endif return((SecurityStatusPal)errorCode); }
// // Acquire Server Side Certificate information and set it on the class. // private bool AcquireServerCredentials(ref byte[] thumbPrint) { GlobalLog.Enter("SecureChannel#" + Logging.HashString(this) + "::AcquireServerCredentials"); X509Certificate localCertificate = null; bool cachedCred = false; if (_certSelectionDelegate != null) { X509CertificateCollection tempCollection = new X509CertificateCollection(); tempCollection.Add(_serverCertificate); localCertificate = _certSelectionDelegate(string.Empty, tempCollection, null, Array.Empty <string>()); GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireServerCredentials() Use delegate selected Cert"); } else { localCertificate = _serverCertificate; } if (localCertificate == null) { throw new NotSupportedException(SR.net_ssl_io_no_server_cert); } // SECURITY: Accessing X509 cert Credential is disabled for semitrust. // We no longer need to demand for unmanaged code permissions. // EnsurePrivateKey should do the right demand for us. X509Certificate2 selectedCert = EnsurePrivateKey(localCertificate); if (selectedCert == null) { throw new NotSupportedException(SR.net_ssl_io_no_server_cert); } GlobalLog.Assert(localCertificate.Equals(selectedCert), "AcquireServerCredentials()|'selectedCert' does not match 'localCertificate'."); // // Note selectedCert is a safe ref possibly cloned from the user passed Cert object // byte[] guessedThumbPrint = selectedCert.GetCertHash(); try { SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); if (cachedCredentialHandle != null) { _credentialsHandle = cachedCredentialHandle; _serverCertificate = localCertificate; cachedCred = true; } else { _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslProtocols, _encryptionPolicy, _serverMode); thumbPrint = guessedThumbPrint; _serverCertificate = localCertificate; } } finally { // An extra cert could have been created, dispose it now. if ((object)localCertificate != (object)selectedCert) { selectedCert.Dispose(); } } GlobalLog.Leave("SecureChannel#" + Logging.HashString(this) + "::AcquireServerCredentials, cachedCreds = " + cachedCred.ToString(), Logging.ObjectToString(_credentialsHandle)); return(cachedCred); }
/*++ * AcquireCredentials - Attempts to find Client Credential * Information, that can be sent to the server. In our case, * this is only Client Certificates, that we have Credential Info. * * How it works: * case 0: Cert Selection delegate is present * Always use its result as the client cert answer. * Try to use cached credential handle whenever feasible. * Do not use cached anonymous creds if the delegate has returned null * and the collection is not empty (allow responding with the cert later). * * case 1: Certs collection is empty * Always use the same statically acquired anonymous SSL Credential * * case 2: Before our Connection with the Server * If we have a cached credential handle keyed by first X509Certificate **content** in the passed collection, then we use that cached * credential and hoping to restart a session. * * Otherwise create a new anonymous (allow responding with the cert later). * * case 3: After our Connection with the Server (i.e. during handshake or re-handshake) * The server has requested that we send it a Certificate then * we Enumerate a list of server sent Issuers trying to match against * our list of Certificates, the first match is sent to the server. * * Once we got a cert we again try to match cached credential handle if possible. * This will not restart a session but helps minimizing the number of handles we create. * * In the case of an error getting a Certificate or checking its private Key we fall back * to the behavior of having no certs, case 1. * * Returns: True if cached creds were used, false otherwise. * * --*/ private bool AcquireClientCredentials(ref byte[] thumbPrint) { GlobalLog.Enter("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials"); // Acquire possible Client Certificate information and set it on the handle. X509Certificate clientCertificate = null; // This is a candidate that can come from the user callback or be guessed when targeting a session restart. ArrayList filteredCerts = new ArrayList(); // This is an intermediate client certs collection that try to use if no selectedCert is available yet. string[] issuers = null; // This is a list of issuers sent by the server, only valid is we do know what the server cert is. bool sessionRestartAttempt = false; // If true and no cached creds we will use anonymous creds. if (_certSelectionDelegate != null) { issuers = GetRequestCertificateAuthorities(); GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() calling CertificateSelectionCallback"); X509Certificate2 remoteCert = null; try { X509Certificate2Collection dummyCollection; remoteCert = CertificateValidationPal.GetRemoteCertificate(_securityContext, out dummyCollection); clientCertificate = _certSelectionDelegate(_hostName, ClientCertificates, remoteCert, issuers); } finally { if (remoteCert != null) { remoteCert.Dispose(); } } if (clientCertificate != null) { if (_credentialsHandle == null) { sessionRestartAttempt = true; } filteredCerts.Add(clientCertificate); if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.net_log_got_certificate_from_delegate); } } else { if (ClientCertificates.Count == 0) { if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.net_log_no_delegate_and_have_no_client_cert); } sessionRestartAttempt = true; } else { if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.net_log_no_delegate_but_have_client_cert); } } } } else if (_credentialsHandle == null && _clientCertificates != null && _clientCertificates.Count > 0) { // This is where we attempt to restart a session by picking the FIRST cert from the collection. // Otherwise it is either server sending a client cert request or the session is renegotiated. clientCertificate = ClientCertificates[0]; sessionRestartAttempt = true; if (clientCertificate != null) { filteredCerts.Add(clientCertificate); } if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.Format(SR.net_log_attempting_restart_using_cert, (clientCertificate == null ? "null" : clientCertificate.ToString(true)))); } } else if (_clientCertificates != null && _clientCertificates.Count > 0) { // // This should be a server request for the client cert sent over currently anonymous sessions. // issuers = GetRequestCertificateAuthorities(); if (Logging.On) { if (issuers == null || issuers.Length == 0) { Logging.PrintInfo(Logging.Web, this, SR.net_log_no_issuers_try_all_certs); } else { Logging.PrintInfo(Logging.Web, this, SR.Format(SR.net_log_server_issuers_look_for_matching_certs, issuers.Length)); } } for (int i = 0; i < _clientCertificates.Count; ++i) { // // Make sure we add only if the cert matches one of the issuers. // If no issuers were sent and then try all client certs starting with the first one. // if (issuers != null && issuers.Length != 0) { X509Certificate2 certificateEx = null; X509Chain chain = null; try { certificateEx = MakeEx(_clientCertificates[i]); if (certificateEx == null) { continue; } GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() root cert:" + certificateEx.Issuer); chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreInvalidName; chain.Build(certificateEx); bool found = false; // // We ignore any errors happened with chain. // if (chain.ChainElements.Count > 0) { for (int ii = 0; ii < chain.ChainElements.Count; ++ii) { string issuer = chain.ChainElements[ii].Certificate.Issuer; found = Array.IndexOf(issuers, issuer) != -1; if (found) { GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() matched:" + issuer); break; } GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() no match:" + issuer); } } if (!found) { continue; } } finally { if (chain != null) { chain.Dispose(); } if (certificateEx != null && (object)certificateEx != (object)_clientCertificates[i]) { certificateEx.Dispose(); } } } if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.Format(SR.net_log_selected_cert, _clientCertificates[i].ToString(true))); } filteredCerts.Add(_clientCertificates[i]); } } bool cachedCred = false; // This is a return result from this method. X509Certificate2 selectedCert = null; // This is a final selected cert (ensured that it does have private key with it). clientCertificate = null; if (Logging.On) { Logging.PrintInfo(Logging.Web, this, SR.Format(SR.net_log_n_certs_after_filtering, filteredCerts.Count)); if (filteredCerts.Count != 0) { Logging.PrintInfo(Logging.Web, this, SR.net_log_finding_matching_certs); } } // // ATTN: When the client cert was returned by the user callback OR it was guessed AND it has no private key, // THEN anonymous (no client cert) credential will be used. // // SECURITY: Accessing X509 cert Credential is disabled for semitrust. // We no longer need to demand for unmanaged code permissions. // EnsurePrivateKey should do the right demand for us. for (int i = 0; i < filteredCerts.Count; ++i) { clientCertificate = filteredCerts[i] as X509Certificate; if ((selectedCert = EnsurePrivateKey(clientCertificate)) != null) { break; } clientCertificate = null; selectedCert = null; } GlobalLog.Assert(((object)clientCertificate == (object)selectedCert) || clientCertificate.Equals(selectedCert), "AcquireClientCredentials()|'selectedCert' does not match 'clientCertificate'."); GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() Selected Cert = " + (selectedCert == null ? "null" : selectedCert.Subject)); try { // Try to locate cached creds first. // // SECURITY: selectedCert ref if not null is a safe object that does not depend on possible **user** inherited X509Certificate type. // byte[] guessedThumbPrint = selectedCert == null ? null : selectedCert.GetCertHash(); SafeFreeCredentials cachedCredentialHandle = SslSessionsCache.TryCachedCredential(guessedThumbPrint, _sslProtocols, _serverMode, _encryptionPolicy); // We can probably do some optimization here. If the selectedCert is returned by the delegate // we can always go ahead and use the certificate to create our credential // (instead of going anonymous as we do here). if (sessionRestartAttempt && cachedCredentialHandle == null && selectedCert != null) { GlobalLog.Print("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials() Reset to anonymous session."); // IIS does not renegotiate a restarted session if client cert is needed. // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client has passed some certificate. // The following block happens if client did specify a certificate but no cached creds were found in the cache. // Since we don't restart a session the server side can still challenge for a client cert. if ((object)clientCertificate != (object)selectedCert) { selectedCert.Dispose(); } guessedThumbPrint = null; selectedCert = null; clientCertificate = null; } if (cachedCredentialHandle != null) { if (Logging.On) { Logging.PrintInfo(Logging.Web, SR.net_log_using_cached_credential); } _credentialsHandle = cachedCredentialHandle; _selectedClientCertificate = clientCertificate; cachedCred = true; } else { _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslProtocols, _encryptionPolicy, _serverMode); thumbPrint = guessedThumbPrint; // Delay until here in case something above threw. _selectedClientCertificate = clientCertificate; } } finally { // An extra cert could have been created, dispose it now. if (selectedCert != null && (object)clientCertificate != (object)selectedCert) { selectedCert.Dispose(); } } GlobalLog.Leave("SecureChannel#" + Logging.HashString(this) + "::AcquireClientCredentials, cachedCreds = " + cachedCred.ToString(), Logging.ObjectToString(_credentialsHandle)); return(cachedCred); }