예제 #1
0
        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);
        }
예제 #2
0
        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;
        }
예제 #3
0
 public void Dispose()
 {
     ClientCertificateList.Clear();
     CAChain.Dispose();
     ServerCertificate.Dispose();
     ClientCertificate.Dispose();
 }
예제 #4
0
        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();
                }
            }
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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();
        }
예제 #8
0
        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);
            }
        }
예제 #10
0
        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);
        }
예제 #11
0
        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();
                }
            }
        }
예제 #12
0
        /*++
         *  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();
                }
            }
예제 #14
0
        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);
        }
예제 #15
0
        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)
                {
                }
            }
        }
예제 #16
0
		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();
		}
예제 #17
0
        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);
                }
            }
        }
예제 #19
0
        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);
        }
예제 #20
0
            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();
                }
            }
예제 #21
0
        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();
                }
            }
        }
예제 #22
0
        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);
        }
예제 #24
0
 /// <summary>
 /// Frees all resources used by the <see cref="AuthenticodeKeyVaultSigner" />.
 /// </summary>
 public void Dispose()
 {
     _chain.Dispose();
     _certificateStore.Close();
 }