Linux 中的 SSL 验证失败
SSL Verification Fails in Linux
我正在使用 Titanium Web 代理修改 headers 的 https 请求。它在 Windows 中工作正常,但在 Linux 中它失败并出现 alert handshake failure
错误。
我已经使用以下命令生成了根 CA 证书。
openssl genrsa -out rootCert.key 4096
openssl req -x509 -new -nodes -key rootCert.key -sha256 -days 1024 -out rootCert.crt
openssl pkcs12 -export -out rootCert.pfx -inkey rootCert.key -in rootCert.crt
并使用以下命令安装生成的证书。还要确保我的证书文件正确附加到 /etc/ssl/certs/ca-certificates.crt
文件。
sudo cp rootCert.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
我的代理服务代码
public class ProxyService : IHostedService, IDisposable
{
#region Private Fields
private readonly ILogger<ProxyService> logger;
private readonly ProxyConfig proxyConfig;
private readonly HeaderConfig headerConfig;
private readonly ProxyServer proxyServer;
private ExplicitProxyEndPoint explicitEndPoint;
#endregion
#region Constructor
public ProxyService(ILogger<ProxyService> logger, IOptions<ProxyConfig> proxyConfig, IOptions<HeaderConfig> headerConfig)
{
this.logger = logger;
this.proxyConfig = proxyConfig.Value;
this.headerConfig = headerConfig.Value;
proxyServer = new ProxyServer();
proxyServer.ExceptionFunc += onException;
proxyServer.TcpTimeWaitSeconds = 10;
proxyServer.ConnectionTimeOutSeconds = 15;
proxyServer.ReuseSocket = false;
proxyServer.EnableConnectionPool = false;
proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true;
}
#endregion
#region IHostedService Implementation
public Task StartAsync(CancellationToken cancellationToken)
{
proxyServer.BeforeRequest += onRequest;
proxyServer.ServerCertificateValidationCallback += onCertificateValidation;
explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Parse(proxyConfig.IPAddress), proxyConfig.Port);
explicitEndPoint.BeforeTunnelConnectRequest += onBeforeTunnelConnectRequest;
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.BouncyCastleFast;
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
foreach (ProxyEndPoint endPoint in proxyServer.ProxyEndPoints)
{
logger.LogInformation("Listening on '{0}' at IP {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Stopping proxy service!");
explicitEndPoint.BeforeTunnelConnectRequest -= onBeforeTunnelConnectRequest;
proxyServer.BeforeRequest -= onRequest;
proxyServer.ServerCertificateValidationCallback -= onCertificateValidation;
proxyServer.Stop();
return Task.CompletedTask;
}
#endregion
#region IDisposable Implementation
public void Dispose()
{
proxyServer.Dispose();
}
#endregion
#region Event Handlers
private Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
logger.LogInformation("Tunnel to: {0}", hostname);
if (!hostname.Contains(headerConfig.BaseHostName))
{
e.DecryptSsl = false;
}
return Task.CompletedTask;
}
private Task onRequest(object sender, SessionEventArgs e)
{
var headers = e.HttpClient.Request.Headers;
if (headers.HeaderExists(headerConfig.TokenHeaderName))
{
var uri = e.HttpClient.Request.RequestUri;
if (uri.Host.EndsWith(headerConfig.BaseHostName) && uri.AbsolutePath.StartsWith(headerConfig.UriPath))
{
logger.LogInformation("Adding auth header to: {0}", uri.ToString());
var token = headers.GetFirstHeader(headerConfig.TokenHeaderName).Value;
headers.AddHeader("Authorization", token);
}
headers.RemoveHeader(headerConfig.TokenHeaderName);
}
return Task.CompletedTask;
}
private Task onCertificateValidation(object sender, CertificateValidationEventArgs e)
{
if (e.SslPolicyErrors == SslPolicyErrors.None)
{
e.IsValid = true;
}
return Task.CompletedTask;
}
private void onException(Exception exception)
{
logger.LogError(exception, exception.Message);
}
#endregion
}
完全错误
proxy_1 | Error occured whilst handling session request
proxy_1 | Titanium.Web.Proxy.Exceptions.ProxyHttpException: Error occured whilst handling session request
proxy_1 | ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
proxy_1 | ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
proxy_1 | ---> Interop+Crypto+OpenSslCryptographicException: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
proxy_1 | --- End of inner exception stack trace ---
proxy_1 | at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, Byte[] recvBuf, Int32 recvOffset, Int32 recvCount, Byte[]& sendBuf, Int32& sendCount)
proxy_1 | at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteContext& context, ArraySegment`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
proxy_1 | --- End of inner exception stack trace ---
proxy_1 | at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
proxy_1 | at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
proxy_1 | --- End of stack trace from previous location where exception was thrown ---
proxy_1 | at System.Net.Security.SslStream.ThrowIfExceptional()
proxy_1 | at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
proxy_1 | at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
proxy_1 | at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
proxy_1 | at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
proxy_1 | at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
proxy_1 | --- End of stack trace from previous location where exception was thrown ---
很难给出明确的答案,但我认为问题出在您生成的证书上。尝试添加 -addext
参数并将主题备用名称设置为您的目标主机名。例如,对于 'example.com' 域:
openssl req -x509 -new -nodes -key rootCert.key -sha256 -days 1024 \
-out rootCert.crt -addext "subjectAltName = DNS:example.com"
或者,如果您想使用根证书,您可以 运行 没有 rootCert.pfx 文件的 Titanium 服务,它应该会生成一个。然后您可以提取 PEM 证书并将其添加到 ca-certificates:
openssl pkcs12 -in rootCert.pfx -out rootCert.crt -clcerts -nokeys
信任根证书后,Titanium 生成的所有证书都应该有效。
我正在使用 Titanium Web 代理修改 headers 的 https 请求。它在 Windows 中工作正常,但在 Linux 中它失败并出现 alert handshake failure
错误。
我已经使用以下命令生成了根 CA 证书。
openssl genrsa -out rootCert.key 4096
openssl req -x509 -new -nodes -key rootCert.key -sha256 -days 1024 -out rootCert.crt
openssl pkcs12 -export -out rootCert.pfx -inkey rootCert.key -in rootCert.crt
并使用以下命令安装生成的证书。还要确保我的证书文件正确附加到 /etc/ssl/certs/ca-certificates.crt
文件。
sudo cp rootCert.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
我的代理服务代码
public class ProxyService : IHostedService, IDisposable
{
#region Private Fields
private readonly ILogger<ProxyService> logger;
private readonly ProxyConfig proxyConfig;
private readonly HeaderConfig headerConfig;
private readonly ProxyServer proxyServer;
private ExplicitProxyEndPoint explicitEndPoint;
#endregion
#region Constructor
public ProxyService(ILogger<ProxyService> logger, IOptions<ProxyConfig> proxyConfig, IOptions<HeaderConfig> headerConfig)
{
this.logger = logger;
this.proxyConfig = proxyConfig.Value;
this.headerConfig = headerConfig.Value;
proxyServer = new ProxyServer();
proxyServer.ExceptionFunc += onException;
proxyServer.TcpTimeWaitSeconds = 10;
proxyServer.ConnectionTimeOutSeconds = 15;
proxyServer.ReuseSocket = false;
proxyServer.EnableConnectionPool = false;
proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true;
}
#endregion
#region IHostedService Implementation
public Task StartAsync(CancellationToken cancellationToken)
{
proxyServer.BeforeRequest += onRequest;
proxyServer.ServerCertificateValidationCallback += onCertificateValidation;
explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Parse(proxyConfig.IPAddress), proxyConfig.Port);
explicitEndPoint.BeforeTunnelConnectRequest += onBeforeTunnelConnectRequest;
proxyServer.CertificateManager.CertificateEngine = Titanium.Web.Proxy.Network.CertificateEngine.BouncyCastleFast;
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
foreach (ProxyEndPoint endPoint in proxyServer.ProxyEndPoints)
{
logger.LogInformation("Listening on '{0}' at IP {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Stopping proxy service!");
explicitEndPoint.BeforeTunnelConnectRequest -= onBeforeTunnelConnectRequest;
proxyServer.BeforeRequest -= onRequest;
proxyServer.ServerCertificateValidationCallback -= onCertificateValidation;
proxyServer.Stop();
return Task.CompletedTask;
}
#endregion
#region IDisposable Implementation
public void Dispose()
{
proxyServer.Dispose();
}
#endregion
#region Event Handlers
private Task onBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
logger.LogInformation("Tunnel to: {0}", hostname);
if (!hostname.Contains(headerConfig.BaseHostName))
{
e.DecryptSsl = false;
}
return Task.CompletedTask;
}
private Task onRequest(object sender, SessionEventArgs e)
{
var headers = e.HttpClient.Request.Headers;
if (headers.HeaderExists(headerConfig.TokenHeaderName))
{
var uri = e.HttpClient.Request.RequestUri;
if (uri.Host.EndsWith(headerConfig.BaseHostName) && uri.AbsolutePath.StartsWith(headerConfig.UriPath))
{
logger.LogInformation("Adding auth header to: {0}", uri.ToString());
var token = headers.GetFirstHeader(headerConfig.TokenHeaderName).Value;
headers.AddHeader("Authorization", token);
}
headers.RemoveHeader(headerConfig.TokenHeaderName);
}
return Task.CompletedTask;
}
private Task onCertificateValidation(object sender, CertificateValidationEventArgs e)
{
if (e.SslPolicyErrors == SslPolicyErrors.None)
{
e.IsValid = true;
}
return Task.CompletedTask;
}
private void onException(Exception exception)
{
logger.LogError(exception, exception.Message);
}
#endregion
}
完全错误
proxy_1 | Error occured whilst handling session request
proxy_1 | Titanium.Web.Proxy.Exceptions.ProxyHttpException: Error occured whilst handling session request
proxy_1 | ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
proxy_1 | ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
proxy_1 | ---> Interop+Crypto+OpenSslCryptographicException: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
proxy_1 | --- End of inner exception stack trace ---
proxy_1 | at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, Byte[] recvBuf, Int32 recvOffset, Int32 recvCount, Byte[]& sendBuf, Int32& sendCount)
proxy_1 | at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteContext& context, ArraySegment`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
proxy_1 | --- End of inner exception stack trace ---
proxy_1 | at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
proxy_1 | at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
proxy_1 | at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
proxy_1 | --- End of stack trace from previous location where exception was thrown ---
proxy_1 | at System.Net.Security.SslStream.ThrowIfExceptional()
proxy_1 | at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
proxy_1 | at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
proxy_1 | at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
proxy_1 | at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
proxy_1 | at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
proxy_1 | --- End of stack trace from previous location where exception was thrown ---
很难给出明确的答案,但我认为问题出在您生成的证书上。尝试添加 -addext
参数并将主题备用名称设置为您的目标主机名。例如,对于 'example.com' 域:
openssl req -x509 -new -nodes -key rootCert.key -sha256 -days 1024 \
-out rootCert.crt -addext "subjectAltName = DNS:example.com"
或者,如果您想使用根证书,您可以 运行 没有 rootCert.pfx 文件的 Titanium 服务,它应该会生成一个。然后您可以提取 PEM 证书并将其添加到 ca-certificates:
openssl pkcs12 -in rootCert.pfx -out rootCert.crt -clcerts -nokeys
信任根证书后,Titanium 生成的所有证书都应该有效。