将gRPC与通过IP地址连接的证书一起使用时,为什么会出现错误?

时间:2020-11-12 14:56:07

标签: .net ssl grpc

我正在使用.Net(C#)gRPC构建示例客户端-服务器测试应用程序。 我正在使用DiDiSoft库在代码中生成SSL证书。 证书是使用“服务器”计算机名称生成的。

使用机器名连接服务器和客户端应用程序时,握手可以正常工作。 如果我使用IP地址,则不会。 在我的阅读中,不建议使用IPAddresses生成证书。 使用SSL证书设置gRPC连接时,我是否只能使用计算机名称?

设置证书的示例代码:

var serverKeypair = rsa.GenerateRsaKeyPair(KeyLength.Length4096);
serverKeypair.Private.Save(Path.Combine(certsPath, "server.key"));
X509Name serverCertificateProperties = new X509Name()
{
    C = "US",
    ST = "VA",
    O = "SomeName",
    OU = "gRPCAPI",
    CN = "MACHINE-NAME",
};
var serverCert = Certificate.CreateCertificate(serverKeypair.Public, 
                        newca.CACertificate.SubjectPublicKey, 
                        newca.CAPrivateKey, 
                        serverCertificateProperties, 
                        newca.CAProperties, 
                        null, 
                        "01", 
                        newca.CACertificate.SerialNumber, 
                        true, 
                        new[] { KeyUsages.DigitalSignature }, 
                        DateTime.Now, DateTime.Now.AddYears(1));
serverCert.Save(Path.Combine(certsPath, "server.crt"));

用于启动gRPC服务器的代码段:

// "Server" App
ServerServiceDefinition serverDef = new ServerDefinitionFoo();
var sslCredentials = GetCerts();
var server = new Server()
{
    Services = { serverDef },
    Ports = { new ServerPort("192.168.1.", port, sslCredentials) }
};

server.Start();

用于设置gRPC客户端的代码段:

// Client App
static SslCredentials GetSslCredentials()
{
    var certsFolder = Path.Combine(Environment.CurrentDirectory, "Certs");
    var cacert = File.ReadAllText(Path.Combine(certsFolder, "newca.crt"));
    var cert = File.ReadAllText(Path.Combine(certsFolder, "client.crt"));
    var key = File.ReadAllText(Path.Combine(certsFolder, "client.key"));

    var keyPair = new KeyCertificatePair(cert, key);
    var Creds = new SslCredentials(cacert, keyPair);
    return Creds;
}

var sslCredentials = GetSslCredentials();
var channel = new Channel("MACHINE-NAME:30052", sslCredentials);
var apiProvider = new UtilitiesApi(new UtilitiesService.UtilitiesServiceClient(channel));
var retVal = apiProvider.SomeServiceCall("Test Data");

以上情况成功完成。

如果我更改以下代码段:

var channel = new Channel("192.168.1.9:30052", sslCredentials);

我在客户端上遇到以下异常:

Grpc.Core.RpcException:Status(StatusCode =“ Unavailable”, Detail =“无法连接到所有地址”, DebugException =“ Grpc.Core.Internal.CoreErrorDetailException: {“创建”:“ @ 1605192569.522000000”,“描述”:“无法选择 子频道”,“文件”

我在服务器控制台上(通过日志记录)收到以下错误消息。

E1112 09:49:29.510018 0 T:\ src \ github \ grpc \ workspace_csharp_ext_windows_x64 \ src \ core \ tsi \ ssl_transport_security.cc:1807: 找不到与服务器名称192.168.1.9匹配的内容。

启动客户端-服务器握手时,我应该能够使用IP地址吗?

谢谢JohnB

1 个答案:

答案 0 :(得分:0)

这按预期工作,并且您的证书配置不正确。您遇到的问题很简单,就像您尝试创建与服务器的TLS连接一样,并且该服务器的名称与客户端期望的名称不同。因此,TLS连接失败-这是为了避免中间人攻击。

每当客户端想要创建TLS连接时,客户端都会使用URL中使用的名称(在您的情况下为计算机名称或IP地址),并对照服务器提供的证书中的规范名称进行检查。如果它们不匹配,则假定客户端正在与假服务器对话,并且连接被拒绝。

想象一下您想与服务器foo.com进行通信的情况,但是在连接之后,服务器将无法出示证书,证明它是真正的foo.com后端(基于您提供的信任根)。当然,正确的措施是停止连接,而不是开始将潜在的敏感数据(例如身份验证信息)发送到错误的服务器。