I have a certificate which I generated as self-signed and then had signed by a vendor. I have to connect to this vendor and authenticate using a certificate. When I do an AuthenticateAsClient, my LocalCertificateSelection callback gets called with the validIssuers parameter being set to the one who signed the certificate. What certificate should I be returning from the callback? When I return the certificate that was issued by the vendor I get an error because the certificate is not sent to the server. I am using the following code.
namespace TestClientAuthenticateHttps {
using System;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
class Program {
private readonly X509Certificate2Collection _certs;
private TcpClient _client;
private SslStream _sslStream;
private const string Host = "smr-staging.surescripts.net";
private const string SsSerial = "40 1f c1 ea 00 00 00 00 02 44";
private const SslProtocols EnabledSslProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
private static void Main() {
var program = new Program();
program.Execute();
}
private Program() {
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
_certs = certStore.Certificates.Find(X509FindType.FindBySerialNumber, SsSerial, false);
certStore.Close();
}
private void Execute() {
ConnectToHost();
try {
OpenSslConnection();
DoAuthentication();
} catch (Exception e) {
do {
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
e = e.InnerException;
} while (e != null);
} finally {
_sslStream.Close();
}
Console.ReadLine();
}
private void DoAuthentication() {
try {
_sslStream.AuthenticateAsClient(Host, _certs, EnabledSslProtocols, true);
} catch (Exception) {
Console.WriteLine($"Host is ({Host})");
Console.WriteLine($"_certs = ");
_certs.Cast<X509Certificate>().ToList().ForEach(Console.WriteLine);
throw;
}
}
private void OpenSslConnection() {
_sslStream = new SslStream(_client.GetStream(), false, RemoteCertificateValidate,
LocalCertificateSelection, EncryptionPolicy.AllowNoEncryption);
}
private static bool RemoteCertificateValidate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
return true;
}
private static X509Certificate LocalCertificateSelection(object sender, string host,
X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) {
Console.WriteLine($"Selecting certificate for ({host})");
var acceptable = acceptableIssuers.Select(s => new X500DistinguishedName(s).Name?.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
Console.WriteLine("Acceptable issuers.");
acceptable.ForEach(Console.WriteLine);
Console.WriteLine("Local certificate Selected");
var result = acceptableIssuers.Length == 0 ? localCertificates[0] : localCertificates?.Cast<X509Certificate2>().First(
c => acceptable.Any(n => n.Equals(c.IssuerName.Name?.Trim(), StringComparison.InvariantCultureIgnoreCase))
);
Console.WriteLine(result);
return result;
}
private void ConnectToHost() {
_client = new TcpClient(Host, 443);
}
}
}