OpenSSL BIO_Read Возвращает отрицательное значениеC#

Место общения программистов C#
Anonymous
OpenSSL BIO_Read Возвращает отрицательное значение

Сообщение Anonymous »

продолжу свой путь отсюда: не могу добавить TLS к моему HTTP-серверу, клиенты не могут к нему подключиться
где я пытаюсь реализовать HTTPS-сервер
HTTPServer

Код: Выделить всё

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

using OpenSsl;

namespace RawHttpListener
{
#region HTTPParser
public interface IHTTPClientHandler
{
Task HTTPClientConnected(HttpContext ctx);
}

public class HttpContext
{
public HttpRequest Request { get; set; }
public HttpResponse Response { get; set; }

public HttpContext(HttpRequest request, HttpResponse response)
{
Request = request;
Response = response;
}
}

public class HttpRequest
{
public string HttpMethod { get; set; }
public string Url { get; set; }
public string UserHostName { get; set; }
public string UserAgent { get; set; }
public string Body { get; set; }

public HttpRequest(string httpMethod, string url, string userHostName, string userAgent)
{
HttpMethod = httpMethod;
Url = url;
UserHostName = userHostName;
UserAgent = userAgent;
}
}

public class HttpResponse
{
public string ContentType { get; set; }
public Encoding ContentEncoding { get; set; }
public long ContentLength64 { get; set; }
public Stream OutputStream { get; set; }

public HttpResponse(Stream outputStream)
{
OutputStream = outputStream;
}
}

public class HTTPParser : Stream
{
private readonly IHTTPClientHandler _clientHandler;
private readonly Stream _baseStream;
private readonly MemoryStream _memoryStream = new MemoryStream();
private const int BufferSize = 4096;
private readonly byte[] _buffer = new byte[BufferSize];

public HTTPParser(Stream baseStream, IHTTPClientHandler clientHandler)
{
_baseStream = baseStream;
_clientHandler = clientHandler;
}

public async Task HandleClientConnected()
{
int bytesRead;
// Read the incoming data until the connection is closed
while ((bytesRead = await _baseStream.ReadAsync(_buffer, 0, _buffer.Length)) > 0)
{
await _memoryStream.WriteAsync(_buffer, 0, bytesRead);

// Check for the end of the request (empty line means headers end)
if (bytesRead < BufferSize) break;
}

// Convert the raw request to a string
string rawRequest = Encoding.UTF8.GetString(_memoryStream.ToArray());
//Console.WriteLine("Raw HTTP Request:");
//Console.WriteLine(rawRequest);

// Handle the HTTP request
await HandleHttpRequest(rawRequest);
}

private async Task HandleHttpRequest(string rawRequest)
{
// Fill the MyOwnHttpListenerContext object and call the HTTPClientConnected method
var lines = rawRequest.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0) return;

// Parse the request line (first line)
var requestLine = lines[0].Split(' ');
if (requestLine.Length < 3) return;

string method = requestLine[0];
string url = requestLine[1];
string httpVersion = requestLine[2];

// Extract headers
string userHostName = string.Empty;
string userAgent = string.Empty;

for (int i = 1; i < lines.Length; i++) // Skip the request line
{
if (lines[i].StartsWith("Host:"))
{
userHostName = lines[i].Substring(6).Trim(); // Skip "Host: "
}
else if (lines[i].StartsWith("User-Agent:"))
{
userAgent = lines[i].Substring(12).Trim();  // Skip "User-Agent: "
}
}

// Create the request and response objects
var request = new HttpRequest(method, url, userHostName, userAgent);
var response = new HttpResponse(_baseStream);

var ctx = new HttpContext(request, response);
await _clientHandler.HTTPClientConnected(ctx);
}

// Implementing abstract members of Stream class
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length;
public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; }

public override void Flush() => _baseStream.Flush();

public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);

public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);

public override void SetLength(long value) => _baseStream.SetLength(value);

public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);
}

#endregion
#region RawHttpServer
public class RawHttpServer : IHTTPClientHandler
{
public string ipAddress = string.Empty; // Change to your desired IP address
public int port; // Use a non-privileged port for testing
bool isHTTPS;

public RawHttpServer()
{
this.ipAddress = "172.0.0.1";
this.port = 80;
this.isHTTPS = false;
}
public RawHttpServer(IPAddress ip, int port, bool isHTTPS)
{
this.ipAddress = ip.ToString();
this.port = port;
this.isHTTPS = isHTTPS;
}

public void Start()
{
TcpListener tcpListener = new TcpListener(IPAddress.Parse(ipAddress), port);
tcpListener.Start();
Console.WriteLine($"Listening for connections on {ipAddress}:{port}");

while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine($"Accepted connection from {tcpClient.Client.RemoteEndPoint}");

Task.Run(() => HandleClient(tcpClient));
}
}

