Why won't SslStream send my client certificate?

0

I have been trying to get this to work for many hours now, but I can't find any information on what is going wrong. Stepping through my code, I eventually reach a part like like this:

clientCerts = new X509CertificateCollection(new[] { <mycert> as X509Certificate });
stream.AuthenticateAsClientAsync(<host>, clientCerts, SslProtocols.Tls12, false); // Continuation omitted

My goal here is to provide a client certificate to a server on the other side. However I got complaints that there were none and the connection failed. I fired up Wireshark and I saw that the last thing the server sent was:

Handshake Protocol: Certificate Request
    Handshake Type: Certificate Request (13)
    Length: 19
    Certificate types count: 2
    Certificate types (2 types)
        Certificate type: RSA Sign (1)
        Certificate type: ECDSA Sign (64)
    Signature Hash Algorithms Length: 12
    Signature Hash Algorithms (6 algorithms)
        Signature Algorithm: rsa_pkcs1_sha384 (0x0501)
        Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
        Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
        Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
        Signature Algorithm: rsa_pkcs1_sha1 (0x0201)
        Signature Algorithm: ecdsa_sha1 (0x0203)
    Distinguished Names Length: 0

So far so good, so naturally since I specified a client certificate in the authenticate method I would expect it to be sent back but...

Handshake Protocol: Certificate
    Handshake Type: Certificate (11)
    Length: 3
    Certificates Length: 0

No explanation why, it just refuses to send it. I created the cert in the actual test that I ran and stored it into the Current User MY store:

var ecdsa = ECDsa.Create();
var req = new CertificateRequest($"cn={name}", ecdsa, HashAlgorithmName.SHA256);
var generatedCert= req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));
using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) {
                    store.Open(OpenFlags.ReadWrite);
                    store.Add(generatedCert);
                    // Do stuff....
                    store.Remove(generatedCert);
}

I loaded up some .NET Core tracing and I found some encouraging trace lines, but no errors. It's obviously recognizing it as a valid usable certificate but it just doesn't send it:

Event Name                                   Time MSec  Process Name            Rest  
Microsoft-System-Net-Security/CertIsType2   12,600.157  Process(84336) (84336)  ThreadID="38,972" ProcessorNumber="6" secureChannelHash="23,128,995" FormattedMessage="Certificate is of type X509Certificate2 and contains the private key." 
Microsoft-System-Net-Security/SelectedCert  12,631.155  Process(84336) (84336)  ThreadID="38,972" ProcessorNumber="6" clientCertificate="<long string that matches what I expect>" secureChannelHash="23,128,995" FormattedMessage="Selected certificate: <long string again>." 

I do see some things talking about "errorCode:0x00090312,refContext:SafeDeleteContext_SECURITY:27780689(0x0)" but I can only tell that 0x00090312 corresponds to SEC_I_INCOMPLETE_CREDENTIALS which apparently means

The server has requested client authentication, but either the supplied credentials do not include a certificate, or the certificate was not issued by a certification authority that the server trusts.

Well...how am I supposed to know what the server trusts without sending it over the wire to see? The other side is code that I work on as well so I know it's waiting to parse the certificate message and check the cert but it can't because no cert is sent. I don't get it, what should I do here?

c#
sslstream
asked on Stack Overflow Jul 10, 2020 by borrrden

1 Answer

0

Ok so in true newbie form, it turns out that I wasn't using a valid signature algorithm. I had to specify up front the curve to use for the key ala

ECDsa.Create() -> ECDsa.Create(ECCurve.CreateFromValue("1.2.840.10045.3.1.7")); to match the secp256r1 part of ecdsa_secp256r1_sha256 (I got that value from here)

answered on Stack Overflow Jul 11, 2020 by borrrden

User contributions licensed under CC BY-SA 3.0