1

I have written a .NET application that tries to do mutual authentication with an ECC client certificate (curve is brainpoolP384r1). The certificate is signed by a PKI and when I look at it in an ASN1 viewer it seems to be perfectly valid. I get the certificate byte data for this the following way:

Console.WriteLine($"Certificate:{BitConverter.ToString(cert.RawData).Replace("-","")}");

Looking at the certificate in the MMC console it also appears to be valid.

However, when I try to use it to authenticate as a client it fails with CRYPT_E_ASN1_BADTAG.

The code-snippet where I try to authenticate:

TcpClient client = new TcpClient(serverName, 9909);
Console.WriteLine("Client connected.");
SslStream sslStream = new SslStream(
    client.GetStream(),
    false,
    new RemoteCertificateValidationCallback(ValidateServerCertificate),
    null
);

try
{
    var certificates = new X509CertificateCollection();
    certificates.Add(cert);
    sslStream.AuthenticateAsClient(serverName, certificates, SslProtocols.Tls12, false);
}
catch (Exception e)
{
    Console.WriteLine("Exception: {0} \n{1}", e.Message, e.StackTrace);
    if (e.InnerException != null)
    {
        Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
    }

    Console.WriteLine("Authentication failed - closing the connection.");
    client.Close();
    return;
}

The exception:

Exception: ASN1 Ungültiger Kennzeichenwert at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface secModule, String package, CredentialUse intent, SCHANNEL_CRED scc) at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(CredentialUse credUsage, SCHANNEL_CRED secureCredential) at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer) at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint) at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output) at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count) at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest) at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult) at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)

As usual for MS the reason for the failure is not detailed enough to fix the issue. Is there any way to find out which of the approximately 70 tags in the certificate is invalid?

One more piece of information: The keypair and the csr are created using bouncycastle as I did not find a way to do a cmp-request with .NET only. Could this be the reason? But why is the certificate displayed as valid in the mmc console?

Sorry for not providing a complete verifiable example but even the stripped down code for creating the csr, sending it, attaching the private key to the certificate and storing it in the MYStore is way to long and without a PKI absolutely useless.

UPDATE

The code to load the certificate from the store is as follows:

static X509Certificate2 LoadFromStore(string commonName)
{
    var distinguishedName = "CN=" + commonName;
    var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);

    X509Certificate2 result = null;

    foreach (var cert in store.Certificates)
    {
        if (cert.SubjectName.Name != null && cert.SubjectName.Name.Equals(distinguishedName))
        {
           Console.WriteLine("Found certificate in local store: " + cert.SubjectName.Name);

            result = cert;
            break;
        }
    }

    store.Close();
    return result;
}
Frank
  • 2,036
  • 1
  • 20
  • 32
  • 1
    How do you get the certificate? – pepo Feb 28 '18 at 08:20
  • @pepo Updated my question to contain the information how I load the certificate from the store. – Frank Feb 28 '18 at 08:36
  • Are you sure that the certificate has been found? What happens when you run [X509Certificate2.Verify](https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.verify(v=vs.110).aspx)? You are opening CurrentUser store. Are you sure that the certificate is in the correct user store (your store)? – pepo Feb 28 '18 at 09:14
  • @pepo That's funny. `cert.Verify()` returns false, but if I write the certificate bytes to the console as mentioned at the beginning of my question and paste the output to an ASN1 viewer everything looks good. – Frank Feb 28 '18 at 10:30
  • OK, try `certutil -user -store my` in commandline. Certutil will check all certificates in CurrentUser\My store and also its private keys. What result does it give for your certificate? Encryption/Signature passed? – pepo Feb 28 '18 at 11:18
  • For my certificate there is an error. Unfortunately it's in german but it translates to something like "certificate public key does not match stored keyset". What is the stored keyset in this case? – Frank Feb 28 '18 at 11:28
  • Are you on Windows 10? (Brainpool curves weren't supported in earlier versions). Does your certificate use named curve parameters, or explicit curve parameters? (Only named will work) – bartonjs Feb 28 '18 at 14:17
  • It means that the public key from certificate does not match public key derived from your private key. So you have not generated the certificate correctly. – pepo Feb 28 '18 at 14:18
  • Yes, I'm on Windows 10. I think the problem is that I create the csr using bouncycastle: `publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(ecPublicKeyParameters);` I try to figure this one out (how to do it with .NET) and hope it helps with my original problem. Thanks a lot for getting me on the (hopefully) right track!!! – Frank Feb 28 '18 at 14:52
  • @Frank if you try to test the concept of authenticating with SSL client certificate then you could make the cert by using [XCA](https://sourceforge.net/projects/xca/). If that succeeds then you know that the problem is with your certificate generation. – pepo Mar 01 '18 at 07:36
  • @Frank [One of my older answers](https://stackoverflow.com/questions/22230745/generate-self-signed-certificate-on-the-fly/22237794#22237794) could help you with generating certificates although it uses RSA keys. – pepo Mar 01 '18 at 07:38
  • @pepo Thanks a lot for your help. I already managed to do things with RSA which is much simpler. As this discussion leaves the original topic I'm going to post a new question. Perhaps you'll like to contribute there, too? – Frank Mar 01 '18 at 07:49

1 Answers1

1

As usual when working with microsoft APIs the error has nothing to do with the real reason... :-(

The cause of all this was a wrong csr that made the received certificate have valid ASN1 structure (verified with http://lapo.it/asn1js/) with invalid content. How to do it right can be found in the answer to this SO question.

User pepo helped me a lot to get on the right track (Thank you once again!). His comment suggesting to use certutil -user -store my instead of looking at the MMC console was the hint I needed to sort this out.

Frank
  • 2,036
  • 1
  • 20
  • 32