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;
}
}
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:
...I would instead look for what might be wrong yet acceptable, and reject any other policy errors:
Something like this for the first part (I did not test or compile this):
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.