簡體   English   中英

SSL gRPC 客戶端在 C# 中運行良好,但在 C++ 中出現不可用的“空更新”失敗

[英]SSL gRPC client works fine in C#, but fails with UNAVAILABLE "Empty update" in C++

在帶有 VS2022 17.1.2 和 .NET 6 的 Windows 10 Pro 21H2 上,我將一個簡單的 C# gRPC 客戶端移植到 C++,但是 C++ 客戶端總是無法連接到服務器,盡管我的代碼似乎沒有用完同樣的想法為什么。

我的 gRPC 服務器使用 SSL 和 LetsEncrypt 生成的證書(通過 LettuceEncrypt),因此我使用默認SslCredentials

在 C#(帶有Grpc.Core )中,我使用 gRPC 如下:

// Channel and client creation
var channel = new Channel("my.domain.org:12345", new SslCredentials());
var client = new MyGrpc.MyGrpcClient(channel);
// Sample call
LoginUserReply reply = client.LoginUser(new LoginUserRequest()
{
    Username = username
});

我按如下方式將其轉換為 C++,主要基於 gRPC 網站上給出的示例:

// Channel and client creation
auto channelCreds = SslCredentials(SslCredentialsOptions());
auto channel = CreateChannel("my.domain.org:12345", channelCreds);
auto stub = MyGrpc::NewStub(channel);
// Sample call
ClientContext context;
LoginUserRequest request;
request.set_username(username);
LoginUserReply reply;
Status status = m_stub->LoginUser(&context, request, &reply);

但是,對於 C++ 中這樣的代碼, status總是報告失敗並顯示UNAVAILABLE (14) "Empty update"

為什么會這樣,我該如何解決?


到目前為止我所做的調查:

  • 僅使用InsecureChannelCredentials()會導致UNAVAILABLE (14) "failed to connect to all addresses"

  • 以純文本方式運行服務器,調用確實可以使用OK (0)僅用於測試目的(雷陽建議)。

  • 我設置GRPC_TRACE=allGRPC_VERBOSITY=DEBUG環境變量,但沒有看到來自客戶端的額外日志記錄。

  • 在 C# 服務器中,我將 Grpc 日志記錄級別設置為調試,並且只注意到沒有像工作/未加密的客戶端那樣獲得“閱讀消息”日志行。

這是 gRPC 客戶端(顯然也是 macOS)的 Windows C++ 實現中的一個已知問題 gRPC authentication guide上有一條小注釋指出:

非 POSIX 兼容系統(例如 Windows)需要在 SslCredentialsOptions 中指定根證書,因為默認值僅針對 POSIX 文件系統配置。

因此,要在 Windows 上實現此功能,請按如下方式填充SslCredentialsOptions

#include <wincrypt.h>

SslCredentialsOptions getSslOptions()
{
    // Fetch root certificate as required on Windows (s. issue 25533).
    SslCredentialsOptions result;

    // Open root certificate store.
    HANDLE hRootCertStore = CertOpenSystemStoreW(NULL, L"ROOT");
    if (!hRootCertStore)
        return result;

    // Get all root certificates.
    PCCERT_CONTEXT pCert = NULL;
    while ((pCert = CertEnumCertificatesInStore(hRootCertStore, pCert)) != NULL)
    {
        // Append this certificate in PEM formatted data.
        DWORD size = 0;
        CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
            CRYPT_STRING_BASE64HEADER, NULL, &size);
        std::vector<WCHAR> pem(size);
        CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
            CRYPT_STRING_BASE64HEADER, pem.data(), &size);

        result.pem_root_certs += utf8Encode(pem.data());
    }

    CertCloseStore(hRootCertStore, 0);
    return result;
}

如果您也需要utf8Encode方法:

std::string utf8Encode(const std::wstring& wstr)
{
    if (wstr.empty())
        return string();

    int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
        NULL, 0, NULL, NULL);
    std::string strTo(sizeNeeded, 0);
    WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
        &strTo[0], sizeNeeded, NULL, NULL);
    return strTo;
}

gRPC 存儲庫上有一個(令人遺憾的是不斷陳舊的)功能請求,以在 Windows 上為此添加開箱即用的支持,並使默認SslCredentials()的行為在所有系統上都相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM