You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

.NET Core中SOAP请求触发Http.WinHttpException及SSL认证异常排查求助

Troubleshooting SSL Authentication Failures in .NET Core 2.0 IHostedService SOAP Requests

Let's break down the likely causes of your SSL authentication failures and walk through actionable fixes based on your scenario.

Key Observations from Your Problem

  • You have three IHostedServices making SOAP requests with client certificate authentication.
  • Adding the third service leads to intermittent failures that block all requests until app restart.
  • Even when isolating the third service, the second call fails (first works fine).
  • The error points to SSL handshake failures during GetResponseAsync().

Likely Root Causes & Fixes

1. Certificate Loading Conflict (Most Probable)

Your code imports the PFX certificate every time a request is made using X509Certificate2Collection.Import() with X509KeyStorageFlags.PersistKeySet. This flag persists the private key to the system's certificate store, which causes two critical issues:

  • Subsequent imports of the same PFX can hit locks or conflicts with the persisted private key.
  • In long-running services like IHostedService, repeated certificate creation leads to orphaned key entries that exhaust system resources.

Fix:

  • Load the certificate once when the service starts, not per request.
  • Use X509KeyStorageFlags.EphemeralKeySet instead of PersistKeySet—this keeps the private key in memory only, avoiding store conflicts.

Example code for service-level certificate loading:

private readonly X509Certificate2 _clientCertificate;
private readonly ILogger _logger;

public PollingHostedService(ILogger<PollingHostedService> logger)
{
    _logger = logger;
    // Load certificate ONCE at service initialization
    string certPath = GetCertPath();
    string certPass = GetCertPassword();
    
    try
    {
        _clientCertificate = new X509Certificate2(
            certPath, 
            certPass, 
            X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable);
    }
    catch (Exception ex)
    {
        _logger.LogCritical(ex, "Failed to load client certificate");
        throw;
    }
}

Then reuse _clientCertificate in all your SOAP requests instead of reimporting.

2. HttpWebRequest Resource Leaks & Connection Pool Issues

HttpWebRequest is legacy in .NET Core—while it works, it's not optimized for long-running background services. Poor connection management can lead to exhausted connection pools or orphaned SSL sessions, which manifest as intermittent handshake failures.

Fix: Replace HttpWebRequest with HttpClient
HttpClient is designed for reuse in long-lived services and handles connection pooling automatically. Here's how to adapt your SOAP request logic:

private readonly HttpClient _httpClient;

public PollingHostedService(ILogger<PollingHostedService> logger)
{
    _logger = logger;
    // Initialize HttpClient with certificate handler ONCE
    var cert = new X509Certificate2(GetCertPath(), GetCertPassword(), X509KeyStorageFlags.EphemeralKeySet);
    
    var handler = new HttpClientHandler
    {
        ClientCertificates = { cert },
        Proxy = null,
        // Enable mutual TLS (match your original AuthenticationLevel.MutualAuthRequired)
        ServerCertificateCustomValidationCallback = (sender, serverCert, chain, errors) =>
        {
            // Validate server certificate here (adjust for your security needs)
            return errors == SslPolicyErrors.None;
        }
    };
    
    _httpClient = new HttpClient(handler);
    _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
}

public async Task<T> SendSoapRequestAsync<T>(string url, string xmlRequestContent)
{
    try
    {
        var content = new StringContent(xmlRequestContent, Encoding.UTF8, "text/xml");
        content.Headers.Add("SOAP:Action", ""); // Replace with your actual SOAP action
        
        var response = await _httpClient.PostAsync(url, content);
        response.EnsureSuccessStatusCode();
        
        var responseBody = await response.Content.ReadAsStringAsync();
        var serializer = new XmlSerializer(typeof(T));
        
        using var reader = new StringReader(responseBody);
        return (T)serializer.Deserialize(reader);
    }
    catch (HttpRequestException ex)
    {
        _logger.LogError(ex, $"SOAP request to {url} failed");
        throw;
    }
}

3. Private Key Permissions

If your app runs under a restricted account (like an IIS AppPool identity), it may lack permission to access the certificate's private key after repeated loads.

Fix:

  1. Open the Certificates MMC snap-in (mmc.exe → Add/Remove Snap-in → Certificates → Computer Account).
  2. Navigate to Personal → Certificates, find your client cert.
  3. Right-click → All Tasks → Manage Private Keys.
  4. Add your app's runtime account (e.g., IIS AppPool\YourAppPool) and grant Read permissions.

4. .NET Core 2.0 Known Issues

.NET Core 2.0 is end-of-life and has known bugs in SSL/TLS handling, especially around client certificates and connection pooling. Upgrading to a supported LTS version (like .NET Core 2.1 or later) could resolve underlying framework-level issues.

Debugging Steps to Confirm the Root Cause

  • Enable SSL/TLS Logging: On Windows, enable Schannel logging to capture detailed TLS handshake logs (check Event Viewer → Applications and Services Logs → Microsoft → Windows → Schannel).
  • Check External System Logs: Ask the external system's admin to review their server logs—they may see specific errors like invalid certificate chain or expired cert.
  • Process Monitor: Use Sysinternals Process Monitor to track file/registry access for your app—look for denied access to the PFX file or certificate store keys.

内容的提问来源于stack exchange,提问作者Simone Serra

火山引擎 最新活动