How to use certificate callback in SslStream.AuthenticateAsClient method?

前端 未结 3 1680
孤独总比滥情好
孤独总比滥情好 2021-01-14 18:47

My C#.NET SSL connect works when I import the certificate manually in IE (Tools/Internet Options/Content/Certificates), but how can I load the certificate by code? Here is m

相关标签:
3条回答
  • 2021-01-14 18:55

    A quick Google pointed me to a piece of text from the Microsoft SslStream class.

    The authentication is handled by the Security Support Provider (SSPI) channel provider. The client is given an opportunity to control validation of the server's certificate by specifying a RemoteCertificateValidationCallback delegate when creating an SslStream. The server can also control validation by supplying a RemoteCertificateValidationCallback delegate. The method referenced by the delegate includes the remote party's certificate and any errors SSPI encountered while validating the certificate. Note that if the server specifies a delegate, the delegate's method is invoked regardless of whether the server requested client authentication. If the server did not request client authentication, the server's delegate method receives a null certificate and an empty array of certificate errors.

    So simply implement the delegate and do the verification yourself.

    0 讨论(0)
  • 2021-01-14 19:03

    I wrote another method to add my certificate to Trusted Root Certification Authorities (root) before attempting to authenticate as client via SSLStream object:

    public static void InstallCertificate()
        {
            X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadWrite);
            string fileName = "sslcert.pem";
            X509Certificate2 certificate1;
            try
            {
                certificate1 = new X509Certificate2(fileName);
            }
            catch (Exception ex)
            {
                throw new Exception("Error loading SSL certificate file." + Environment.NewLine + fileName);
            }
    
            store.Add(certificate1);
            store.Close();
        }
    

    And then:

    InstallCertificate();
    sslStream.AuthenticateAsClient("Test");
    

    It works fine without any warnings or errors. But base question still remains unsolved:

    How can I use a certificate to authenticate as client without installing it in Windows?

    0 讨论(0)
  • 2021-01-14 19:06

    To build upon owlstead's answer, here's how I use a single CA certificate and a custom chain in the verification callback to avoid Microsoft's store.

    I have not figured out how to use this chain (chain2 below) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work". And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback. I think these are architectural defects in .Net, but I might be missing something obvious.

    The name of the function does not matter. Below, VerifyServerCertificate is the same callback as RemoteCertificateValidationCallback. You can also use it for the ServerCertificateValidationCallback in ServicePointManager.

    static bool VerifyServerCertificate(object sender, X509Certificate certificate,
        X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        try
        {
            String CA_FILE = "ca-cert.der";
            X509Certificate2 ca = new X509Certificate2(CA_FILE);
    
            X509Chain chain2 = new X509Chain();
            chain2.ChainPolicy.ExtraStore.Add(ca);
    
            // Check all properties
            chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
    
            // This setup does not have revocation information
            chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    
            // Build the chain
            chain2.Build(new X509Certificate2(certificate));
    
            // Are there any failures from building the chain?
            if (chain2.ChainStatus.Length == 0)
                return true;
    
            // If there is a status, verify the status is NoError
            bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
            Debug.Assert(result == true);
    
            return result;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    
        return false;
    }
    
    0 讨论(0)
提交回复
热议问题