140 lines
5.3 KiB
C#
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;
|
|
}
|
|
}
|
|
} |