/// <summary> /// Find the issuer certificate. /// First checks if the leaf certificate's Subject and Issuer fields are not the same. /// Otherwise, the certificate is the issuer (self-signed certificate) /// If different it instatniates a X509Chain object and passes leaf certificate to X509Chain.Build method.Examine ChainElements property (a collection) and element at index 1 is the issuer. /// </summary> /// <param name="leafCert"></param> /// <returns></returns> public static X509Certificate2 GetIssuer(X509Certificate2 leafCert) { if (leafCert.Subject == leafCert.Issuer) { return(leafCert); } var chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.Build(leafCert); X509Certificate2 issuer = null; if (chain.ChainElements.Count > 1) { issuer = chain.ChainElements[1].Certificate; } chain.Reset(); return(issuer); }
public void Reset () { X509Chain c = new X509Chain (); c.ChainPolicy.ApplicationPolicy.Add (new Oid ("1.2.3")); c.ChainPolicy.CertificatePolicy.Add (new Oid ("1.2.4")); c.ChainPolicy.ExtraStore.AddRange (collection); c.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; c.ChainPolicy.RevocationMode = X509RevocationMode.Offline; c.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (1000); c.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage; c.ChainPolicy.VerificationTime = DateTime.MinValue; c.Reset (); // resetting the chain doesn't reset the policy Assert.AreEqual (1, c.ChainPolicy.ApplicationPolicy.Count, "ApplicationPolicy"); Assert.AreEqual (1, c.ChainPolicy.CertificatePolicy.Count, "CertificatePolicy"); Assert.AreEqual (2, c.ChainPolicy.ExtraStore.Count, "ExtraStore"); Assert.AreEqual (X509RevocationFlag.EntireChain, c.ChainPolicy.RevocationFlag, "RevocationFlag"); Assert.AreEqual (X509RevocationMode.Offline, c.ChainPolicy.RevocationMode, "RevocationMode"); Assert.AreEqual (1000, c.ChainPolicy.UrlRetrievalTimeout.Ticks, "UrlRetrievalTimeout"); Assert.AreEqual (X509VerificationFlags.IgnoreWrongUsage, c.ChainPolicy.VerificationFlags, "VerificationFlags"); Assert.AreEqual (DateTime.MinValue, c.ChainPolicy.VerificationTime, "VerificationTime"); }
public void Build_Cert2 () { X509Chain c = new X509Chain (); foreach (X509VerificationFlags vf in Enum.GetValues (typeof (X509VerificationFlags))) { c.ChainPolicy.VerificationFlags = vf; CheckCert2 (c); c.Reset (); } // minimal criteria for success c.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.AllowUnknownCertificateAuthority; CheckCert2 (c); }
public void Build_Cert1_X509RevocationMode_NoCheck () { X509Chain c = new X509Chain (); c.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; foreach (X509VerificationFlags vf in Enum.GetValues (typeof (X509VerificationFlags))) { c.ChainPolicy.VerificationFlags = vf; CheckCert1 (c); c.Reset (); } }
public void Build_Twice_WithReset () { X509Chain c = new X509Chain (); Assert.IsFalse (c.Build (cert1), "Build-1"); c.Reset (); Assert.IsFalse (c.Build (cert2), "Build-2"); c.Reset (); CheckDefaultChain (c); }
private bool AcquireClientCredentials(ref byte[] thumbPrint) { GlobalLog.Enter("SecureChannel#" + ValidationHelper.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 (m_CertSelectionDelegate!=null) { if (issuers == null) issuers = GetIssuers(); GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() calling CertificateSelectionCallback"); X509Certificate2 remoteCert = null; try { X509Certificate2Collection dummyCollection; remoteCert = GetRemoteCertificate(out dummyCollection); clientCertificate = m_CertSelectionDelegate(m_HostName, ClientCertificates, remoteCert, issuers); } finally { if (remoteCert != null) remoteCert.Reset(); } if (clientCertificate != null) { if (m_CredentialsHandle == null) sessionRestartAttempt = true; filteredCerts.Add(clientCertificate); if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_got_certificate_from_delegate)); } else { // If ClientCertificates.Count != 0, how come we don't try to go through them and add them to the filtered certs, just like when there is no delegate???? if (ClientCertificates.Count == 0) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_delegate_and_have_no_client_cert)); sessionRestartAttempt = true; } else { if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_delegate_but_have_client_cert)); } } } else if (m_CredentialsHandle == null && m_ClientCertificates != null && m_ClientCertificates.Count > 0) { // This is where we attempt to restart a session by picking the FIRST cert from the collection. // Otheriwse (next elses) 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.GetString(SR.net_log_attempting_restart_using_cert, (clientCertificate == null ? "null" : clientCertificate.ToString(true)))); } else if (m_ClientCertificates!=null && m_ClientCertificates.Count > 0) { // // This should be a server request for the client cert sent over currently anonyumous sessions. // if (issuers == null) issuers = GetIssuers(); if (Logging.On) { if (issuers == null || issuers.Length == 0) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_no_issuers_try_all_certs)); else Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_server_issuers_look_for_matching_certs, issuers.Length)); } for (int i = 0; i < m_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(m_ClientCertificates[i]); if (certificateEx == null) continue; GlobalLog.Print("SecureChannel#" + ValidationHelper.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. // Consider: try to locate the "best" client cert that has no errors and the lognest validity internal // 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#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() matched:" + issuer); break; } GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() no match:" + issuer); } } if (!found) { continue; } } finally { if (chain != null) chain.Reset(); if (certificateEx != null && (object)certificateEx != (object)m_ClientCertificates[i]) certificateEx.Reset(); } } if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_selected_cert, m_ClientCertificates[i].ToString(true))); filteredCerts.Add(m_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.GetString(SR.net_log_n_certs_after_filtering, filteredCerts.Count)); if (filteredCerts.Count != 0) Logging.PrintInfo(Logging.Web, this, SR.GetString(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#" + ValidationHelper.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, m_ProtocolFlags, m_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#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials() Reset to anonymous session."); // (see VsWhidbey#363953) For some (probably good) reason 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.Reset(); guessedThumbPrint = null; selectedCert = null; clientCertificate = null; } if (cachedCredentialHandle != null) { if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_log_using_cached_credential)); m_CredentialsHandle = cachedCredentialHandle; m_SelectedClientCertificate = clientCertificate; cachedCred = true; } else { SecureCredential.Flags flags = SecureCredential.Flags.ValidateManual | SecureCredential.Flags.NoDefaultCred; if (!ServicePointManager.DisableSendAuxRecord) { flags |= SecureCredential.Flags.SendAuxRecord; } if (!ServicePointManager.DisableStrongCrypto && ((m_ProtocolFlags & (SchProtocols.Tls10 | SchProtocols.Tls11 | SchProtocols.Tls12)) != 0) && (m_EncryptionPolicy != EncryptionPolicy.AllowNoEncryption) && (m_EncryptionPolicy != EncryptionPolicy.NoEncryption)) { flags |= SecureCredential.Flags.UseStrongCrypto; } SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, selectedCert, flags, m_ProtocolFlags, m_EncryptionPolicy); m_CredentialsHandle = AcquireCredentialsHandle(CredentialUse.Outbound, ref secureCredential); thumbPrint = guessedThumbPrint; //delay it until here in case something above threw m_SelectedClientCertificate = clientCertificate; } } finally { // an extra cert could have been created, dispose it now if (selectedCert != null && (object)clientCertificate != (object)selectedCert) selectedCert.Reset(); } GlobalLog.Leave("SecureChannel#" + ValidationHelper.HashString(this) + "::AcquireClientCredentials, cachedCreds = " + cachedCred.ToString(), ValidationHelper.ToString(m_CredentialsHandle)); return cachedCred; }
internal static X509CertificateCollection FindClientCertificates() { if (!ComNetOS.IsWin7orLater) { throw new PlatformNotSupportedException(); } X509CertificateCollection certificates = new X509CertificateCollection(); X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.MaxAllowed); int chainCount = 0; SafeFreeCertChainList chainList = null; SafeCertSelectCritera criteria = new SafeCertSelectCritera(); try { bool success = CertSelectCertificateChains( IntPtr.Zero, CertificateSelect.HasPrivateKey, IntPtr.Zero, criteria.Count, // DWORD criteria, // PCCERT_SELECT_CRITERIA store.StoreHandle, out chainCount, out chainList); if (!success) { throw new Win32Exception(); // Calls GetLastError. } Debug.Assert(chainCount == 0 || !chainList.IsInvalid); for (int i = 0; i < chainCount; i++) { // Resolve IntPtr in array. using (SafeFreeCertChain chainRef = new SafeFreeCertChain( Marshal.ReadIntPtr(chainList.DangerousGetHandle() + i * Marshal.SizeOf(typeof(IntPtr))), true)) { Debug.Assert(!chainRef.IsInvalid); // X509Chain will duplicate the chain by increasing its ref-count. X509Chain chain = new X509Chain(chainRef.DangerousGetHandle()); // Copy base cert from chain. if (chain.ChainElements.Count > 0) { X509Certificate2 cert = chain.ChainElements[0].Certificate; certificates.Add(cert); } // Remove the X509Chain's reference prior to releasing the Chain List. chain.Reset(); } } } finally { // Close store. store.Close(); chainList.Dispose(); criteria.Dispose(); } return certificates; }
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; }