带有 WinHttpHander() 的 gRPC “ArgumentException:路径中有非法字符。”

Posted

技术标签:

【中文标题】带有 WinHttpHander() 的 gRPC “ArgumentException:路径中有非法字符。”【英文标题】:gRPC with WinHttpHander() "ArgumentException: Illegal characters in path." 【发布时间】:2021-11-09 09:46:13 【问题描述】:

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

ArgumentException:路径中有非法字符。

我尝试了几种添加证书的方法,使用 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

【问题讨论】:

【参考方案1】:

您似乎将错误的值传递给X509Certificate.CreateFromSignedFile(String)。这个静态方法接受一个字符串,表示

签名文件的路径,从中创建 X.509 证书。

但是,您传递的是文件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 编码的客户端根证书用于验证客户端。

因此,您传入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 的回调只返回true。根据文档,

定义

获取或设置一个回调方法来验证服务器证书。此回调是 SSL 握手的一部分。

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

资产价值

如果服务器证书被认为有效并且应该发送请求,回调应该返回true。否则,返回false

备注

默认值为null。如果此属性为 null,则使用标准知名证书颁发机构验证服务器证书。

所以看来你仍然需要实现这个方法(或者让它为空)。

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

【讨论】:

以上是关于带有 WinHttpHander() 的 gRPC “ArgumentException:路径中有非法字符。”的主要内容,如果未能解决你的问题,请参考以下文章

带有 GRPC 的 Angular 通用

带有REST和Open API的gRPC

部署 Rest + gRPC 服务器部署到带有入口的 k8s

无法在带有 VS2017 的 Windows 10 上安装和使用 gRPC C/C++

如何创建符合 GRPC 的 OkHttpClient?

gRPC 间歇性地具有高延迟