private async Task HandleClient(TcpClient tcpClient)
{
using (NetworkStream stream = tcpClient.GetStream())
{
HTTPParser httpParser;
if (!this.isHTTPS)
{
// For HTTP connections
httpParser = new HTTPParser(stream, this);
await httpParser.HandleClientConnected(); // Handle the client connection
}
else
{
// For HTTPS connections
try
{
Console.WriteLine("Starting TLS Handshake...");

//using (var tlsStream = new TlsStream(tcpClient, "cert.pem", "key.pem", new[] { "h2", "http/1.1" })) //If using BIO //If I use socket handle, I have to provide TCPClient object
using (var tlsStream = new TlsStream(stream, "cert.pem", "key.pem", new[] { "h2", "http/1.1" })) //If using BIO
{
// Perform the TLS handshake
await tlsStream.DoHandshakeAsync();
Console.WriteLine("TLS Handshake completed.");

string applicationProtocol = tlsStream.GetNegotiatedApplicationProtocol();
Console.WriteLine($"Negotiated Application Protocol: {applicationProtocol}");

// Create a new HTTP parser using the TlsStream for the connection
httpParser = new HTTPParser(tlsStream, this);
await httpParser.HandleClientConnected();  // Handle the client connection
}
}
catch (Exception ex)
{
Console.WriteLine($"Error during TLS handshake: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
}
}
tcpClient.Close();
}

public async Task HTTPClientConnected(HttpContext ctx)
{
// Print out some info about the request
Console.WriteLine($"Received request: {ctx.Request.HttpMethod} {ctx.Request.Url}");
Console.WriteLine($"User Host Name: {ctx.Request.UserHostName}");
Console.WriteLine($"User Agent: {ctx.Request.UserAgent}");

if (ctx.Request.HttpMethod == "POST")
{
Console.WriteLine("POST BODY: ");
Console.WriteLine(ctx.Request.Body);
}

// Write the response info
byte[] data = Encoding.UTF8.GetBytes("Hello World");
ctx.Response.ContentType = "text/html";
ctx.Response.ContentEncoding = Encoding.UTF8;
ctx.Response.ContentLength64 = data.Length;

// Write out to the response stream (asynchronously), then close it
await ctx.Response.OutputStream.WriteAsync(data, 0, data.Length);
ctx.Response.OutputStream.Close(); // Close the output stream
Console.WriteLine("Response sent to the client.");
}
}
#endregion

//Our main class here
class Program
{
static void Main(string[] args)
{
bool isHTTPS = true;
RawHttpServer server = new RawHttpServer(IPAddress.Parse("192.168.88.12"), 443, isHTTPS);
server.Start();

//Usefull: https://github.com/FoxCouncil/LibFoxyProxy/tree/72a32174a7adc71885c8b0a2758eab173f5a0d4a
}
}
}
и в рабочем, и в нерабочем случае я использую одни и те же оболочки OpenSSL, которые пришлось разместить здесь: https://hastebin.com/share/dayisopedo.csharp
Я удалось заставить его работать с использованием сокетов, работа TLSStream с использованием сокетов выглядит так

Код: Выделить всё

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OpenSsl
{
public class TlsStream : Stream
{
private readonly TcpClient _tcpClient;
private readonly Stream _innerStream;
private IntPtr _ctx;
private IntPtr _ssl;

static TlsStream()
{
OpenSsl.SSL_library_init();
OpenSsl.SSL_load_error_strings();
OpenSsl.ERR_load_BIO_strings();
OpenSsl.OpenSSL_add_all_algorithms();
}

public TlsStream(TcpClient tcpClient, Stream innerStream, string certificatePath, string privateKeyPath, IEnumerable protocols)
{
_tcpClient = tcpClient;

_ctx = OpenSsl.SSL_CTX_new(OpenSsl.TLSv1_2_method());
if (_ctx == IntPtr.Zero)
{
throw new Exception("Unable to create SSL context.");
}

OpenSsl.SSL_CTX_set_ecdh_auto(_ctx, 1);

if (OpenSsl.SSL_CTX_use_certificate_file(_ctx, certificatePath, 1) != 1)
{
throw new Exception("Unable to load certificate file.");
}

if (OpenSsl.SSL_CTX_use_PrivateKey_file(_ctx, privateKeyPath, 1) != 1)
{
throw new Exception("Unable to load private key file.");
}

_ssl = OpenSsl.SSL_new(_ctx);

// Set the file descriptor (socket FD) directly on the SSL object
//var socket = tcpClient.Client;  // Get the underlying socket
//OpenSsl.SSL_set_fd(_ssl, socket.Handle.ToInt32());

Console.WriteLine("[TLS Stream] Setting file descriptor for SSL...");
OpenSsl.SSL_set_fd(_ssl, (int)_tcpClient.Client.Handle); // Set the file descriptor for SSL
}

public override bool CanRead => true;
public override bool CanWrite =>  true;
public override bool CanSeek => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();

public override void Flush() => FlushAsync(default(CancellationToken)).GetAwaiter().GetResult();

public override int Read(byte[] buffer, int offset, int count)
{
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
}

public override void Write(byte[] buffer, int offset, int count)
{
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
}

public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// Call SSL_read directly
var ret = OpenSsl.SSL_read(_ssl, buffer, offset, count);
if (ret 

Подробнее здесь: [url]https://stackoverflow.com/questions/79067359/openssl-bio-read-returns-negative-value[/url]

Вернуться в «C#»