带有 WinHttpHander() 的 gRPC "ArgumentException: Illegal characters in path."

gRPC with WinHttpHander() "ArgumentException: Illegal characters in path."

我正在尝试使用 .NET 5.0 服务器和 .NET Framework 客户端启动 HTTP2 服务器端流并 运行。使用框架客户端时,我必须根据 Microsoft 文档 (https://docs.microsoft.com/en-us/aspnet/core/grpc/netstandard?view=aspnetcore-5.0) 使用 WinHttpHander。 将 WinHttpHandler 与 X509Certificate 一起使用时,我总是会遇到相同的错误。该错误似乎仅在使用证书时才会出现,而使用 gRPC 的 Http2 似乎需要证书。

ArgumentException: Illegal characters in path.

我尝试了几种添加证书的方法,使用 X509Certificate.Import、X509Certificate.CreateFromCertFile、X509Certificate.CreateFromSignedFile 和 X509Certificate.Add,它们都给我同样的错误。

客户端连接是通过以下过程建立的(代码是 F#)

let cacert = File.ReadAllText(@"ca.crt");
let clientCert = File.ReadAllText(@"client.crt");
let clientkey = File.ReadAllText(@"client.key");

let X509Cert = X509Certificate.CreateFromSignedFile(clientCert)
let handler = new WinHttpHandler()  

handler.ClientCertificates.Add(X509Cert) |> ignore
handler.ServerCertificateValidationCallback <- fun msg clientcert cacert e -> true
let httpclient = new HttpClient(handler)
let channelOptions = GrpcChannelOptions(HttpClient = httpclient)

let channel = GrpcChannel.ForAddress("https://127.0.0.1:5001",channelOptions)
let client = DeFactoGrpc.EventSubscriberService.EventSubscriberServiceClient(channel)

如果需要,服务器按以下程序制作(代码为 F#)

let cacert = File.ReadAllText(@"ca.crt");
let servercert = File.ReadAllText(@"server.crt");
let serverkey = File.ReadAllText(@"server.key");

let certificatePair = new KeyCertificatePair(servercert, serverkey);
        
let certList = new System.Collections.Generic.List<KeyCertificatePair>()
certList.Add(certificatePair)
        
let server = new Server()
server.Services.Add(EventSubscriberService.EventSubscriberServiceMethodBinder.BindService(new EventSubscriber()))
server.Ports.Add(new ServerPort("localhost", 5001,SslServerCredentials(certList,cacert,false))) 
|> ignore

您似乎将错误的值传递给 X509Certificate.CreateFromSignedFile(String)。此静态方法采用表示

的字符串

The path of the signed file from which to create the X.509 certificate.

但是,您正在传递文件的内容client.crt:

let clientCert = File.ReadAllText(@"client.crt"); // Here clientCert contains the contents of client.crt
let X509Cert = X509Certificate.CreateFromSignedFile(clientCert)

除非文件 client.crt 包含包含实际签名文件的文件的路径,否则这种用法是不正确的,并且是 ArgumentException 的原因:路径中的非法字符.相反,您应该只传递文件名:

let X509Cert = X509Certificate.CreateFromSignedFile(@"client.crt")

为了比较,constructor for SslServerCredentials 将表示

的字符串作为其第二个参数

PEM encoded client root certificates used to authenticate client.

所以您传入 ca.crt 内容的服务器端代码看起来是正确的:

let cacert = File.ReadAllText(@"ca.crt"); // Here cacert contains the contents of ca.crt
server.Ports.Add(new ServerPort("localhost", 5001, SslServerCredentials(certList,cacert,false))) 

而在客户端,您对 WinHttpHandler.ServerCertificate 的回调只是 returns true。根据文档,

Definition

Gets or sets a callback method to validate the server certificate. This callback is part of the SSL handshake.

member this.ServerCertificateValidationCallback : 
Func<System.Net.Http.HttpRequestMessage, 
System.Security.Cryptography.X509Certificates.X509Certificate2, 
System.Security.Cryptography.X509Certificates.X509Chain, 
System.Net.Security.SslPolicyErrors, bool> with get, set

Property Value

The callback should return true if the server certificate is considered valid and the request should be sent. Otherwise, return false.

Remarks

The default value is null. If this property is null, the server certificate is validated using standard well-known certificate authorities.

看来您仍然需要实现此方法(或将其保留为空)。

(您根本没有展示如何使用 clientkey。)