ln.http/ln.http/HttpsListener.cs

140 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
namespace ln.http
{
public class HttpsListener : IDisposable
{
public static int DefaultPort = 443;
public string CertificateStore = Path.Combine(AppContext.BaseDirectory, "certs");
private IPEndPoint _localEndPoint;
private Socket _socket;
private HttpServer _httpServer;
private X509Certificate _defaultCertificate;
private Dictionary<string, X509Certificate> _certificateCache = new Dictionary<string, X509Certificate>();
public HttpsListener(HttpServer httpServer) : this(httpServer, DefaultPort)
{
}
public HttpsListener(HttpServer httpServer, int port)
{
_httpServer = httpServer;
_localEndPoint = new IPEndPoint(IPAddress.IPv6Any, port);
Initialize();
}
private void Initialize()
{
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket.ExclusiveAddressUse = false;
_socket.Bind(_localEndPoint);
_socket.Listen();
if (File.Exists("localhost.crt"))
_defaultCertificate = X509Certificate.CreateFromCertFile("localhost.crt");
else
_defaultCertificate = buildSelfSignedServerCertificate();
ThreadPool.QueueUserWorkItem((state )=> ListenerThread());
}
private void ListenerThread()
{
while (_socket?.IsBound ?? false)
{
try
{
Socket clientSocket = _socket.Accept();
InitializeTLS(clientSocket);
}
catch
{
throw;
}
}
}
private void InitializeTLS(Socket clientSocket)
{
SslStream sslStream = new SslStream(new NetworkStream(clientSocket), false, null, CertificateSelectionCallback);
sslStream.AuthenticateAsServer(_defaultCertificate, false, false);
_httpServer.Connection(
new HttpConnection(
_localEndPoint,
(IPEndPoint)clientSocket.RemoteEndPoint,
sslStream
)
);
}
private X509Certificate CertificateSelectionCallback(object sender, string targethost,
X509CertificateCollection localcertificates, X509Certificate? remotecertificate, string[] acceptableissuers)
{
Console.Error.WriteLine("Certificate Selection for: {0}", targethost);
if (_certificateCache.TryGetValue(targethost, out X509Certificate localCertificate))
{
return localCertificate;
}
else if (File.Exists(Path.Combine(CertificateStore ?? ".", String.Format("{0}.crt", targethost))))
{
localCertificate = X509Certificate.CreateFromCertFile(
Path.Combine(CertificateStore ?? ".", String.Format("{0}.crt", targethost))
);
_certificateCache.Add(targethost, localCertificate);
return localCertificate;
}
return _defaultCertificate;
}
private X509Certificate2 buildSelfSignedServerCertificate()
{
SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
sanBuilder.AddDnsName("localhost");
sanBuilder.AddDnsName(Environment.MachineName);
X500DistinguishedName distinguishedName = new X500DistinguishedName($"CN=localhost");
using (RSA rsa = RSA.Create(4096))
{
var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature , false));
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(sanBuilder.Build());
var certificate= request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
//certificate.FriendlyName = "localhost";
using (FileStream fs = new FileStream("localhost.crt", FileMode.Create, FileAccess.Write))
fs.Write(certificate.Export(X509ContentType.Pfx));
return certificate;
}
}
public void Dispose()
{
_socket?.Close();
_socket?.Dispose();
_socket = null;
}
}
}