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

Time:11-11

I am trying to get a HTTP2 serverside stream up and running using an .NET 5.0 server, and a .NET Framework client. When using a framework client i have to use the WinHttpHander according to Microsofts Documentation(https://docs.microsoft.com/en-us/aspnet/core/grpc/netstandard?view=aspnetcore-5.0). When using the WinHttpHandler with a X509Certificate i always get the same error. The error seems to come only when using a certificate, which seems to be needed for Http2 with gRPC.

ArgumentException: Illegal characters in path.

I have tried several ways to add the certificate, using X509Certificate.Import, X509Certificate.CreateFromCertFile, X509Certificate.CreateFromSignedFile, and X509Certificate.Add, and they all give me the same error.

The client connection is made with following procedure (Code is 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)

If needed the server is made with following procedure (Code is 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

CodePudding user response:

It looks as though you are passing the wrong value for the to X509Certificate.CreateFromSignedFile(String). This static method takes a string that represents

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

However, you are passing the contents of the file client.crt:

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

Unless the file client.crt contains the path to a file that contains the actual signed file, this usage is incorrect and is the cause of your ArgumentException: Illegal characters in path. Instead you should just pass the filename:

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

For comparison, the the constructor for SslServerCredentials takes as its 2nd argument a string that represents

PEM encoded client root certificates used to authenticate client.

So your server-side code which passes in the contents of ca.crt looks correct:

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))) 

While on the client side, your callback for WinHttpHandler.ServerCertificate simply returns true. According to the docs,

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.

So it appears you still need to implement this method (or leave it null).

(You don't show how you use clientkey at all.)

  • Related