Ejemplo n.º 1
0
        HandshakeType SendCertificateVerify(ref int offset)
        {
#if NET45 || NET451
            var key = new X509Certificate2(_clientCertificates[0]).PrivateKey;

            var keyDsa = key as DSACryptoServiceProvider;
            var keyRsa = key as RSACryptoServiceProvider;
#else
            var keyRsa = new X509Certificate2(_clientCertificates[0].Export(X509ContentType.Cert)).GetRSAPrivateKey();
#endif

            byte[] signature = null, hash = null;

#if NET45 || NET451
            if (keyDsa != null)
            {
                if (_pendingConnState.TlsVersion == TlsVersion.TLSv1_2 && !_handshakeData.SupportedSignatureAlgorithms.Contains(Tuple.Create(TLSHashAlgorithm.SHA1, SignatureAlgorithm.DSA)))
                {
                    SendAlertFatal(AlertDescription.HandshakeFailure, "Server does not support client certificate sha1-dsa signatures");
                }
                hash = _handshakeData.CertificateVerifyHash_SHA1.Final();
                signature = keyDsa.SignHash(hash, Utils.HashNameToOID["SHA1"]);

                // Convert to DER
                var r = new byte[21];
                var s = new byte[21];
                Array.Reverse(signature, 0, 20);
                Array.Reverse(signature, 20, 20);
                Buffer.BlockCopy(signature, 0, r, 0, 20);
                Buffer.BlockCopy(signature, 20, s, 0, 20);
                r = new BigInteger(r).ToByteArray();
                s = new BigInteger(s).ToByteArray();
                Array.Reverse(r);
                Array.Reverse(s);

                signature = new byte[r.Length + s.Length + 6];
                signature[0] = 0x30; // SEQUENCE
                signature[1] = (byte)(signature.Length - 2);
                signature[2] = 0x02; // INTEGER
                signature[3] = (byte)r.Length;
                Buffer.BlockCopy(r, 0, signature, 4, r.Length);
                signature[4 + r.Length] = 0x02; // INTEGER
                signature[4 + r.Length + 1] = (byte)s.Length;
                Buffer.BlockCopy(s, 0, signature, 4 + r.Length + 2, s.Length);

                if (_pendingConnState.TlsVersion == TlsVersion.TLSv1_2)
                {
                    _buf[offset++] = (byte)TLSHashAlgorithm.SHA1;
                    _buf[offset++] = (byte)SignatureAlgorithm.DSA;
                }
            }
            else
#endif
            if (keyRsa != null)
            {
                if (_pendingConnState.TlsVersion == TlsVersion.TLSv1_2 && !_handshakeData.SupportedSignatureAlgorithms.Contains(Tuple.Create(TLSHashAlgorithm.SHA1, SignatureAlgorithm.RSA)))
                {
                    SendAlertFatal(AlertDescription.HandshakeFailure, "Server does not support client certificate sha1-rsa signatures");
                }

                hash = _handshakeData.CertificateVerifyHash_SHA1.Final();
                byte[] md5Hash = null;
                if (_handshakeData.CertificateVerifyHash_MD5 != null)
                {
                    md5Hash = _handshakeData.CertificateVerifyHash_MD5.Final();
                }

                // NOTE: It seems problematic to support other hash algorithms than SHA1 since the PrivateKey included in the certificate
                // often uses an old Crypto Service Provider (Microsoft Base Cryptographic Provider v1.0) instead of Microsoft Enhanced RSA and AES Cryptographic Provider.
                // The following out-commented code might work to change CSP.

                //var csp = new RSACryptoServiceProvider().CspKeyContainerInfo;
                //keyRsa = new RSACryptoServiceProvider(new CspParameters(csp.ProviderType, csp.ProviderName, keyRsa.CspKeyContainerInfo.KeyContainerName));

                // TLS 1.0 and 1.1, export private key and calculate md5-sha1 hash and sign manually
                if (_pendingConnState.TlsVersion != TlsVersion.TLSv1_2)
                {
                    var fullHash = new byte[36];
                    Buffer.BlockCopy(md5Hash, 0, fullHash, 0, 16);
                    Buffer.BlockCopy(hash, 0, fullHash, 16, 20);
                    signature = RsaPKCS1.SignRsaPKCS1(keyRsa, fullHash);

                    // BigIntegers have no Dispose/Clear methods, but they contain sensitive data, so force a garbage collection to remove the data.
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, false);
                }
                else
                {
#if NET45 || NET451
                    signature = keyRsa.SignHash(hash, Utils.HashNameToOID["SHA1"]);
#else
                    signature = keyRsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
#endif

                    if (_pendingConnState.TlsVersion == TlsVersion.TLSv1_2)
                    {
                        _buf[offset++] = (byte)TLSHashAlgorithm.SHA1;
                        _buf[offset++] = (byte)SignatureAlgorithm.RSA;
                    }
                }
            }
            else
            {
                SendAlertFatal(AlertDescription.HandshakeFailure);
            }
            _handshakeData.CertificateVerifyHash_SHA1.Dispose();
            _handshakeData.CertificateVerifyHash_SHA1 = null;
            if (_handshakeData.CertificateVerifyHash_MD5 != null)
            {
                _handshakeData.CertificateVerifyHash_MD5.Dispose();
                _handshakeData.CertificateVerifyHash_MD5 = null;
            }

