using Org.BouncyCastle.Crypto.Tls;
using Org.BouncyCastle.Security;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
namespace AIK.Service.Service
{
///
/// BouncyCastle 纯托管 TLS 1.2 握手封装,供 HTTP(BouncyCastleTlsHandler)与
/// WebSocket(BouncyCastleWebSocketClient)共用。完全绕过 Win7 SChannel 对 TLS 1.2 的限制。
///
internal static class BouncyCastleTlsHelper
{
///
/// 在已建立的 TCP 流上执行 BouncyCastle TLS 1.2 握手(含 SNI 与证书链校验),
/// 返回可读写的 TLS 流。握手为阻塞操作,调用方可在后台线程执行。
///
public static Stream ConnectTlsStream(Stream innerStream, string host)
{
var protocol = new TlsClientProtocol(innerStream, new SecureRandom());
protocol.Connect(new BouncyTlsClient(host));
return protocol.Stream;
}
///
/// BouncyCastle TLS 1.2 客户端,支持 SNI(server_name 扩展)。
/// 证书验证:使用 .NET X509Chain 校验,RevocationMode=NoCheck 以兼容 Win7 离线环境。
///
private sealed class BouncyTlsClient : DefaultTlsClient
{
private readonly string _host;
public BouncyTlsClient(string host)
: base()
{
_host = host;
}
public override IDictionary GetClientExtensions()
{
var extensions = base.GetClientExtensions() ?? new Hashtable();
// SNI:多域名虚拟主机(CDN/nginx)必须带 server_name,否则握手被拒
var serverNames = new ServerNameList(new List { new ServerName(0, _host) });
TlsExtensionsUtilities.AddServerNameExtension(extensions, serverNames);
return extensions;
}
public override TlsAuthentication GetAuthentication() => new BouncyTlsAuthentication();
}
private sealed class BouncyTlsAuthentication : TlsAuthentication
{
public void NotifyServerCertificate(Certificate serverCertificate)
{
if (serverCertificate == null || serverCertificate.Length == 0)
{
throw new TlsFatalAlert(AlertDescription.bad_certificate);
}
// 将 BouncyCastle 证书转为 .NET X509Certificate2 并做链校验
var chain = new X509Chain { ChainPolicy = { RevocationMode = X509RevocationMode.NoCheck } };
var certs = new List();
try
{
for (int i = 0; i < serverCertificate.Length; i++)
{
certs.Add(new X509Certificate2(serverCertificate.GetCertificateAt(i).GetEncoded()));
}
foreach (var extra in certs.Skip(1))
{
chain.ChainPolicy.ExtraStore.Add(extra);
}
bool valid = chain.Build(certs[0]);
if (!valid)
{
var errors = string.Join("; ", chain.ChainStatus.Select(s => s.StatusInformation.Trim()));
throw new TlsFatalAlert(AlertDescription.bad_certificate, new Exception($"证书链校验失败: {errors}"));
}
}
finally
{
foreach (var c in certs) c.Dispose();
}
}
public TlsCredentials GetClientCredentials(Org.BouncyCastle.Crypto.Tls.CertificateRequest certificateRequest) => null;
}
}
}