Can you force HttpClient to only trust a single certificate?
I know you can do:
WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);
But will this force it to only trust that single certificate, or will it trust that certifate AND all certificates that fx. GlobalSign can verify?
Basicly I want to ensure that it can ONLY be my server/certificate that my client is talking to.
Can you force HttpClient to only trust a single certificate?
...
Basically I want to ensure that it can ONLY be my server/certificate that my client is talking to.
Yes. But what type of certificate? Server or CA? Examples for both follow.
Also, it might be better to pin the public key rather than the certificate in the case of a server. That's because some organizations, like Google, rotate their server certificates every 30 days or so in an effort to keep the CRLs small for mobile clients. However, the organizations will re-certify the same public key.
Here's an example of pinning the CA from Use a particular CA for a SSL connection. It does not require placing the certificate in a Certificate Store. You can carry the CA around in your app.
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 (NoFlag is correct)
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 false;
// 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;
}
I have not figured out how to use this chain (chain2
above) 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 because my chain2
is not passed into the functions as chain
.
Here's an example of pinning the server certificate from OWASP's Certificate and Public Key Pinning. It does not require placing the certificate in a Certificate Store. You can carry the certificate or public key around in your app.
// Encoded RSAPublicKey
private static String PUB_KEY = "30818902818100C4A06B7B52F8D17DC1CCB47362" +
"C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E" +
"D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2" +
"9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE" +
"2C3F4CBED9460129C72B0203010001";
public static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = PinPublicKey;
WebRequest wr = WebRequest.Create("https://encrypted.google.com/");
wr.GetResponse();
}
public static bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (null == certificate)
return false;
String pk = certificate.GetPublicKeyString();
if (pk.Equals(PUB_KEY))
return true;
// Bad dog
return false;
}
For anyone who comes across this in the future tou should be aware that some certificate authorities will no longer reissue certificates with the same public key when the certificate is renewed. We had this problem specifically with Globalsign who left us with the very difficult logistical problem of updating the client software with new public key pinning details for all our customers in a very short space of time, despite their published policy documents saying that they provided the option to reuse the public key. If this may be an issue for you confirm your certificate provider's policy in advance, and don't use Globalsign!
Client can use ServerCertificateValidationCallback
like below -
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
};