服务器端代码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();
SignalR 服务器端的CertificateMgr.cs
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格式的证书,客户端可以执行到CheckCertificate回调函数中,客户端就可以连接到服务器端了;如果服务端使用der证书和pem私钥,则只有服务端能够成功运行,客户端无法执行CheckVerifieCallback函数。运行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)