byte[] CreateKeyVaultSignature(X509Certificate2 publicCert, SignatureManifest signatureManifest) { var chain = new X509Chain(); chain.Build(publicCert); // Get the chain as bc certs var additionals = chain.ChainElements.Cast <X509ChainElement>() .Select(ce => DotNetUtilities.FromX509Certificate(ce.Certificate)) .ToList(); chain.Dispose(); var bcCer = DotNetUtilities.FromX509Certificate(publicCert); var store = X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(additionals)); var generator = new CmsSignedDataGenerator(); var builder = new SignerInfoGeneratorBuilder(); var b = builder.Build(new RsaSignatureFactory("SHA256WITHRSA", provider), bcCer); generator.AddSignerInfoGenerator(b); generator.AddCertificates(store); var msg = new CmsProcessableByteArray(signatureManifest.GetBytes()); var data = generator.Generate(msg, true); var encoded = data.GetEncoded(); return(encoded); }
public void Dispose() { if (_isDisposed) { return; } if (_sessionAdapter != null) { _sessionAdapter.Dispose(); } if (_clientStream != null) { _clientStream.Dispose(); _clientStream = null; } if (_certificate != null) { _certificate.Dispose(); _certificate = null; } if (_chain != null) { _chain.Dispose(); _chain = null; } _isDisposed = true; }
public void Dispose() { ClientCertificateList.Clear(); CAChain.Dispose(); ServerCertificate.Dispose(); ClientCertificate.Dispose(); }
public void SignedCmsRoundTripWithBouncyCastleLocalCertificate() { var content = "This is some content"; // Get cert var netcert = GetLocalSignerCert(); var chain = new X509Chain(); chain.Build(netcert); // Get the chain without the root CA var additionals = chain.ChainElements.Cast <X509ChainElement>() .Where(ce => ce.Certificate.Issuer != ce.Certificate.SubjectName.Name) .Select(ce => DotNetUtilities.FromX509Certificate(ce.Certificate)) .ToList(); chain.Dispose(); var bcCer = DotNetUtilities.FromX509Certificate(netcert); var bcKey = DotNetUtilities.GetRsaKeyPair(netcert.GetRSAPrivateKey()); var store = X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(additionals)); var generator = new CmsSignedDataGenerator(); var builder = new SignerInfoGeneratorBuilder(); var b = builder.Build(new Asn1SignatureFactory("SHA256WITHRSA", bcKey.Private), bcCer); generator.AddSignerInfoGenerator(b); // generator.AddSigner(bcKey.Private, bcCer, CmsSignedDataGenerator.DigestSha256); generator.AddCertificates(store); var msg = new CmsProcessableByteArray(Encoding.UTF8.GetBytes(content)); var data = generator.Generate(msg, true); var encoded = data.GetEncoded(); var signedCms = new SignedCms(); signedCms.Decode(encoded); signedCms.CheckSignature(true); // don't validate the certiciate itself here var cContent = signedCms.ContentInfo.Content; var str = Encoding.UTF8.GetString(cContent); Assert.Equal(content, str); }
private CertificateVerificationResult VerifyCertificate(X509Certificate2 certificate, IReadOnlyList <X509Certificate2> extraCertificates, Oid applicationPolicy) { X509Chain chain = null; try { chain = new X509Chain(); // Allow the chain to use whatever additional extra certificates were provided. chain.ChainPolicy.ExtraStore.AddRange(extraCertificates.ToArray()); if (applicationPolicy != null) { chain.ChainPolicy.ApplicationPolicy.Add(applicationPolicy); } chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; var resultBuilder = new CertificateVerificationResult.Builder(); if (chain.Build(certificate)) { resultBuilder.WithStatus(EndCertificateStatus.Good); resultBuilder.WithStatusFlags(X509ChainStatusFlags.NoError); } else { resultBuilder.WithStatus(GetEndCertificateStatusFromInvalidChain(chain)); resultBuilder.WithStatusFlags(FlattenChainStatusFlags(chain)); } InspectChain(chain, certificate, resultBuilder); return(resultBuilder.Build()); } finally { if (chain != null) { foreach (var chainElement in chain.ChainElements) { chainElement.Certificate.Dispose(); } chain.Dispose(); } } }
public async void SignedCmsRoundTripWithKeyVault() { using (var materialized = await KeyVaultConfigurationDiscoverer.Materialize(certificateConfiguration)) { var content = "This is some content"; var publicCert = materialized.PublicCertificate; // Get cert var chain = new X509Chain(); chain.Build(publicCert); // Get the chain without the root CA var additionals = chain.ChainElements.Cast <X509ChainElement>() .Where(ce => ce.Certificate.Issuer != ce.Certificate.SubjectName.Name) .Select(ce => DotNetUtilities.FromX509Certificate(ce.Certificate)) .ToList(); chain.Dispose(); var bcCer = DotNetUtilities.FromX509Certificate(publicCert); var store = X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(additionals)); var generator = new CmsSignedDataGenerator(); var builder = new SignerInfoGeneratorBuilder(); var b = builder.Build(new RsaSignatureFactory("SHA256WITHRSA", materialized.ToRSA()), bcCer); generator.AddSignerInfoGenerator(b); generator.AddCertificates(store); var msg = new CmsProcessableByteArray(Encoding.UTF8.GetBytes(content)); var data = generator.Generate(msg, true); var encoded = data.GetEncoded(); var signedCms = new SignedCms(); signedCms.Decode(encoded); signedCms.CheckSignature(true); // don't validate the certiciate itself here var cContent = signedCms.ContentInfo.Content; var str = Encoding.UTF8.GetString(cContent); Assert.Equal(content, str); } }
public void TestCase() { string serverCertPath = @"/certs/server.pfx"; string serverPrivateKeyPassword = "******"; string caFilePath = "/certs/ca_chain.pem"; string clientCertPath = "/certs/client.pfx"; string clientPrivateKeyPassword = "******"; string projDir = Directory.GetParent(Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).FullName).FullName; // Initialize OpenSSL for multithreaded use ThreadInitialization.InitializeThreads(); try { // Intitialize server certificates serverCAChain = LoadCACertificateChain(projDir + caFilePath); serverCertificate = LoadPKCS12Certificate(projDir + serverCertPath, serverPrivateKeyPassword); // Kick the server thread Thread serverThread = new Thread(new ThreadStart(ServerTestThreadProc)); serverThread.Start(); // Intialize the client certificates clientCAChain = LoadCACertificateChain(projDir + caFilePath); X509Certificate clientCert = LoadPKCS12Certificate(projDir + clientCertPath, clientPrivateKeyPassword); // Add the cert to the client certificate list clientCertificateList = new X509List(); clientCertificateList.Add(clientCert); // Kick the client thread Thread clientThread = new Thread(new ThreadStart(ClientTestThreadProc)); clientThread.Start(); // Wait for the threads to exit serverThread.Join(); clientThread.Join(); // Cleanup serverCertificate.Dispose(); serverCAChain.Dispose(); clientCAChain.Dispose(); clientCert.Dispose(); } catch (Exception ex) { Console.WriteLine("Server test failed with exception: {0}", ex.Message); } ThreadInitialization.UninitializeThreads(); }
public void Execute(string[] args) { string serverCertPath = @"../../test/certs/server.pfx"; string serverPrivateKeyPassword = "******"; string caFilePath = "../../test/certs/ca_chain.pem"; string clientCertPath = "../../test/certs/client.pfx"; string clientPrivateKeyPassword = "******"; // Initialize OpenSSL for multithreaded use ThreadInitialization.InitializeThreads(); try { // Intitialize server certificates serverCAChain = LoadCACertificateChain(caFilePath); serverCertificate = LoadPKCS12Certificate(serverCertPath, serverPrivateKeyPassword); // Kick the server thread Thread serverThread = new Thread(new ThreadStart(ServerTestThreadProc)); serverThread.Start(); // Intialize the client certificates clientCAChain = LoadCACertificateChain(caFilePath); X509Certificate clientCert = LoadPKCS12Certificate(clientCertPath, clientPrivateKeyPassword); // Add the cert to the client certificate list clientCertificateList = new X509List(); clientCertificateList.Add(clientCert); // Kick the client thread Thread clientThread = new Thread(new ThreadStart(ClientTestThreadProc)); clientThread.Start(); // Wait for the threads to exit serverThread.Join(); clientThread.Join(); // Cleanup serverCertificate.Dispose(); serverCAChain.Dispose(); clientCAChain.Dispose(); clientCert.Dispose(); } catch (Exception ex) { Console.WriteLine("Server test failed with exception: {0}", ex.Message); } ThreadInitialization.UninitializeThreads(); }
internal static X509Chain BuildNewChain(X509Certificate2 certificate, bool includeClientApplicationPolicy) { var chain = new X509Chain(); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; if (includeClientApplicationPolicy) { chain.ChainPolicy.ApplicationPolicy.Add(s_clientCertOidInst); } if (chain.Build(certificate)) { return(chain); } else { chain.Dispose(); return(null); } }
private static bool IsSelfSignedCertificate(X509Certificate2 certificate) { X509Chain ch = null; // This function is only passed well-formed certs, so no need error handling. try { ch = new X509Chain(); ch.Build(certificate); if (ch.ChainElements.Count == 1) { return(true); } } finally { ch?.Dispose(); } return(false); }
ValidationResult ValidateChain(string host, bool server, X509Certificate leaf, X509Chain chain, X509CertificateCollection certs, SslPolicyErrors errors) { var oldChain = chain; var ownsChain = chain == null; try { var result = ValidateChain(host, server, leaf, ref chain, certs, errors); if (chain != oldChain) { ownsChain = true; } return(result); } finally { // If ValidateChain() changed the chain, then we need to free it. if (ownsChain && chain != null) { chain.Dispose(); } } }
/*++ * 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); }
private int TlsClientCertCallback(IntPtr ssl, out IntPtr certHandle, out IntPtr privateKeyHandle) { const int CertificateSet = 1, NoCertificateSet = 0, SuspendHandshake = -1; certHandle = IntPtr.Zero; privateKeyHandle = IntPtr.Zero; if (ssl == IntPtr.Zero) { Debug.Fail("Expected valid SSL pointer"); EventSourceTrace("Invalid SSL pointer in callback"); return(NoCertificateSet); } SafeSslHandle sslHandle = null; X509Chain chain = null; X509Certificate2 certificate = null; try { sslHandle = new SafeSslHandle(ssl, ownsHandle: false); ISet <string> issuerNames = GetRequestCertificateAuthorities(sslHandle); if (_clientCertificates != null) // manual mode { // If there's one certificate, just use it. Otherwise, try to find the best one. if (_clientCertificates.Count == 1) { certificate = _clientCertificates[0]; chain = TLSCertificateExtensions.BuildNewChain(certificate, includeClientApplicationPolicy: false); } else if (!_clientCertificates.TryFindClientCertificate(issuerNames, out certificate, out chain)) { EventSourceTrace("No manual certificate or chain."); return(NoCertificateSet); } } else if (!GetAutomaticClientCertificate(issuerNames, out certificate, out chain)) // automatic mode { EventSourceTrace("No automatic certificate or chain."); return(NoCertificateSet); } Interop.Crypto.CheckValidOpenSslHandle(certificate.Handle); using (RSAOpenSsl rsa = certificate.GetRSAPrivateKey() as RSAOpenSsl) { if (rsa != null) { _privateKeyHandle = rsa.DuplicateKeyHandle(); EventSourceTrace("RSA key"); } else { using (ECDsaOpenSsl ecdsa = certificate.GetECDsaPrivateKey() as ECDsaOpenSsl) { if (ecdsa != null) { _privateKeyHandle = ecdsa.DuplicateKeyHandle(); EventSourceTrace("ECDsa key"); } } } } if (_privateKeyHandle == null || _privateKeyHandle.IsInvalid) { EventSourceTrace("Invalid private key"); return(NoCertificateSet); } _certHandle = Interop.Crypto.X509Duplicate(certificate.Handle); Interop.Crypto.CheckValidOpenSslHandle(_certHandle); if (chain != null) { for (int i = chain.ChainElements.Count - 2; i > 0; i--) { SafeX509Handle dupCertHandle = Interop.Crypto.X509Duplicate(chain.ChainElements[i].Certificate.Handle); Interop.Crypto.CheckValidOpenSslHandle(dupCertHandle); if (!Interop.Ssl.SslAddExtraChainCert(sslHandle, dupCertHandle)) { EventSourceTrace("Failed to add extra chain certificate"); return(SuspendHandshake); } } } certHandle = _certHandle.DangerousGetHandle(); privateKeyHandle = _privateKeyHandle.DangerousGetHandle(); EventSourceTrace("Client certificate set: {0}", certificate); return(CertificateSet); } finally { if (_clientCertificates == null) { certificate?.Dispose(); // only dispose cert if it's automatic / newly created } chain?.Dispose(); sslHandle?.Dispose(); } }
private bool RTServerCertificateCallbackHelper( RTHttpRequestMessage requestMessage, RTCertificate cert, IReadOnlyList <RTCertificate> intermediateCerts, IReadOnlyList <RTChainValidationResult> certErrors) { // Convert WinRT certificate to .NET certificate. X509Certificate2 serverCert = CertificateHelper.ConvertPublicKeyCertificate(cert); // Create .NET X509Chain from the WinRT information. We need to rebuild the chain since WinRT only // gives us an array of intermediate certificates and not a X509Chain object. var serverChain = new X509Chain(); SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None; foreach (RTCertificate intermediateCert in intermediateCerts) { serverChain.ChainPolicy.ExtraStore.Add(CertificateHelper.ConvertPublicKeyCertificate(cert)); } serverChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; // WinRT always checks revocation. serverChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; // Authenticate the remote party: (e.g. when operating in client mode, authenticate the server). serverChain.ChainPolicy.ApplicationPolicy.Add(s_serverAuthOid); if (!serverChain.Build(serverCert)) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } // Determine name-mismatch error from the existing WinRT information since .NET X509Chain.Build does not // return that in the X509Chain.ChainStatus fields. foreach (RTChainValidationResult result in certErrors) { if (result == RTChainValidationResult.InvalidName) { sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; break; } } // Get the .NET HttpRequestMessage we saved in the property bag of the WinRT HttpRequestMessage. HttpRequestMessage request = (HttpRequestMessage)requestMessage.Properties[RequestMessageLookupKey]; // Call the .NET callback. bool success = false; try { success = _serverCertificateCustomValidationCallback(request, serverCert, serverChain, sslPolicyErrors); } catch (Exception ex) { // Save the exception info. We will return it later via the SendAsync response processing. requestMessage.Properties.Add( SavedExceptionDispatchInfoLookupKey, ExceptionDispatchInfo.Capture(ex)); } finally { serverChain.Dispose(); serverCert.Dispose(); } return(success); }
private bool RemoteCertificateValidationCallback( object sender, X509Certificate certificate, X509Chain chainEngine, SslPolicyErrors policyErrors) { var chain = new X509Chain(_engine.UseMachineContext); try { if (_engine.CheckCRL == 0) { chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; } X509Certificate2Collection?caCerts = _engine.CaCerts; if (caCerts != null) { // // We need to set this flag to be able to use a certificate authority from the extra store. // chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; foreach (X509Certificate2 cert in caCerts) { chain.ChainPolicy.ExtraStore.Add(cert); } } string message = ""; int errors = (int)policyErrors; if (certificate != null) { chain.Build(new X509Certificate2(certificate)); if (chain.ChainStatus != null && chain.ChainStatus.Length > 0) { errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors; } else if (_engine.CaCerts != null) { X509ChainElement e = chain.ChainElements[chain.ChainElements.Count - 1]; if (!chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) { message += "\nuntrusted root certificate"; errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors; } else { return(true); } } else { return(true); } } if ((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) > 0) { // // The RemoteCertificateNotAvailable case does not appear to be possible // for an outgoing connection. Since .NET requires an authenticated // connection, the remote peer closes the socket if it does not have a // certificate to provide. // if (_incoming) { if (_verifyPeer > 1) { if (_engine.SecurityTraceLevel >= 1) { _communicator.Logger.Trace(_engine.SecurityTraceCategory, "SSL certificate validation failed - client certificate not provided"); } return(false); } errors ^= (int)SslPolicyErrors.RemoteCertificateNotAvailable; message += "\nremote certificate not provided (ignored)"; } } if ((errors & (int)SslPolicyErrors.RemoteCertificateNameMismatch) > 0) { if (_engine.SecurityTraceLevel >= 1) { _communicator.Logger.Trace(_engine.SecurityTraceCategory, "SSL certificate validation failed - Hostname mismatch"); } return(false); } if ((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0 && chain.ChainStatus != null && chain.ChainStatus.Length > 0) { int errorCount = 0; foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status == X509ChainStatusFlags.UntrustedRoot && _engine.CaCerts != null) { // // Untrusted root is OK when using our custom chain engine if // the CA certificate is present in the chain policy extra store. // X509ChainElement e = chain.ChainElements[chain.ChainElements.Count - 1]; if (!chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) { message += "\nuntrusted root certificate"; ++errorCount; } } else if (status.Status == X509ChainStatusFlags.Revoked) { if (_engine.CheckCRL > 0) { message += "\ncertificate revoked"; ++errorCount; } else { message += "\ncertificate revoked (ignored)"; } } else if (status.Status == X509ChainStatusFlags.OfflineRevocation) { // If a certificate's revocation status cannot be determined, the strictest policy is to // reject the connection. if (_engine.CheckCRL > 1) { message += "\ncertificate revocation list is offline"; ++errorCount; } else { message += "\ncertificate revocation list is offline (ignored)"; } } else if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown) { // If a certificate's revocation status cannot be determined, the strictest policy is to // reject the connection. if (_engine.CheckCRL > 1) { message += "\ncertificate revocation status unknown"; ++errorCount; } else { message += "\ncertificate revocation status unknown (ignored)"; } } else if (status.Status == X509ChainStatusFlags.PartialChain) { message += "\npartial certificate chain"; ++errorCount; } else if (status.Status != X509ChainStatusFlags.NoError) { message = message + "\ncertificate chain error: " + status.Status.ToString(); ++errorCount; } } if (errorCount == 0) { errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors; } } if (errors > 0) { if (_engine.SecurityTraceLevel >= 1) { if (message.Length > 0) { _communicator.Logger.Trace(_engine.SecurityTraceCategory, $"SSL certificate validation failed: {message}"); } else { _communicator.Logger.Trace(_engine.SecurityTraceCategory, "SSL certificate validation failed"); } } return(false); } else if (message.Length > 0 && _engine.SecurityTraceLevel >= 1) { _communicator.Logger.Trace(_engine.SecurityTraceCategory, $"SSL certificate validation status: {message}"); } return(true); } finally { try { chain.Dispose(); } catch (Exception) { } } }
public void TestCase() { string serverCertPath = @"../../test/certs/server.pfx"; string serverPrivateKeyPassword = "******"; string caFilePath = "../../test/certs/ca_chain.pem"; string clientCertPath = "../../test/certs/client.pfx"; string clientPrivateKeyPassword = "******"; // Initialize OpenSSL for multithreaded use ThreadInitialization.InitializeThreads(); try { // Intitialize server certificates serverCAChain = LoadCACertificateChain(caFilePath); serverCertificate = LoadPKCS12Certificate(serverCertPath, serverPrivateKeyPassword); // Kick the server thread Thread serverThread = new Thread(new ThreadStart(ServerTestThreadProc)); serverThread.Start(); // Intialize the client certificates clientCAChain = LoadCACertificateChain(caFilePath); X509Certificate clientCert = LoadPKCS12Certificate(clientCertPath, clientPrivateKeyPassword); // Add the cert to the client certificate list clientCertificateList = new X509List(); clientCertificateList.Add(clientCert); // Kick the client thread Thread clientThread = new Thread(new ThreadStart(ClientTestThreadProc)); clientThread.Start(); // Wait for the threads to exit serverThread.Join(); clientThread.Join(); // Cleanup serverCertificate.Dispose(); serverCAChain.Dispose(); clientCAChain.Dispose(); clientCert.Dispose(); } catch (Exception ex) { Console.WriteLine("Server test failed with exception: {0}", ex.Message); } ThreadInitialization.UninitializeThreads(); }
private static async Task <bool> CheckSignature(string contentSignature, byte[] jsonContentsBlob) { if (string.IsNullOrWhiteSpace(contentSignature) || jsonContentsBlob.Length == 0) { return(false); } var signature = default(Signature); try { signature = ParseContentSignatureElements(contentSignature); } catch (ArgumentException) { return(false); } // Download chain file string chainContents; using (var chainResponse = await UpdateHttpClient.QueryWithRetryAsync(signature.X509Url, MaxHttpRetries)) { if (!chainResponse.IsSuccessStatusCode) { return(false); } chainContents = await chainResponse.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(chainContents)) { return(false); } } // Parse chain file var x509Chain = new X509Chain(); try { x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; x509Chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; x509Chain.ChainPolicy.ApplicationPolicy.Add(new Oid(UpdateCodeSigningOid)); X509Certificate2 x509Leaf = null; var chainCerts = Regex.Split(chainContents, @"(?=-----BEGIN CERTIFICATE-----\n)").Where(item => !string.IsNullOrWhiteSpace(item)).ToList(); var x509ChainCerts = new List <X509Certificate2>(); chainCerts.ForEach(chainCert => { x509ChainCerts.Add(new X509Certificate2(Encoding.UTF8.GetBytes(chainCert))); }); // Get the leaf certificate x509Leaf = x509ChainCerts.FirstOrDefault(); if (x509Leaf == null) { return(false); } // All other certs x509ChainCerts.GetRange(1, x509ChainCerts.Count - 1).ForEach(cert => { if (cert != null) { x509Chain.ChainPolicy.ExtraStore.Add(cert); } }); // Attempt to build chain if (!x509Chain.Build(x509Leaf)) { return(false); } // Check whether all certs within the chain (with the leaf) were built successfully if (x509Chain.ChainElements.Count != x509Chain.ChainPolicy.ExtraStore.Count + 1) { return(false); } // Check root certificate var rootCert = x509Chain.ChainElements[x509Chain.ChainElements.Count - 1]; using (var sha256Hasher = SHA256.Create()) { var w = BitConverter.ToString(sha256Hasher.ComputeHash(rootCert.Certificate.RawData)).Replace("-", ":"); if (w != UpdateRootFingerprint) { return(false); } } // Validate cert subject if (x509Leaf.SubjectName.Decode(X500DistinguishedNameFlags.UseNewLines).Split(new[] { Environment.NewLine }, StringSplitOptions.None).FirstOrDefault((str) => str.TrimStart().StartsWith("CN=")) != UpdateCertSubject) { return(false); } // Verify JSON data var ecdsaPublicKey = x509Leaf.GetECDsaPublicKey(); if (ecdsaPublicKey == null) { return(false); } byte[] verificationData = Encoding.UTF8.GetBytes("Content-Signature:").Concat(new byte[] { 0 }).Concat(jsonContentsBlob).ToArray(); if (!ecdsaPublicKey.VerifyData(verificationData, signature.SignatureBlob, signature.HashAlgorithm)) { return(false); } return(true); } catch (Exception) { return(false); } finally { x509Chain.Dispose(); } }
private static void OnRequestSendingRequest(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle is null"); if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https) { // Not SSL/TLS. return; } // Grab the channel binding token (CBT) information from the request handle and put it into // the TransportContext object. state.TransportContext.SetChannelBinding(state.RequestHandle); if (state.ServerCertificateValidationCallback != null) { IntPtr certHandle = IntPtr.Zero; uint certHandleSize = (uint)IntPtr.Size; if (!Interop.WinHttp.WinHttpQueryOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CERT_CONTEXT, ref certHandle, ref certHandleSize)) { int lastError = Marshal.GetLastWin32Error(); WinHttpTraceHelper.Trace( "OnRequestSendingRequest: Error getting WINHTTP_OPTION_SERVER_CERT_CONTEXT, {0}", lastError); if (lastError == Interop.WinHttp.ERROR_WINHTTP_INCORRECT_HANDLE_STATE) { // Not yet an SSL/TLS connection. This occurs while connecting thru a proxy where the // CONNECT verb hasn't yet been processed due to the proxy requiring authentication. // We need to ignore this notification. Another notification will be sent once the final // connection thru the proxy is completed. return; } throw WinHttpException.CreateExceptionUsingError(lastError); } // Get any additional certificates sent from the remote server during the TLS/SSL handshake. X509Certificate2Collection remoteCertificateStore = UnmanagedCertificateContext.GetRemoteCertificatesFromStoreContext(certHandle); // Create a managed wrapper around the certificate handle. Since this results in duplicating // the handle, we will close the original handle after creating the wrapper. var serverCertificate = new X509Certificate2(certHandle); Interop.Crypt32.CertFreeCertificateContext(certHandle); X509Chain chain = null; SslPolicyErrors sslPolicyErrors; bool result = false; try { WinHttpCertificateHelper.BuildChain( serverCertificate, remoteCertificateStore, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); } catch (Exception ex) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE, ex); } finally { if (chain != null) { chain.Dispose(); } serverCertificate.Dispose(); } if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } }
internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle certHandle, SafeEvpPKeyHandle certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions) { SafeSslHandle context = null; // Always use SSLv23_method, regardless of protocols. It supports negotiating to the highest // mutually supported version and can thus handle any of the set protocols, and we then use // SetProtocolOptions to ensure we only allow the ones requested. using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method)) { if (innerContext.IsInvalid) { throw CreateSslException(SR.net_allocate_ssl_context_failed); } // TLS 1.3 uses different ciphersuite restrictions than previous versions. // It has no equivalent to a NoEncryption option. if (policy == EncryptionPolicy.NoEncryption) { if (protocols == SslProtocols.None) { protocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; } else { protocols &= ~SslProtocols.Tls13; if (protocols == SslProtocols.None) { throw new SslException( SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } } } // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just // create the handle, it's rooted by the using, no one else has a reference to it, etc. Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols); // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet // shutdown (we aren't negotiating for session close to enable later session // restoration). // // If you find yourself wanting to remove this line to enable bidirectional // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect(). // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html Ssl.SslCtxSetQuietShutdown(innerContext); if (!Ssl.SetEncryptionPolicy(innerContext, policy)) { Crypto.ErrClearError(); throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy)); } bool hasCertificateAndKey = certHandle != null && !certHandle.IsInvalid && certKeyHandle != null && !certKeyHandle.IsInvalid; if (hasCertificateAndKey) { SetSslCertificate(innerContext, certHandle, certKeyHandle); } if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) { Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate); } GCHandle alpnHandle = default; try { if (sslAuthenticationOptions.ApplicationProtocols != null) { if (sslAuthenticationOptions.IsServer) { alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols); Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle)); } else { if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0) { throw CreateSslException(SR.net_alpn_config_failed); } } } context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer); Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create"); if (context.IsInvalid) { context.Dispose(); throw CreateSslException(SR.net_allocate_ssl_context_failed); } if (!sslAuthenticationOptions.IsServer) { // The IdnMapping converts unicode input into the IDNA punycode sequence. string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost); // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors. if (!Ssl.SslSetTlsExtHostName(context, punyCode)) { Crypto.ErrClearError(); } } if (hasCertificateAndKey) { bool hasCertReference = false; try { certHandle.DangerousAddRef(ref hasCertReference); using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) { X509Chain chain = null; try { chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false); if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) { throw CreateSslException(SR.net_ssl_use_cert_failed); } } finally { if (chain != null) { int elementsCount = chain.ChainElements.Count; for (int i = 0; i < elementsCount; i++) { chain.ChainElements[i].Certificate.Dispose(); } chain.Dispose(); } } } } finally { if (hasCertReference) { certHandle.DangerousRelease(); } } } context.AlpnHandle = alpnHandle; } catch { if (alpnHandle.IsAllocated) { alpnHandle.Free(); } throw; } } return(context); }
private int TlsClientCertCallback(IntPtr ssl, out IntPtr certHandle, out IntPtr privateKeyHandle) { EventSourceTrace("SSL: {0}", ssl); const int CertificateSet = 1, NoCertificateSet = 0, SuspendHandshake = -1; certHandle = IntPtr.Zero; privateKeyHandle = IntPtr.Zero; if (ssl == IntPtr.Zero) { Debug.Fail("Expected valid SSL pointer"); EventSourceTrace("Invalid SSL pointer in callback"); return(NoCertificateSet); } SafeSslHandle sslHandle = null; X509Chain chain = null; X509Certificate2 certificate = null; try { sslHandle = new SafeSslHandle(ssl, ownsHandle: false); ISet <string> issuerNames = GetRequestCertificateAuthorities(sslHandle); if (_clientCertificates != null) // manual mode { // If there's one certificate, just use it. Otherwise, try to find the best one. int certCount = _clientCertificates.Count; if (certCount == 1) { EventSourceTrace("Single certificate. Building chain."); certificate = _clientCertificates[0]; chain = TLSCertificateExtensions.BuildNewChain(certificate, includeClientApplicationPolicy: false); } else { EventSourceTrace("Finding the best of {0} certificates", certCount); if (!_clientCertificates.TryFindClientCertificate(issuerNames, out certificate, out chain)) { EventSourceTrace("No certificate set."); return(NoCertificateSet); } } EventSourceTrace("Chain built."); } else if (!GetAutomaticClientCertificate(issuerNames, out certificate, out chain)) // automatic mode { EventSourceTrace("No automatic certificate or chain."); return(NoCertificateSet); } SafeEvpPKeyHandle privateKeySafeHandle = null; Interop.Crypto.CheckValidOpenSslHandle(certificate.Handle); using (RSAOpenSsl rsa = certificate.GetRSAPrivateKey() as RSAOpenSsl) { if (rsa != null) { privateKeySafeHandle = rsa.DuplicateKeyHandle(); EventSourceTrace("RSA key"); } else { using (ECDsaOpenSsl ecdsa = certificate.GetECDsaPrivateKey() as ECDsaOpenSsl) { if (ecdsa != null) { privateKeySafeHandle = ecdsa.DuplicateKeyHandle(); EventSourceTrace("ECDsa key"); } } } } if (privateKeySafeHandle == null || privateKeySafeHandle.IsInvalid) { EventSourceTrace("Invalid private key"); return(NoCertificateSet); } SafeX509Handle certSafeHandle = Interop.Crypto.X509UpRef(certificate.Handle); Interop.Crypto.CheckValidOpenSslHandle(certSafeHandle); if (chain != null) { if (!Interop.Ssl.AddExtraChainCertificates(sslHandle, chain)) { EventSourceTrace("Failed to add extra chain certificate"); return(SuspendHandshake); } } certHandle = certSafeHandle.DangerousGetHandle(); privateKeyHandle = privateKeySafeHandle.DangerousGetHandle(); EventSourceTrace("Client certificate set: {0}", certificate); // Ownership has been transferred to OpenSSL; do not free these handles certSafeHandle.SetHandleAsInvalid(); privateKeySafeHandle.SetHandleAsInvalid(); return(CertificateSet); } finally { if (_clientCertificates == null) { certificate?.Dispose(); // only dispose cert if it's automatic / newly created } chain?.Dispose(); sslHandle?.Dispose(); } }
private static void OnRequestSendingRequest(WinHttpRequestState state) { Debug.Assert(state != null, "OnRequestSendingRequest: state is null"); Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle is null"); if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https) { // Not SSL/TLS. return; } // Grab the channel binding token (CBT) information from the request handle and put it into // the TransportContext object. state.TransportContext.SetChannelBinding(state.RequestHandle); if (state.ServerCertificateValidationCallback != null) { IntPtr certHandle = IntPtr.Zero; uint certHandleSize = (uint)IntPtr.Size; if (!Interop.WinHttp.WinHttpQueryOption( state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_SERVER_CERT_CONTEXT, ref certHandle, ref certHandleSize)) { int lastError = Marshal.GetLastWin32Error(); throw WinHttpException.CreateExceptionUsingError(lastError); } // Create a managed wrapper around the certificate handle. Since this results in duplicating // the handle, we will close the original handle after creating the wrapper. var serverCertificate = new X509Certificate2(certHandle); Interop.Crypt32.CertFreeCertificateContext(certHandle); X509Chain chain = null; SslPolicyErrors sslPolicyErrors; try { WinHttpCertificateHelper.BuildChain( serverCertificate, state.RequestMessage.RequestUri.Host, state.CheckCertificateRevocationList, out chain, out sslPolicyErrors); bool result = state.ServerCertificateValidationCallback( state.RequestMessage, serverCertificate, chain, sslPolicyErrors); if (!result) { throw WinHttpException.CreateExceptionUsingError( (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE); } } finally { if (chain != null) { chain.Dispose(); } serverCertificate.Dispose(); } } }
private bool validationCallback(object sender, X509Certificate certificate, X509Chain chainEngine, SslPolicyErrors policyErrors) { X509Chain chain = new X509Chain(_instance.engine().useMachineContext()); try { if (_instance.checkCRL() == 0) { chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; } X509Certificate2Collection?caCerts = _instance.engine().caCerts(); if (caCerts != null) { // // We need to set this flag to be able to use a certificate authority from the extra store. // chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; foreach (X509Certificate2 cert in caCerts) { chain.ChainPolicy.ExtraStore.Add(cert); } } string message = ""; int errors = (int)policyErrors; if (certificate != null) { chain.Build(new X509Certificate2(certificate)); if (chain.ChainStatus != null && chain.ChainStatus.Length > 0) { errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors; } else if (_instance.engine().caCerts() != null) { X509ChainElement e = chain.ChainElements[chain.ChainElements.Count - 1]; if (!chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) { if (_verifyPeer > 0) { message += "\npuntrusted root certificate"; } else { message += "\nuntrusted root certificate (ignored)"; _verified = false; } errors |= (int)SslPolicyErrors.RemoteCertificateChainErrors; } else { _verified = true; return(true); } } else { _verified = true; return(true); } } if ((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) > 0) { // // The RemoteCertificateNotAvailable case does not appear to be possible // for an outgoing connection. Since .NET requires an authenticated // connection, the remote peer closes the socket if it does not have a // certificate to provide. // if (_incoming) { if (_verifyPeer > 1) { if (_instance.securityTraceLevel() >= 1) { _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation failed - client certificate not provided"); } return(false); } errors ^= (int)SslPolicyErrors.RemoteCertificateNotAvailable; message += "\nremote certificate not provided (ignored)"; } } bool certificateNameMismatch = (errors & (int)SslPolicyErrors.RemoteCertificateNameMismatch) > 0; if (certificateNameMismatch) { if (_instance.engine().getCheckCertName() && !string.IsNullOrEmpty(_host)) { if (_instance.securityTraceLevel() >= 1) { string msg = "SSL certificate validation failed - Hostname mismatch"; if (_verifyPeer == 0) { msg += " (ignored)"; } _instance.logger().trace(_instance.securityTraceCategory(), msg); } if (_verifyPeer > 0) { return(false); } else { errors ^= (int)SslPolicyErrors.RemoteCertificateNameMismatch; } } else { errors ^= (int)SslPolicyErrors.RemoteCertificateNameMismatch; certificateNameMismatch = false; } } if ((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0 && chain.ChainStatus != null && chain.ChainStatus.Length > 0) { int errorCount = 0; foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status == X509ChainStatusFlags.UntrustedRoot && _instance.engine().caCerts() != null) { // // Untrusted root is OK when using our custom chain engine if // the CA certificate is present in the chain policy extra store. // X509ChainElement e = chain.ChainElements[chain.ChainElements.Count - 1]; if (!chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) { if (_verifyPeer > 0) { message += "\npuntrusted root certificate"; ++errorCount; } else { message += "\nuntrusted root certificate (ignored)"; } } else { _verified = !certificateNameMismatch; } } else if (status.Status == X509ChainStatusFlags.Revoked) { if (_instance.checkCRL() > 0) { message += "\ncertificate revoked"; ++errorCount; } else { message += "\ncertificate revoked (ignored)"; } } else if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown) { // // If a certificate's revocation status cannot be determined, the strictest // policy is to reject the connection. // if (_instance.checkCRL() > 1) { message += "\ncertificate revocation status unknown"; ++errorCount; } else { message += "\ncertificate revocation status unknown (ignored)"; } } else if (status.Status == X509ChainStatusFlags.PartialChain) { if (_verifyPeer > 0) { message += "\npartial certificate chain"; ++errorCount; } else { message += "\npartial certificate chain (ignored)"; } } else if (status.Status != X509ChainStatusFlags.NoError) { message = message + "\ncertificate chain error: " + status.Status.ToString(); ++errorCount; } } if (errorCount == 0) { errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors; } } if (errors > 0) { if (_instance.securityTraceLevel() >= 1) { if (message.Length > 0) { _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation failed:" + message); } else { _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation failed"); } } return(false); } else if (message.Length > 0 && _instance.securityTraceLevel() >= 1) { _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation status:" + message); } return(true); } finally { if (chain.ChainElements != null && chain.ChainElements.Count > 0) { _certs = new X509Certificate2[chain.ChainElements.Count]; for (int i = 0; i < chain.ChainElements.Count; ++i) { _certs[i] = chain.ChainElements[i].Certificate; } } try { chain.Dispose(); } catch (Exception) { } } }
protected override async Task <Stream> OnInitiateUpgradeAsync(Stream stream, OutWrapper <SecurityMessageProperty> remoteSecurityWrapper) { if (WcfEventSource.Instance.SslOnInitiateUpgradeIsEnabled()) { WcfEventSource.Instance.SslOnInitiateUpgrade(); } // There is currently no way to convert a .Net X509Certificate2 to a UWP Certificate. The client certificate // needs to be provided by looking it up in the certificate store. E.g. // // factory.Credentials.ClientCertificate.SetCertificate( // StoreLocation.CurrentUser, // StoreName.My, // X509FindType.FindByThumbprint, // clientCertThumb); // // The certificate is retrieved using .Net api's and UWP api's. An artifical X509Extension is used to attach the // UWP certificate to the .Net X509Certificate2. This is then retrieved at the point of usage to use with UWP // networking api's. Certificate clientCertificate = null; if (_clientToken != null) { foreach (var extension in _clientToken.Certificate.Extensions) { var attachmentExtension = extension as X509CertificateInitiatorClientCredential.X509UwpCertificateAttachmentExtension; if (attachmentExtension != null && attachmentExtension.AttachedCertificate != null) { clientCertificate = attachmentExtension.AttachedCertificate; break; } } Contract.Assert(clientCertificate != null, "Missing UWP Certificate as an attachment to X509Certificate2"); } try { // Fetch the underlying raw transport object. For UWP, this will be a StreamSocket var connectionStream = stream as ConnectionStream; Contract.Assert(connectionStream != null, "stream is either null or not a ConnectionStream"); var rtStreamSocket = connectionStream.Connection.GetCoreTransport() as StreamSocket; Contract.Assert(rtStreamSocket != null, "Core transport is either null or not a StreamSocket"); rtStreamSocket.Control.ClientCertificate = clientCertificate; // On CoreClr, we use SslStream which calls a callback with any problems with the server certificate, which // returns whether to accept the certificate or not. With SocketStream in UWP, any custom validation needs to // happen after the connection has successfully negotiated. Some certificate errors need to be set to be ignored // to allow the connection to be established so we can retrieve the server certificate and choose whether to // accept the server certificate or not. rtStreamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); rtStreamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired); rtStreamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); rtStreamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain); rtStreamSocket.Control.IgnorableServerCertificateErrors.Add( ChainValidationResult.RevocationInformationMissing); rtStreamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); // SocketStream doesn't take a bitwise field of accepted protocols, but instead accepts a value specifying the highest // protocol that can be negotiated. A check is made for each of the protocols in order to see if they've been requested // by the binding and set the protection level to the UWP equivalent. This will have the effect of protectionLevel being // set to the most secure protocol that was specified by client code. After the connection is established, if a protocol // was negotiated which the binding didn't request, the connection needs to be aborted. This could happen for example if // the requested SslProtocols was SslProtocols.Tls11 | SslProtocols.Tls12 and the server only supported SSL3 | Tls10. In // this case, SocketProtectionLevel would be set to SocketProtectionLevel.Tls12, which would mean Tls10, Tls11 and Tls12 // are all acceptable protocols to negotiate. As the server is offering SSL3 | Tls10, the connection would be successfully // negotiated using Tls10 which isn't allowed according to the binding configuration. SocketProtectionLevel protectionLevel = SocketProtectionLevel.PlainSocket; if ((_parent.SslProtocols & SslProtocols.Tls) != SslProtocols.None) { protectionLevel = SocketProtectionLevel.Tls10; } if ((_parent.SslProtocols & SslProtocols.Tls11) != SslProtocols.None) { protectionLevel = SocketProtectionLevel.Tls11; } if ((_parent.SslProtocols & SslProtocols.Tls12) != SslProtocols.None) { protectionLevel = SocketProtectionLevel.Tls12; } // With SslStream, the hostname provided in the server certificate is provided to the client and verified in the callback. // With UWP StreamSocket, the hostname needs to be provided to the call to UpgradeToSslAsync. The code to fetch the identity // lives in the callback for CoreClr but needs to be pulled into this method for UWP. EndpointAddress remoteAddress = RemoteAddress; if (remoteAddress.Identity == null && remoteAddress.Uri != Via) { remoteAddress = new EndpointAddress(Via); } EndpointIdentity identity; if (!_parent.IdentityVerifier.TryGetIdentity(remoteAddress, out identity)) { SecurityTraceRecordHelper.TraceIdentityVerificationFailure(identity: identity, authContext: null, identityVerifier: GetType()); throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning( new MessageSecurityException(SR.Format(SR.IdentityCheckFailedForOutgoingMessage, identity, remoteAddress))); } Contract.Assert(identity.IdentityClaim.ClaimType == ClaimTypes.Dns); string dnsHostName = identity.IdentityClaim.Resource as string; // This is the actual call to negotiate an SSL connection await rtStreamSocket.UpgradeToSslAsync(protectionLevel, new HostName(dnsHostName)).AsTask(); // Verify that we didn't negotiate a protocol lower than the binding configuration specified. No need to check Tls12 // as it will only be negotiated if Tls12 was actually specified. var negotiatedProtectionLevel = rtStreamSocket.Information.ProtectionLevel; if ((negotiatedProtectionLevel == SocketProtectionLevel.Tls11 && (_parent.SslProtocols & SslProtocols.Tls11) == SslProtocols.None) || (negotiatedProtectionLevel == SocketProtectionLevel.Tls10 && (_parent.SslProtocols & SslProtocols.Tls) == SslProtocols.None)) { // Need to dispose StreamSocket as normally SslStream wouldn't end up in a usable state in this situation. As // post-upgrade validation is required in UWP, the connection needs to be Dispose'd to ensure it isn't used. rtStreamSocket.Dispose(); throw new SecurityNegotiationException(SR.Format(SR.SSLProtocolNegotiationFailed, _parent.SslProtocols, negotiatedProtectionLevel)); } X509Certificate2 serverCertificate = null; X509Certificate2[] chainCertificates = null; X509Chain chain = null; try { // Convert the UWP Certificate object to a .Net X509Certificate2. byte[] serverCertificateBlob = rtStreamSocket.Information.ServerCertificate.GetCertificateBlob().ToArray(); serverCertificate = new X509Certificate2(serverCertificateBlob); // The chain building and validation logic is done by SslStream in CoreClr. This section of code is based // on the SslStream implementation to try to maintain behavior parity. chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; var serverIntermediateCertificates = rtStreamSocket.Information.ServerIntermediateCertificates; chainCertificates = new X509Certificate2[serverIntermediateCertificates.Count]; for (int i = 0; i < chainCertificates.Length; i++) { chainCertificates[i] = new X509Certificate2(serverIntermediateCertificates[i].GetCertificateBlob().ToArray()); } chain.ChainPolicy.ExtraStore.AddRange(chainCertificates); chain.Build(serverCertificate); SslPolicyErrors policyErrors = SslPolicyErrors.None; foreach (var serverCertificateError in rtStreamSocket.Information.ServerCertificateErrors) { if (serverCertificateError == ChainValidationResult.InvalidName) { policyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; continue; } if (serverCertificateError == ChainValidationResult.IncompleteChain) { policyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } } X509ChainStatus[] chainStatusArray = chain.ChainStatus; if (chainStatusArray != null && chainStatusArray.Length != 0) { policyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; } if (!ValidateRemoteCertificate(this, serverCertificate, chain, policyErrors)) { // Need to dispose StreamSocket as normally SslStream wouldn't end up in a usable state in this situation. As // post-upgrade validation is required in UWP, the connection needs to be Dispose'd to ensure it isn't used. rtStreamSocket.Dispose(); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityNegotiationException(SR.ssl_io_cert_validation)); } } finally { serverCertificate?.Dispose(); chain?.Dispose(); if (chainCertificates != null) { foreach (var chainCert in chainCertificates) { chainCert?.Dispose(); } } } } catch (SecurityTokenValidationException tokenValidationException) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityNegotiationException(tokenValidationException.Message, tokenValidationException)); } catch (IOException ioException) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException( SR.Format(SR.NegotiationFailedIO, ioException.Message), ioException)); } catch (Exception exception) { // In NET Native the WinRT API's can throw the base Exception // class with an HRESULT indicating the issue. However, custom // validation code can also throw Exception, and to be compatible // with the CoreCLR version, we must allow those exceptions to // propagate without wrapping them. We use the simple heuristic // that if an HRESULT has been set to other than the default, // the exception should be wrapped in SecurityNegotiationException. if (exception.HResult == __HResults.COR_E_EXCEPTION) { throw; } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException( exception.Message, exception)); } remoteSecurityWrapper.Value = _serverSecurity; return(stream); }
/// <summary> /// Frees all resources used by the <see cref="AuthenticodeKeyVaultSigner" />. /// </summary> public void Dispose() { _chain.Dispose(); _certificateStore.Close(); }