#if NET45 || NET451
            key.Dispose();
#endif

            offset += Utils.WriteUInt16(_buf, offset, (ushort)signature.Length);
            Buffer.BlockCopy(signature, 0, _buf, offset, signature.Length);
            offset += signature.Length;

            return HandshakeType.CertificateVerify;
        }
        public void CloneTo(X509Certificate2Collection collection)
        {
            Debug.Assert(collection != null);

            if (!Directory.Exists(_storePath))
            {
                return;
            }

            var loadedCerts = new HashSet<X509Certificate2>();

            foreach (string filePath in Directory.EnumerateFiles(_storePath, PfxWildcard))
            {
                try
                {
                    var cert = new X509Certificate2(filePath);

                    // If we haven't already loaded a cert .Equal to this one, copy it to the collection.
                    if (loadedCerts.Add(cert))
                    {
                        collection.Add(cert);
                    }
                    else
                    {
                        cert.Dispose();
                    }
                }
                catch (CryptographicException)
                {
                    // The file wasn't a certificate, move on to the next one.
                }
            }
        }
Ejemplo n.º 3
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();
                    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);
                }
                
                // Create a managed wrapper around the certificate handle. Since this results in duplicating
                // the handle, we will close the original handle after creating the wrapper.
                var serverCertificate = new X509Certificate2(certHandle);
                Interop.Crypt32.CertFreeCertificateContext(certHandle);

                X509Chain chain = null;
                SslPolicyErrors sslPolicyErrors;

                try
                {
                    WinHttpCertificateHelper.BuildChain(
                        serverCertificate,
                        state.RequestMessage.RequestUri.Host,
                        state.CheckCertificateRevocationList,
                        out chain,
                        out sslPolicyErrors);

                    bool result = state.ServerCertificateValidationCallback(
                        state.RequestMessage,
                        serverCertificate,
                        chain,
                        sslPolicyErrors);
                    if (!result)
                    {
                        throw WinHttpException.CreateExceptionUsingError(
                            (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE);
                    }
                }
                finally
                {
                    if (chain != null)
                    {
                        chain.Dispose();
                    }

                    serverCertificate.Dispose();
                }
            }
        }
        private static Tuple <SafeX509StackHandle, SafeX509StackHandle> LoadMachineStores(
            DirectoryInfo?rootStorePath,
            FileInfo?rootStoreFile)
        {
            Debug.Assert(
                Monitor.IsEntered(s_recheckStopwatch),
                "LoadMachineStores assumes a lock(s_recheckStopwatch)");

            SafeX509StackHandle rootStore = Interop.Crypto.NewX509Stack();

            Interop.Crypto.CheckValidOpenSslHandle(rootStore);
            SafeX509StackHandle intermedStore = Interop.Crypto.NewX509Stack();

            Interop.Crypto.CheckValidOpenSslHandle(intermedStore);

            DateTime newFileTime = default;
            DateTime newDirTime  = default;

            var  uniqueRootCerts         = new HashSet <X509Certificate2>();
            var  uniqueIntermediateCerts = new HashSet <X509Certificate2>();
            bool firstLoad = (s_nativeCollections == null);

            if (rootStoreFile != null && rootStoreFile.Exists)
            {
                newFileTime = ContentWriteTime(rootStoreFile);
                ProcessFile(rootStoreFile);
            }

            bool hasStoreData = false;

            if (rootStorePath != null && rootStorePath.Exists)
            {
                newDirTime   = ContentWriteTime(rootStorePath);
                hasStoreData = ProcessDir(rootStorePath);
            }

            if (firstLoad && !hasStoreData && s_defaultRootDir)
            {
                DirectoryInfo etcSslCerts = new DirectoryInfo("/etc/ssl/certs");

                if (etcSslCerts.Exists)
                {
                    DateTime tmpTime = ContentWriteTime(etcSslCerts);
                    hasStoreData = ProcessDir(etcSslCerts);

                    if (hasStoreData)
                    {
                        newDirTime = tmpTime;
                        s_rootStoreDirectoryInfo = etcSslCerts;
                    }
                }
            }

            bool ProcessDir(DirectoryInfo dir)
            {
                bool hasStoreData = false;

                foreach (FileInfo file in dir.EnumerateFiles())
                {
                    hasStoreData |= ProcessFile(file);
                }

                return(hasStoreData);
            }

            bool ProcessFile(FileInfo file)
            {
                bool readData = false;

                using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb"))
                {
                    // The handle may be invalid, for example when we don't have read permission for the file.
                    if (fileBio.IsInvalid)
                    {
                        Interop.Crypto.ErrClearError();
                        return(false);
                    }

                    // Some distros ship with two variants of the same certificate.
                    // One is the regular format ('BEGIN CERTIFICATE') and the other
                    // contains additional AUX-data ('BEGIN TRUSTED CERTIFICATE').
                    // The additional data contains the appropriate usage (e.g. emailProtection, serverAuth, ...).
                    // Because we don't validate for a specific usage, derived certificates are rejected.
                    // For now, we skip the certificates with AUX data and use the regular certificates.
                    ICertificatePal?pal;
                    while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal) ||
                           OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal))
                    {
                        readData = true;
                        X509Certificate2 cert = new X509Certificate2(pal);

                        // The HashSets are just used for uniqueness filters, they do not survive this method.
                        if (StringComparer.Ordinal.Equals(cert.Subject, cert.Issuer))
                        {
                            if (uniqueRootCerts.Add(cert))
                            {
                                using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle))
                                {
                                    if (!Interop.Crypto.PushX509StackField(rootStore, tmp))
                                    {
                                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                                    }

                                    // The ownership has been transferred to the stack
                                    tmp.SetHandleAsInvalid();
                                }

                                continue;
                            }
                        }
                        else
                        {
                            if (uniqueIntermediateCerts.Add(cert))
                            {
                                using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle))
                                {
                                    if (!Interop.Crypto.PushX509StackField(intermedStore, tmp))
                                    {
                                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                                    }

                                    // The ownership has been transferred to the stack
                                    tmp.SetHandleAsInvalid();
                                }

                                continue;
                            }
                        }

                        // There's a good chance we'll encounter duplicates on systems that have both one-cert-per-file
                        // and one-big-file trusted certificate stores. Anything that wasn't unique will end up here.
                        cert.Dispose();
                    }
                }

                return(readData);
            }

            foreach (X509Certificate2 cert in uniqueRootCerts)
            {
                cert.Dispose();
            }

            foreach (X509Certificate2 cert in uniqueIntermediateCerts)
            {
                cert.Dispose();
            }

            Tuple <SafeX509StackHandle, SafeX509StackHandle> newCollections =
                Tuple.Create(rootStore, intermedStore);

            Debug.Assert(
                Monitor.IsEntered(s_recheckStopwatch),
                "LoadMachineStores assumes a lock(s_recheckStopwatch)");

            // The existing collections are not Disposed here, intentionally.
            // They could be in the gap between when they are returned from this method and not yet used
            // in a P/Invoke, which would result in exceptions being thrown.
            // In order to maintain "finalization-free" the GetNativeCollections method would need to
            // DangerousAddRef, and the callers would need to DangerousRelease, adding more interlocked operations
            // on every call.

            Volatile.Write(ref s_nativeCollections, newCollections);
            s_directoryCertsLastWrite = newDirTime;
            s_fileCertsLastWrite      = newFileTime;
            s_recheckStopwatch.Restart();
            return(newCollections);
        }
Ejemplo n.º 5
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();
                }
            }
        }