部署至Azure后调用Itaú银行API遇SSL证书认证错误求助
问题描述
集成Itaú银行API生成票据,本地运行时能正常建立连接,部署到Azure服务器后返回以下错误:
无法建立SSL连接,请查看内部异常。提供给程序包的凭据未被识别 - System.Net.WebException: 无法建立SSL连接,请查看内部异常。提供给程序包的凭据未被识别
---> System.Net.Http.HttpRequestException: 无法建立SSL连接,请查看内部异常。
---> System.ComponentModel.Win32Exception (0x8009030D): 提供给程序包的凭据未被识别
调用API的代码如下:
public async Task<AuthTokenItau> AutenticarItauJwt() { var request = (HttpWebRequest)WebRequest.Create("https://sts.itau.com.br/api/oauth/token"); request.ContentType = "application/x-www-form-urlencoded"; request.Method = "POST"; var dataBody = "grant_type=" + Uri.EscapeDataString("client_credentials"); dataBody += "&client_id=" + Uri.EscapeDataString("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); dataBody += "&client_secret=" + Uri.EscapeDataString("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); var body = Encoding.ASCII.GetBytes(dataBody); request.ContentLength = body.Length; string response = ""; Stream myWriter = null; try { var caminhoCertificado = $@"{_webHostingEnvironment.ContentRootPath}\App_Data\Certificado\certifica_digital.pfx"; var senhaCertificado = @"senhaZZZZ"; X509Certificate2 certificate = new X509Certificate2(caminhoCertificado, senhaCertificado, X509KeyStorageFlags.DefaultKeySet); request.ClientCertificates.Add(certificate); RegistraLog.Log($"Certificado -{certificate.ToString()}"); myWriter = request.GetRequestStream(); myWriter.Write(body, 0, body.Length); var requestResponse = (HttpWebResponse)request.GetResponse(); RegistraLog.Log($"Response : {requestResponse.Headers.AllKeys} - {dataBody.ToString()}"); response = new StreamReader(requestResponse.GetResponseStream()).ReadToEnd(); RegistraLog.Log($"Response -{response.ToString()}"); return JsonConvert.DeserializeObject<AuthTokenItau>(response); ; } catch (Exception ex) { RegistraLog.Log($"Erro ao solicitar o token.\r\nDetalhe: {ex.Message} - {ex.ToString()}"); throw new Exception("Erro ao solicitar o token.\r\nDetalhe: " + ex.Message); } finally { if (myWriter != null) myWriter.Close(); } }
完整异常堆栈:
无法建立SSL连接,请查看内部异常。提供给程序包的凭据未被识别 - System.Net.WebException: 无法建立SSL连接,请查看内部异常。提供给程序包的凭据未被识别
---> System.Net.Http.HttpRequestException: 无法建立SSL连接,请查看内部异常。
---> System.ComponentModel.Win32Exception (0x8009030D): 提供给程序包的凭据未被识别
at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface secModule, String package, CredentialUse intent, SCHANNEL_CRED scc)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(CredentialUse credUsage, SCHANNEL_CRED secureCredential)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer)
at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output)
at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at System.Net.HttpWebRequest.SendRequest()
at System.Net.HttpWebRequest.GetResponse()
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.GetResponse()
at EscolaPro.ControleBoleto.AuthItauService.AutenticarItauJwt() in D:\GlobalManage\Repositorio\globalmanage\global-manage-api-web\EscolaPro.ControleBoleto\AuthItauService.cs:line 58
解决方案
1. 调整证书加载的KeyStorageFlags
本地环境用DefaultKeySet没问题,但Azure应用服务的运行身份没有用户证书存储的访问权限。改成以下组合,强制证书加载到机器存储,同时允许密钥持久化和导出:
X509Certificate2 certificate = new X509Certificate2( caminhoCertificado, senhaCertificado, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable );
2. 验证Azure环境中的证书文件
- 用Azure门户的Kudu工具(访问
https://<你的应用名>.scm.azurewebsites.net)检查App_Data\Certificado目录是否存在,PFX文件是否上传完整。 - 若文件权限有问题,建议将证书上传到Azure Key Vault,通过应用服务的托管身份读取证书,避免文件系统权限限制。
3. 替换HttpWebRequest为HttpClient
HttpWebRequest是.NET旧版API,HttpClient对SSL证书的处理更稳定,且异步支持更完善。重构后的代码示例:
public async Task<AuthTokenItau> AutenticarItauJwt() { var handler = new HttpClientHandler(); var caminhoCertificado = $@"{_webHostingEnvironment.ContentRootPath}\App_Data\Certificado\certifica_digital.pfx"; var senhaCertificado = @"senhaZZZZ"; X509Certificate2 certificate = new X509Certificate2( caminhoCertificado, senhaCertificado, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable ); handler.ClientCertificates.Add(certificate); using var client = new HttpClient(handler); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var formData = new Dictionary<string, string> { {"grant_type", "client_credentials"}, {"client_id", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}, {"client_secret", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"} }; var response = await client.PostAsync("https://sts.itau.com.br/api/oauth/token", new FormUrlEncodedContent(formData)); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); RegistraLog.Log($"Response -{responseContent}"); return JsonConvert.DeserializeObject<AuthTokenItau>(responseContent); }
4. 检查Azure应用服务的TLS设置
在Azure门户的应用服务“TLS/SSL设置”中,将最低TLS版本设置为TLS 1.2,Itaú银行API通常要求此版本,避免因TLS版本不兼容导致握手失败。
内容的提问来源于stack exchange,提问作者David Garzaro

