using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Runtime.ConstrainedExecution; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using DnsClient; ////////////////////////////////// #region __________ INIT __________ using Microsoft.Extensions.Logging.Console; var builder = WebApplication.CreateBuilder(args); //控制台日志格式 builder.Services.AddLogging(opt => { opt.AddSimpleConsole(p => { p.TimestampFormat = "[dd HH:mm:ss] "; p.SingleLine = true; p.ColorBehavior = LoggerColorBehavior.Enabled; }); }); using var host = builder.Build(); builder.WebHost.UseUrls("http://*:0"); await host.StartAsync(); var isRunning = true; var cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, _) => { isRunning = false; cts.Cancel(false); }; var logger = host.Services.GetRequiredService>(); logger.LogInformation("Hello, World!"); //Main try { await RealMain(); } catch (Exception ex) { logger.LogError(ex, "Main"); } finally { logger.LogInformation("Bye!"); await host.StopAsync(); Console.WriteLine(); Console.Write("Press ENTER to exit..."); Console.ReadLine(); } #endregion __________ INIT __________ ////////////////////////////////// async Task RealMain() { const string url = "http://example.com/index.html"; const string host = "example.com"; const string path = "/index.html"; const string dnsServerName = "reliable-dns-server-in-hosts"; var dnsServerIp = Dns.GetHostEntry(dnsServerName).AddressList.FirstOrDefault(); var lookup = new LookupClient(dnsServerIp); var result = await lookup.QueryAsync(host, QueryType.A); var record = result.Answers.ARecords().FirstOrDefault(); var ip = record?.Address; var tcpClient = new TcpClient(); tcpClient.Connect(new IPEndPoint(ip, 443)); // stuck if was ip gfw-ed var ssl = new SslStream(tcpClient.GetStream()); var sslOptions = new SslClientAuthenticationOptions { TargetHost = string.Empty, // Leave this empty to avoid sending SNI RemoteCertificateValidationCallback = (_, certificate, chain, errs) => { if (errs == SslPolicyErrors.None) return true; if (errs != SslPolicyErrors.RemoteCertificateNameMismatch) return false; if (certificate is not X509Certificate2 cert2) return false; // 先验证证书的有效期 if (DateTime.Now < cert2.NotBefore || DateTime.Now > cert2.NotAfter) return false; // 然后比较证书名称和主机名称 (任意一个匹配) var names = GetAllSubjectAlternativeNames(cert2); var flagNameMatched = false; foreach (var certName in names) { if (certName.StartsWith("*.")) { if (!host.EndsWith(certName[2..], StringComparison.OrdinalIgnoreCase)) continue; flagNameMatched = true; break; } if (certName.Equals(host, StringComparison.OrdinalIgnoreCase)) { flagNameMatched = true; break; } } if (flagNameMatched == false) return false; // 最后检查信任链 if (chain == null) return false; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; //检测吊销耗时太长,忽略 chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 10); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; var isValidChain = chain.Build(cert2); if (isValidChain) return true; foreach (X509ChainStatus chainStatus in chain.ChainStatus) { // 仅处理会影响安全性的错误状态 if (chainStatus.Status == X509ChainStatusFlags.RevocationStatusUnknown || chainStatus.Status == X509ChainStatusFlags.OfflineRevocation || chainStatus.Status == X509ChainStatusFlags.NoError) { continue; } // 其他任何错误状态都认为证书无效 return false; } return true; } }; await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token); ssl.Write(Encoding.ASCII.GetBytes($"GET {path} HTTP/1.1\r\n")); ssl.Write(Encoding.ASCII.GetBytes($"Host: {host}\r\n")); ssl.Write(Encoding.ASCII.GetBytes($"\r\n")); var reader = new StreamReader(ssl); var lines = new List(); // <- HTTP/1.1 200 OK -- PoC SUCC! do { var line = reader.ReadLine(); if (line == "") break; lines.Add(line); } while (true); int bp = 0; } IReadOnlyList GetAllSubjectAlternativeNames(X509Certificate2 cert) { var names = new HashSet(); foreach (var extension in cert.Extensions) { if (extension is X509SubjectAlternativeNameExtension sanExtension) { foreach (var name in sanExtension.EnumerateDnsNames()) names.Add(name); } else if (extension.Oid?.Value == "2.5.29.17") // Subject Alternative Name OID { var asnData = new AsnEncodedData(extension.Oid, extension.RawData); var sanString = asnData.Format(true); var sanParts = sanString.Split(new[] { ", ", "DNS Name=", " " }, StringSplitOptions.RemoveEmptyEntries); foreach (var part in sanParts) { if (!string.IsNullOrEmpty(part) && !part.StartsWith("IPAddress") && !part.StartsWith("Uri")) { names.Add(part); } } } } // 添加证书的CN(通用名称) var certName = cert.GetNameInfo(X509NameType.DnsName, false); if (!string.IsNullOrEmpty(certName)) names.Add(certName); return [.. names]; }