qq_37512015 2023-12-26 14:59 采纳率: 100%
浏览 17
已结题

使用der证书时,SignalR客户端和服务器端无法建立https通信

在C# .NET Core中,我使用SignalR创建了客户端和服务器端,想使用https通信,因此会使用到SSL证书,我遇到的问题是,如果使用pfx格式的证书,那么客户端和服务器端可以正常连接,如果使用der格式证书+Pem加密格式的私钥,客户端就无法连接到服务器,我的代码如下:
Server端Program.cs中

using SignalRService.Common;
using SignalRService.Hubs;
using SignalRService.Providers;
using SignalRService.Services;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseWindowsService(options =>
{
    options.ServiceName = "SignalR Server Service";
});
builder.WebHost.UseKestrel(options =>
{
    options.ListenAnyIP(12316, config =>
    {
        var certificateMgr = new CertificateMgr();
        var certificate = certificateMgr.GetCertificate();
        if (certificate != null)
        {
            config.UseHttps(certificate);
        }
    });
});
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: "AllowAll", builder =>
        builder.AllowAnyOrigin()
               .AllowAnyMethod()
               .AllowAnyHeader());
});
builder.Services.AddSignalR(options =>
{
    options.EnableDetailedErrors = true;
});
builder.Services
    .AddSingleton<IUserIdProvider, UserIdProvider>()
    .AddHostedService<ServerService>();

var app = builder.Build();
app.UseRouting();
app.UseCors("AllowAll");
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ProxyHub>("proxyHub").RequireCors("AllowAll");
});
await app.RunAsync();
CertificateMgr.cs文件中解析证书和私钥

