.net core X509Certificates2 with Fleck WebSocket -> 身份验证失败 -> 处理证书时发生未知错误

.net core X509Certificates2 with Fleck WebSocket -> Authentication failed -> An unknown error occurred while processing the certificate

我们有一个较旧的 .net 核心项目,它使用 HttpListener 和 Fleck,但没有 ssl。我们现在需要 ssl,所以我开始更新所有内容。一切都适用于网络服务器,但是当我尝试将 websocket 与 ssl 一起使用时,出现以下异常:

Failed to Authenticate System.AggregateException: One or more errors occurred. (Authentication failed, see inner exception.) ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. ---> System.ComponentModel.Win32Exception (0x80090327): An unknown error occurred while processing the certificate. --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm) at System.Threading.Tasks.TaskToApm.End(IAsyncResult asyncResult) at System.Net.Security.SslStream.EndAuthenticateAsServer(IAsyncResult asyncResult) at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization) --- End of inner exception stack trace ---

我使用 Portable.BouncyCastle 1.8.10 生成这样的证书(这是我尝试生成证书的第三个代码):

using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;


namespace WebServer
{
    public static class CertificateHelper
    {
        private const string DefaultName = "X509Cert2.PFX";

        // Source: Přemysl Šťastný 
        public static X509Certificate2 GenerateCertificate(string subjectName = "CN=root ca", int keyStrength = 4096)
        {
            // Generating Random Numbers
            var randomGenerator = new CryptoApiRandomGenerator();
            var random = new SecureRandom(randomGenerator);

            // The Certificate Generator
            var certificateGenerator = new X509V3CertificateGenerator();

            // Serial Number
            var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
            certificateGenerator.SetSerialNumber(serialNumber);

            // Signature Algorithm
            const string signatureAlgorithm = "SHA256WithRSA";

            // Issuer and Subject Name
            var subjectDN = new X509Name(subjectName);
            var issuerDN = subjectDN;
            certificateGenerator.SetIssuerDN(issuerDN);
            certificateGenerator.SetSubjectDN(subjectDN);

            // Valid For
            var notBefore = DateTime.UtcNow.Date;
            var notAfter = notBefore.AddYears(20);

            certificateGenerator.SetNotBefore(notBefore);
            certificateGenerator.SetNotAfter(notAfter);

            // Subject Public Key
            AsymmetricCipherKeyPair subjectKeyPair;
            var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
            var keyPairGenerator = new RsaKeyPairGenerator();
            keyPairGenerator.Init(keyGenerationParameters);
            subjectKeyPair = keyPairGenerator.GenerateKeyPair();

            certificateGenerator.SetPublicKey(subjectKeyPair.Public);

            // Generating the Certificate
            var issuerKeyPair = subjectKeyPair;

            ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random);

            // selfsign certificate
            var certificate = certificateGenerator.Generate(signatureFactory);

            // in-memory PFX stream
            var pkcs12Store = new Pkcs12Store();
            var certEntry = new X509CertificateEntry(certificate);
            pkcs12Store.SetCertificateEntry(subjectName, certEntry);
            pkcs12Store.SetKeyEntry(subjectName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] {certEntry});
            X509Certificate2 keyedCert;
            using (MemoryStream pfxStream = new MemoryStream())
            {
                pkcs12Store.Save(pfxStream, new char[0], new SecureRandom());
                pfxStream.Seek(0, SeekOrigin.Begin);
                keyedCert = new X509Certificate2(pfxStream.ToArray(), string.Empty, X509KeyStorageFlags.Exportable);
            }

            return keyedCert;
        }

        public static void Save(this X509Certificate2 cert, string outputFileName)
        {
            var bytes = cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12);
            File.WriteAllBytes(outputFileName, bytes);
        }

        public static X509Certificate2 SaveNewX509Certificate2(string fileName = DefaultName)
        {
            var cert = GenerateCertificate();
            cert.Save(DefaultName);
            return cert;
        }

        public static string GetInstallX509Certificate2(string fileName = DefaultName)
        {
            if (!File.Exists(fileName))
            {
                var cert = SaveNewX509Certificate2(fileName);
                X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
                store.Open(OpenFlags.ReadWrite);
                if (!store.Certificates.Contains(cert))
                {
                    store.Add(cert);
                }

                store.Close();
                return fileName;
            }
            else
            {
                return fileName;
            }
        }
    }
}

CertificateHelper.GetInstallX509Certificate2() 方法是我最后 运行

然后我像这样启动 Fleck 1.2.0 websocket(SSLCertificatePath 是一个包含文件路径的字符串):

var server = new WebSocketServer(wsLocation)
{
    SupportedSubProtocols = webSocketHandlers.Keys
};
if (SSLCertificatePath != null)
{
    server.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12;
    server.Certificate = new X509Certificate2(SSLCertificatePath);
}
server.Start(...);

服务器启动正常,但一旦我尝试连接到 websocket,就会抛出上述异常。 老实说,我不知道我在做什么,以前从未使用过证书。

没有真正找到问题的解决方案,但我通过将 websocket 支持集成到我们的 httplistener 而不是使用库来解决它。 http 侦听器已经在使用 ssl,因此 websockets 自动工作。