无法连接到此链接中提到的greeter grpc服务-来自使用grpc.core库(Grpc.Core.2.24.0
和{{1}从.net框架应用编写的greeter客户端的https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.0 }。
下面是我的客户代码。它适用于非SSL,但不适用于SSL
具有非SSL的客户端代码(有效)
Grpc.Core.Api.2.24.0
具有SSL的客户端代码(无法连接)
var channel = new Channel("localhost:5000", ChannelCredentials.Insecure);
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
channel.ShutdownAsync().Wait();
以下是我收到的SSL错误
SslCredentials secureChannel = new SslCredentials();
var channel = new Channel("localhost", 5001, secureChannel);
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
channel.ShutdownAsync().Wait();
我尝试使用在同一链接(https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.0)中提到的.net核心应用程序客户端,该客户端可与SSL和非SSL一起使用,但不能直接使用grp库。我的客户端是.Net框架客户端,这就是我无法使用.net库连接到grpc服务的原因。 .net grpc库仅受.net核心应用程序支持。
Grpc.Core.RpcException: 'Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")'
预期结果-服务器的响应
实际结果-SslCredentials secureChannel = new SslCredentials();
var channel = new Channel("localhost", 5001, secureChannel);
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
channel.ShutdownAsync().Wait();
答案 0 :(得分:6)
我在.NET Framework c上创建了一个工作客户端,并在本地主机上的.NET Core上安装了服务器:
static async Task Main(string[] args)
{
string s = GetRootCertificates();
var channel_creds = new SslCredentials(s);
var channel = new Channel("localhost",50051, channel_creds);
var client = new Informer.InformerClient(channel);
await GetPing(client);
}
public static string GetRootCertificates()
{
StringBuilder builder = new StringBuilder();
X509Store store = new X509Store(StoreName.Root);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
builder.AppendLine(
"# Issuer: " + mCert.Issuer.ToString() + "\n" +
"# Subject: " + mCert.Subject.ToString() + "\n" +
"# Label: " + mCert.FriendlyName.ToString() + "\n" +
"# Serial: " + mCert.SerialNumber.ToString() + "\n" +
"# SHA1 Fingerprint: " + mCert.GetCertHashString().ToString() + "\n" +
ExportToPEM(mCert) + "\n");
}
return builder.ToString();
}
/// <summary>
/// Export a certificate to a PEM format string
/// </summary>
/// <param name="cert">The certificate to export</param>
/// <returns>A PEM encoded string</returns>
public static string ExportToPEM(X509Certificate cert)
{
StringBuilder builder = new StringBuilder();
builder.AppendLine("-----BEGIN CERTIFICATE-----");
builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine("-----END CERTIFICATE-----");
return builder.ToString();
}
private static async Task GetPing(Informer.InformerClient client)
{
Console.WriteLine("Getting ping...");
try
{
Metadata headers = null;
var response = await client.GetServerPingAsync(new Empty(), headers);
string result = "Nan";
if (response.PingResponse_ == 1)
result = "Ok!";
Console.WriteLine($"Ping say: {result }");
}
catch (Exception ex)
{
Console.WriteLine("Error get server ping." + Environment.NewLine + ex.ToString());
}
}
但是我尚未在远程计算机上成功完成此工作(例如,其中ip 192.168.1.7是服务器地址,而客户端地址是192.168.1.2)
答案 1 :(得分:1)
我通过在客户端中使用pem格式的服务器证书来使用SSL端口。
SslCredentials secureCredentials = new SslCredentials(File.ReadAllText("certificate.pem"));
var channel = new Channel("localhost", 5001, secureCredentials);
这意味着VS 2019中的Asp.NETCore模板使用开发证书
与%AppData%\ASP.NET\Https\ProjectName.pfx
上的pfx文件一起使用
密码= %AppData%\Microsoft\UserSecrets\{UserSecretsId}\secrets.json {:Kestrel:Certificates:Development:Password} Value
您可以从UserSecretsId
获取ProjectName.csproj
的ID。对于每个ASP.NET Core项目,这都是不同的。
我使用以下命令将pfx密码组合转换为pem
openssl pkcs12 -in "<DiskLocationOfPfx>\ProjectName.pfx" -out "TargetLocation\certifcate.pem" -clcerts
这将提示您输入pfx密码。使用以上secrets.json
中的密码。
提供一些密码来生成certificate.pem
(至少4个字母)。
复制此cerificate.pem
,以供gRPC .NET Framework客户端访问并在其中使用
SslCredentials secureCredentials = new SslCredentials(File.ReadAllText("certificate.pem"))
用于生产方案
使用来自证书签名机构的有效证书,并在ASP.NET Core Server中使用pfx,在.NET Framework客户端中使用pem。
答案 2 :(得分:1)
我没有在客户端保存 pem 就让它工作了(如果客户端和服务器在不同的机器上,则事件发生)。
首先,目标/主机名(用于创建通道的名称)必须与服务器证书中的 CN(通用名称)相匹配,这一点非常重要,这里的棘手部分是 区分大小写!
e.q: 证书的 CN 是 SV-XXX-DEV-01 并且您指定 sv-xxx-dev-01 这将不起作用并且您得到以下错误:
Grpc.Core.RpcException: 'Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")'
所以这是我的解决方案(当然这可以优化,不应该在一个类中(关注点分离),但更容易理解这一点。
public static async Task Main(string[] args)
{
await FullFrameworkSample();
}
private static async Task FullFrameworkSample()
{
Uri host = new Uri("https://sv-xxx-dev-cpu-01:44301");
int port = host.Port;
(string publicKeyInPemFormat, string commonName) = await GetCertificateInformationFromServer(host);
//note: in the full framework implementation it's very important that the casing of the target is correct (the same casing as in the CN name of the certificate)
string target = $"{commonName}:{port}";
//note: thats only needed in our case, because we have a server side interceptor, that checks if the secureKey is valid.
CallCredentials credentials = CallCredentials.FromInterceptor((context, metadata) =>
{
metadata.Add("SecurityTokenId", "someSecureKey");
return Task.CompletedTask;
});
ChannelCredentials channelCredentials = ChannelCredentials.Create(new SslCredentials(publicKeyInPemFormat), credentials);
Channel channel = new Channel(target, channelCredentials);
ProjectInlayDataService.ProjectInlayDataServiceClient client = new ProjectInlayDataService.ProjectInlayDataServiceClient(channel);
GetProjectInlayDataResponse result = await client.GetProjectInlayDataAsync(new GetProjectInlayDataRequest());
await channel.ShutdownAsync();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static async Task<(string PublicKeyInPemFormat, string CommonName)> GetCertificateInformationFromServer(Uri host)
{
Regex commonNameRegex = new Regex("CN=([\\w\\-.]*),?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
StringBuilder builder = new StringBuilder();
const string newline = "\n";
X509Certificate certFromServer;
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage _ = await client.GetAsync(host))
{
//get the certificate from the server, so we don't need to store the pem.
certFromServer = ServicePointManager.FindServicePoint(host).Certificate;
if (certFromServer == null)
throw new InvalidOperationException($"Could not get certificate from server ({host}).");
}
}
Match match = commonNameRegex.Match(certFromServer.Subject);
if (!match.Success)
throw new InvalidOperationException($"Could not extract CN (Common Name) from server certificate ({certFromServer.Subject}).");
string commonName = match.Groups[1].Captures[0].Value;
X509Certificate2 certificate = new X509Certificate2(certFromServer);
string pem = ExportToPem(certificate);
builder.AppendLine(
"# Issuer: " + certificate.Issuer + newline +
"# Subject: " + certificate.Subject + newline +
"# Label: " + certificate.FriendlyName + newline +
"# Serial: " + certificate.SerialNumber + newline +
"# SHA1 Fingerprint: " + certificate.GetCertHashString() + newline +
pem + newline);
return (builder.ToString(), commonName);
}
/// <summary>
/// Export a certificate to a PEM format string
/// </summary>
/// <param name="cert">The certificate to export</param>
/// <returns>A PEM encoded string</returns>
private static string ExportToPem(X509Certificate cert)
{
StringBuilder builder = new StringBuilder();
builder.AppendLine("-----BEGIN CERTIFICATE-----");
builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine("-----END CERTIFICATE-----");
return builder.ToString();
}
}