```c#
namespace SignalRService.Common
{
    public class PasswordFinder : IPasswordFinder
    {
        private string password;
        public PasswordFinder(string pwd) => password = pwd;
        public char[] GetPassword() => password.ToCharArray();
    }
    public class CertificateMgr
    {
        public string _certPath = "C:\\Working Folder\\certificateFile.der";
        public string _keyPath = "C:\\Working Folder\\privateKey.pem";
        public X509Certificate2? GetCertificate()
        {
            string fileExtension = Path.GetExtension(_certPath);
            if (string.Equals(fileExtension, ".pfx", StringComparison.OrdinalIgnoreCase))
            {
                return new X509Certificate2(_certPath, "123123");
            }
            else
            {
                StreamReader sr = new StreamReader(_keyPath);
                string privateKeyPass = "123123";
                var pf = new PasswordFinder(privateKeyPass);
                PemReader pr = new PemReader(sr, pf);
                AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
                RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
                using (RSA rsa = RSA.Create())
                {
                    rsa.ImportParameters(rsaParameters);
                    X509Certificate2 certificate = new X509Certificate2(_certPath);
                    certificate = certificate.CopyWithPrivateKey(rsa);
                    return certificate;
                }
            }
        }
    }
}

在客户端的Client.cs文件中建立连接

using SignalRClient.Common;
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Data.SqlTypes;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace SignalRClient.Connections
{
    public interface IProxyClient
    {
        Task Initialize();
        Task HubConnection_StartAsync();
        Task HubConnection_StopAsync();
    }
    public class SignalRClient : IProxyClient
    {
        private HubConnection? _hubConnection;
        public Task Initialize()
        {
            Console.WriteLine($"Initialize");
            return Task.CompletedTask;
        }
        public async Task HubConnection_StartAsync()
        {
            _hubConnection = new HubConnectionBuilder()
                .WithUrl("https:10.224.10.10:12316/proxyHub", (options) =>
                {
                    options.HttpMessageHandlerFactory = (handler) =>
                    {
                        if (handler is HttpClientHandler clientHandler)
                        {
                            clientHandler.ServerCertificateCustomValidationCallback += CheckCertificateCallback;
                        }
                        return handler;
                    };
                })
                .WithAutomaticReconnect(new RandomRetryPolicy())
                .Build();
            _hubConnection.Closed += HubConnection_Closed;
            _hubConnection.Reconnecting += HubConnection_Reconnecting;
            _hubConnection.Reconnected += HubConnection_Reconnected;
            _hubConnection.On<string>("MessageToClient", HubConnection_Received);
            try
            {
                await _hubConnection.StartAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private bool CheckCertificateCallback(HttpRequestMessage message, X509Certificate2? certificate, X509Chain? chain, SslPolicyErrors errors)
        {
            return true;
        }

        public async Task HubConnection_StopAsync()
        {
            if (_hubConnection != null)
            {
                await _hubConnection.StopAsync();
                _hubConnection = null;
            }
        }
        public async Task SendMessageToServer(string message)
        {
            if (_hubConnection != null
                && _hubConnection.State == HubConnectionState.Connected)
            {
                await _hubConnection.InvokeAsync("MessageFromClient", message);
            }
        }
        private Task HubConnection_Closed(Exception? exception)
        {
            Console.WriteLine($"Closed {exception?.Message}");
            return Task.CompletedTask;
        }
        private Task HubConnection_Reconnecting(Exception? exception)
        {
            Console.WriteLine($"Reconnecting {exception?.Message}");
            return Task.CompletedTask;
        }
        private Task HubConnection_Reconnected(string? arg)
        {
            Console.WriteLine($"{arg}");
            return Task.CompletedTask;
        }
        private void HubConnection_Received(string message)
        {
            DateTime dateTime = DateTime.UtcNow;
            Console.WriteLine($"Received {message}: " + dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff"));
        }
    }
}

在运行的时候,如果使用pfx格式的证书,那么客户端可以正常连接到服务器端,如果把pfx格式证书转换为der格式的证书和pem格式的加密私钥,那么客户端就无法建立连接,在运行到StartAsync函数后报下面错误,想请教原因

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.IO.IOException:  Received an unexpected EOF or 0 bytes from the transport stream.
   at System.Net.Security.SslStream.<FillHandshakeBufferAsync>g__InternalFillHandshakeBufferAsync|189_0[TIOAdapter](TIOAdapter adap, ValueTask`1 task, Int32 minSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(HttpRequestMessage request)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.Internal.AccessTokenHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.Internal.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport(TransferFormat transferFormat, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsyncCore(TransferFormat transferFormat, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsync(TransferFormat transferFormat, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)


我的私钥格式如下

    -----BEGIN RSA PRIVATE KEY-----
    Proc-Type: 4,ENCRYPTED
    DEK-Info: AES-256-CBC,5BF69C9B6974DB708CE3C6A133642F05

    keh2HGnXDyAO4rLB67aT5meMVMlOjf3/me5JwfixMk3FV9WI5GOb3w+weX+J7WRL
    GDvFvHGWezYHNQi1OfiKZQbYQi5POuir2LplhLCuDHPae/8HVhoUXcyH6n4VIE1N
    R/DtqUNHw3sAP5vbdllHDSlsDm5kLrdOh2KsFtKthyDHP6iMTj6OZZ3EuskX3gxy
    fUHNlA9boU0nDaQcDJqV0FPx8p29/vKkHrcc4LFh0JpEUh/gEd5p9dHeAq1HoyO4
    D+BpBBNnugCm5ZcrEBUNA2qqqWgXNL3XVfiLJ05s+VUsfUilNssoPyi5dW7fA5fd
    uX7NE7rG5jnJ8EX7jvLwcfeG4aQE3hRQwLsXSMDGek0Hnlj3qKYU3LpAXH+XguvV
    TRJNK+SN3jrdR6ZdICHQAdT9q6uo/wlffjDLn+h7vQWtYYzdlomEY/Pl7ErV8Rq6
    P7vr91hLK9/VluenSvcrQhI+a2ZzrFlkxgWfPV5QzC1IaZSW50PoXwp2HcYtvt5+
    mdvZx+0nwrF5TxlcKk4zTyxTpe6pweWFvq+8EGfIz+pIPm3ayO9VKvTvQ1xR627j
    cvOmycCUQYrBpIld9uurn3o/Lp+Wjcdoqk7kih1cwq5Xbcnv/P+yeiSXmNYUSfaY
    t+BlsDzcwWDf3GWL+tjxdukiR60PmAji9oSBMNmzrvluV/LIuSV/7abtAGCYIawz
    xJkSrmLbfLYH2i6Xij8AlkQm7wT3MbEHVlDOeXi2DXAokdrFBc1/0HeawciowAYz
    tVp8EdQWUtvkAArQbS82rUU2ST2uzL/OfDPLCXBrbdVL1j8+jCMBwwUSxmqPQR3M
    8y3lKR+NBlubu/Y54k3aLSk7Wvyp6y7G5qHpTT4x2uHpibJD3G7qU/wUVKd7NT4R
    X8aqWC/ceCdlSQoBR2hUDIbmXxrDvkQyKLR8VcYCnhjE6bAaCf3tmlJ/fGwI8nqN
    gwF822IKaXDGtyhve2dcB60GoSXZLDKynAU4QpYO59etwgskucEEf2Edgda/SoGn
    vWwGMNHFoZp290W9MgV+Q+bAfKuurT0LurOK06UfCPK3B2rOGsuaXAmfX37avNZL
    zs7CfL+aaIRaEdSBDUDxLulIrYGIIBdSM/sh7RUR+lx4R11Y0qeMEf6nORygj1jG
    bpMUdGstHYKF2ChM2KrTAng/ly1xqaKvvWFRUXSuFvZGjYDYOVtszPBNJ+XedZ5h
    JrOhYb9FhoWX+LnGjUtQmZCUZjzhXt8IXydnNvRb5pf2DPWtEmbm//X51vRkyTkh
    MhxA6VgFXav9Q2C1qSDWivFRznUOiN446/qH44EpAPKHJWMlu9EJwVk78/Rp0Ohj
    8YBzYmOTbBthkGk2SOAmayHJX6sxjObEh/AIuHiXOVLRJTqdkt7MtlG6mtzYwquI
    LJsdTbhqYTzyMw1KjhUNpvGXaKfLgH5SPKsNtVJs75Cu0T1Je9IHPV8BL2kJQssq
    z2hlIN//D8bMHu78x36P9MbNBIbeCzVa9JerFESIuauyhxk42/sQqWnzSHkV9zsN
    49TkuyseyCn46kM+B+f56ak7CznM+DXlLi2aJUr04WWOTUWXWuInj+LuFJ+vD930
    -----END RSA PRIVATE KEY-----

我使用下面方法,客户端也不能实现SignalR连接

                var privateKey = File.ReadAllText(_keyPath);
                byte[] privateKeyBytes = Convert.FromBase64String(privateKey);
                privateKey = Encoding.UTF8.GetString(privateKeyBytes);
                rsa.ImportEncryptedPkcs8PrivateKey(
                    Encoding.UTF8.GetBytes(_certPassword),
                    Encoding.UTF8.GetBytes(privateKey), out _);
                X509Certificate2 certificate = new X509Certificate2(_certPath);
                certificate = certificate.CopyWithPrivateKey(rsa);
                return certificate;

  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 1月3日
    • 修改了问题 12月26日
    • 创建了问题 12月26日

    悬赏问题

    • ¥15 PointNet++的onnx模型只能使用一次
    • ¥20 西南科技大学数字信号处理
    • ¥15 有两个非常“自以为是”烦人的问题急期待大家解决!
    • ¥30 STM32 INMP441无法读取数据
    • ¥15 R语言绘制密度图,一个密度曲线内fill不同颜色如何实现
    • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
    • ¥15 用visualstudio2022创建vue项目后无法启动
    • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
    • ¥500 把面具戴到人脸上,请大家贡献智慧,别用大模型回答,大模型的答案没啥用
    • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。