.NET 6 Azure Linux App Service的gRPC mTLS配置问题求助
应用服务配置
应用设置
- HTTP20_ONLY_PORT:gRPC使用的端口
常规设置
- Http版本 = 2.0
- HTTP 2.0代理 = 开启
- 客户端证书模式 = 忽略(此时客户端可正常调用gRPC,设为其他值则抛出异常)
gRPC应用代码配置
builder.WebHost.ConfigureKestrel(options => { options.ConfigureHttpsDefaults(httpsOptions => { //httpsOptions.ServerCertificate = certificate; httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; }); } builder.Services.AddGrpc(options => { options.EnableDetailedErrors = true; options.Interceptors.Add<AuthenticationInterceptor>(); // 拦截器仅包含日志逻辑 });
若修改代码中的ClientCertificateMode为其他值,调用可正常执行,但HttpContext中无法获取到客户端证书。
客户端代码
var certificate = new X509Certificate2("client.crt", "pwd"); var handler = new HttpClientHandler(); handler.ClientCertificates.Add(certificate); using var channel = GrpcChannel.ForAddress("app-service-url", new GrpcChannelOptions { HttpHandler = handler, }); var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
自签名证书生成脚本
#CA openssl genrsa -aes256 -out rootCA.key -passout pass:<pwd> 4096 openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt -passin pass:<pwd> -subj "//CN=MyRootCA" #Server openssl genrsa -aes256 -out server.key -passout pass:<pwd> 4096 openssl req -new -key server.key -out server.csr -passin pass:<pwd> -subj "//CN=app-service-url" openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 -passin pass:<pwd> openssl pkcs12 -passin pass:<pwd> -passout pass:<pwd> -export -out server.pfx -inkey server.key -in server.crt #Client openssl genrsa -aes256 -out client.key -passout pass:<pwd> 4096 openssl req -new -key client.key -out client.csr -passin pass:<pwd> -subj "//CN=Client" openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 365 -sha256 -passin pass:<pwd> openssl pkcs12 -passin pass:<pwd> -passout pass:<pwd> -export -out client.pfx -inkey client.key -in client.crt
问题现象
已在本地安装客户端证书,将服务器证书部署至App Service,也曾尝试在ConfigureHttpsDefaults中直接设置证书,但仍出现相同异常。还尝试配置转发头,但未成功。
核心问题出在App Service:当客户端证书模式设为忽略以外的值时,调用抛出如下异常:
Grpc.Core.RpcException: 'Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed because the remote party sent a TLS alert: 'ProtocolVersion'. Win32Exception: The message received was unexpected or badly formatted.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Authentication failed because the remote party sent a TLS alert: 'ProtocolVersion'. ---> System.ComponentModel.Win32Exception (0x80090326): The message received was unexpected or badly formatted. --- End of inner exception stack trace --- at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm) at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken) --- End of inner exception stack trace ---
更新信息
已将TLS设置为1.2,本地操作系统为Win11。
若客户端指定使用TLS1.2:
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
则抛出异常:
Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. Win32Exception: The function requested is not supported
使用Chrome访问App Service时,会弹出证书选择窗口且无异常,推测问题可能出在本地操作系统或客户端配置。
排查方向与解决方案
1. 客户端证书格式与存储问题
- 确保客户端使用PFX格式证书而非CRT:当前代码加载的
client.crt仅含公钥,缺少私钥,应加载生成的client.pfx,同时添加存储权限参数避免Win11下的权限问题:var certificate = new X509Certificate2("client.pfx", "pwd", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
2. Azure App Service的mTLS配置验证
- 确认App Service的TLS/SSL设置中已启用“客户端证书(入站)”,若使用自定义域名,需确保服务器证书已正确绑定。
- 检查
WEBSITE_LOAD_CERTIFICATES应用设置,添加服务器证书的Thumbprint,让应用可以访问证书。
3. Kestrel转发头配置
- 在Azure Linux App Service上,需启用证书转发,确保客户端证书能传递到应用:
并在builder.Services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownProxies.Add(IPAddress.Parse("168.63.129.16")); // 添加App Service代理IP });app.UseRouting();之前添加app.UseForwardedHeaders();。
4. TLS协议协商调整
- 检查App Service的TLS版本设置,仅启用TLS 1.2和TLS 1.3,避免旧协议冲突。
- 客户端不要硬指定单一TLS版本,让系统自动协商;若必须指定,尝试同时包含TLS 1.3:
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
5. 根CA证书信任配置
- 将
rootCA.crt安装到本地“受信任的根证书颁发机构”存储区,确保客户端信任服务器证书,避免TLS握手失败。
6. 启用详细日志排查
- 在客户端添加日志配置,查看TLS握手的具体细节:
在appsettings.json中添加:"Logging": { "LogLevel": { "System.Net.Http.HttpClient": "Debug", "System.Net.Security": "Debug" } }
内容的提问来源于stack exchange,提问作者Flow

