Asking SslStream to accept ONLY a certificate signed by a particular public key

本小妞迷上赌 提交于 2019-12-11 03:55:37

问题


I have a working implementation of this but want to make sure it is secure. The goal is to use SSLStream and only accept SSL certificates from the server that are signed by a particular RSA key.

Here is my connection code:

        var client = new TcpClient("server_address", port_number);
        var sslStream = new SslStream(client.GetStream(), false,
            new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
        sslStream.AuthenticateAsClient("SpeechGrid");

And here is my implementation of ValidateServerCertificate:

    private static bool ValidateServerCertificate(object sender, X509Certificate certificate,
            X509Chain chain, SslPolicyErrors sslPolicyErrors) {

        // Only accept our specific key pair
        foreach (var cert in chain.ChainElements) {
            if (cert.Certificate.GetPublicKeyString() == k_prodPublicKey) {
                return true;
            }
        }

        return false;
    }

Because of the richness of the X509Chain object I want to make sure that I don't need to check for things like X509ChainStatusFlags.NotSignatureValid, etc.

For example, would it be possible for an attacker to "claim" to be signed by my public key, send an invalid signature, and this attack would work because .NET assumes I'm checking all of these flags?

Thanks!!

UPDATE: Ok, so far I've decided to put the following checks above the original foreach. Note that this is somewhat application specific; for example if I wanted certificates to expire I would check for NotTimeValid, etc.

        foreach (var status in chain.ChainStatus) {
            switch (status.Status) {
                case X509ChainStatusFlags.Cyclic:
                case X509ChainStatusFlags.NotSignatureValid:
                case X509ChainStatusFlags.PartialChain:
                    return false;
            }
        }

回答1:


I would reverse the logic of the check you added in the update to your question. Instead of looking for what might be wrong and accepting everything else:

foreach (thing that I can think of that might be wrong)
 return false;

if (public key matches regardless of other policy errors)
 return true;

...I would instead look for what might be wrong yet acceptable, and reject any other policy errors:

if (policy errors)
{
 foreach (error that is acceptable: remote name mismatch, untrusted root, etc.)
   policy errors -= that particular error
}

if (any policy errors left)
 return false;
else if (public key matches)
 return true;
else
 return false;

Something like this for the first part (I did not test or compile this):

if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
{
    sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
}

if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
{
    var otherFlagsFound =
        from i in chain.ChainStatus
        where (i.Status & ~X509ChainStatusFlags.UntrustedRoot) != X509ChainStatusFlags.NoError
        select i;

    if (otherFlagsFound.Count() == 0)
    {
        sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
    }
}



回答2:


You can check the sslPolicyErrors parameter for additional errors such as expired, or if the certificates are not trusted. If everything is ok it should return SslPolicyErrors.None. It is computationally unfeasible to derive a private key from a public key so you don't need to worry about someone else creating the same key pair and signing it.



来源:https://stackoverflow.com/questions/5225373/asking-sslstream-to-accept-only-a-certificate-signed-by-a-particular-public-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!