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; } } }