Skip to content

Commit

Permalink
feat: RsaStream,用于加密通信
Browse files Browse the repository at this point in the history
remove: 删除无用代码
  • Loading branch information
SALTWOOD committed Aug 13, 2024
1 parent 7a1e1a9 commit 816fab5
Show file tree
Hide file tree
Showing 17 changed files with 212 additions and 332 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -362,5 +362,4 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd

/TeraIO.Test/
/TeraIO.Test/*
/TeraIO.Test/Program.cs
8 changes: 8 additions & 0 deletions Extension/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,13 @@ public static void Merge<T>(this ICollection<T> left, IEnumerable<T> right)
left.Add(item);
}
}

public static void ForEach<T>(this IEnumerable<T> values, Action<T> action)
{
foreach (T item in values)
{
action(item);
}
}
}
}
7 changes: 3 additions & 4 deletions Network/Http/HttpServerAppBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,22 @@
using System.Text;
using System.Threading.Tasks;
using TeraIO.Extension;
using TeraIO.Runnable;

namespace TeraIO.Network.Http
{
/// <summary>
/// <see cref="HttpServer"/> 返回的 HttpServer 实例。调用 <see cref="HttpServerAppBase.Run"/> 来启动一个简单的 Http 服务器
/// 正常情况下,你不应该创建这个类的实例,而是由 <see cref="HttpServer"/> 创建或者通过它的子类来获得它的实例!
/// </summary>
public class HttpServerAppBase : RunnableBase
public class HttpServerAppBase
{

public List<string> UriPrefixes { get; set; }
public Dictionary<HttpHandlerAttribute, MethodInfo> methods;

protected HttpListener listener;

public HttpServerAppBase(Dictionary<HttpHandlerAttribute, MethodInfo>? methods = null, ILoggerBuilder? loggerBuilder = null) : base(loggerBuilder)
public HttpServerAppBase(Dictionary<HttpHandlerAttribute, MethodInfo>? methods = null) : base()
{
if (methods == null)
{
Expand Down Expand Up @@ -57,7 +56,7 @@ protected void LoadNew()
this.methods = methods;
}

protected override int Run(string[] args)
protected int Run(string[] args)
{
int result = 0;
this.LoadNew();
Expand Down
11 changes: 5 additions & 6 deletions Network/Http/SimpleWebServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using TeraIO.Extension;
using System.Threading.Tasks;
using TeraIO.Runnable;
using System.Text.RegularExpressions;
using TeraIO.Extension;
using System.Diagnostics;

namespace CSharpOpenBMCLAPI.Modules.WebServer
{
public class SimpleWebServer : RunnableBase
public class SimpleWebServer
{
private int Port = 0; // TCP 随机端口
private readonly X509Certificate2? _certificate; // SSL证书
Expand All @@ -28,7 +27,7 @@ public SimpleWebServer(int port, X509Certificate2? certificate)
_certificate = certificate;
}

protected override int Run(string[] args)
protected int Run(string[] args)
{
while (true)
{
Expand Down Expand Up @@ -61,7 +60,7 @@ protected async Task<int> AsyncRun()
tcpClient = await listener.AcceptTcpClientAsync();
_ = Task.Run(() => HandleRequest(tcpClient));
}
catch (Exception ex)
catch
{
if (tcpClient != null && tcpClient.Connected)
{
Expand Down Expand Up @@ -144,7 +143,7 @@ private static void PrintBytes(byte[] bytes)

private static void PrintArrayBytes(byte[][] bytes)
{
bytes.ForEach(e => PrintBytes(e));
bytes.ForEach(PrintBytes);
}

private static string ByteToString(byte hex)
Expand Down
155 changes: 155 additions & 0 deletions Network/RsaStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System;
using System.IO;

public class RsaStream : Stream
{
protected readonly Stream _stream;
protected RSA _rsaPrivate;
protected RSA _rsaPublic;
protected RSAParameters _publicKey;
protected RSA? _remotePublicKey;
protected ushort _protocolVersion = 1;

public RsaStream(Stream stream)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
if (!_stream.CanRead || !_stream.CanWrite)
{
throw new InvalidOperationException("Stream must be readable and writable.");
}
_rsaPrivate = RSA.Create();
_rsaPublic = RSA.Create();
_publicKey = _rsaPrivate.ExportParameters(false);
_rsaPublic.ImportParameters(_publicKey);
}

public void Handshake()
{
// Send public key
byte[] publicKeyBytes = _rsaPublic.ExportRSAPublicKey();
_stream.Write(publicKeyBytes, 0, publicKeyBytes.Length);
_stream.Flush();

// Receive remote public key
byte[] remotePublicKeyBytes = new byte[4096]; // Adjust size as needed
int bytesRead = _stream.Read(remotePublicKeyBytes, 0, remotePublicKeyBytes.Length);
if (bytesRead == 0) throw new IOException("Failed to read remote public key.");

_remotePublicKey = RSA.Create();
_remotePublicKey.ImportRSAPublicKey(remotePublicKeyBytes.AsSpan(0, bytesRead), out _);

// Test encryption/decryption
byte[] helloBytes = Encoding.UTF8.GetBytes("RSA HELLO");
byte[] encryptedHello = _remotePublicKey.Encrypt(helloBytes, RSAEncryptionPadding.OaepSHA256);
_stream.Write(encryptedHello, 0, encryptedHello.Length);
_stream.Flush();

byte[] responseBytes = new byte[4096]; // Adjust size as needed
int responseLength = _stream.Read(responseBytes, 0, responseBytes.Length);
if (responseLength == 0) throw new IOException("Failed to read response.");
byte[] decryptedResponse = _rsaPrivate.Decrypt(responseBytes.AsSpan(0, responseLength), RSAEncryptionPadding.OaepSHA256);
string decryptedResponseStr = Encoding.UTF8.GetString(decryptedResponse);
if (decryptedResponseStr != "RSA HELLO") throw new InvalidOperationException("Handshake failed.");

// Send protocol version
byte[] versionBytes = BitConverter.GetBytes(_protocolVersion);
_stream.Write(versionBytes, 0, versionBytes.Length);
_stream.Flush();

// Receive protocol version
byte[] remoteVersionBytes = new byte[2];
int versionLength = _stream.Read(remoteVersionBytes, 0, remoteVersionBytes.Length);
if (versionLength == 0) throw new IOException("Failed to read remote protocol version.");
ushort remoteVersion = BitConverter.ToUInt16(remoteVersionBytes);
if (remoteVersion != _protocolVersion) throw new InvalidOperationException("Protocol version mismatch.");
}

public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));

// The max block size depends on the key size and padding. With OAEP and SHA-256, it's approximately: KeySize/8 - 2*HashSize - 2
int maxBlockSize = _remotePublicKey!.KeySize / 8 - 2 * 32 - 2;

using (var cryptoStream = new MemoryStream())
{
for (int i = offset; i < offset + count; i += maxBlockSize)
{
int blockSize = Math.Min(maxBlockSize, count - (i - offset));
byte[] block = new byte[blockSize];
Array.Copy(buffer, i, block, 0, blockSize);

byte[] encryptedBlock = _remotePublicKey.Encrypt(block, RSAEncryptionPadding.OaepSHA256);
cryptoStream.Write(encryptedBlock, 0, encryptedBlock.Length);
}

_stream.Write(cryptoStream.ToArray(), 0, (int)cryptoStream.Length);
_stream.Flush();
}
}


public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException("Offset and count must be non-negative.");
if (buffer.Length - offset < count) throw new ArgumentException("Invalid offset and count relative to buffer length.");

using (var cryptoStream = new MemoryStream())
{
int totalBytesRead = 0;
int encryptedBlockSize = _rsaPrivate.KeySize / 8; // Each encrypted block's size should be equal to the RSA key size in bytes

byte[] encryptedBuffer = new byte[encryptedBlockSize];
int bytesRead;

while ((bytesRead = _stream.Read(encryptedBuffer, 0, encryptedBlockSize)) > 0)
{
if (bytesRead != encryptedBlockSize)
throw new CryptographicException("The length of the data to decrypt is not valid for the size of this key.");

byte[] decryptedBlock = _rsaPrivate.Decrypt(encryptedBuffer, RSAEncryptionPadding.OaepSHA256);

if (decryptedBlock.Length > 0)
{
int copySize = Math.Min(decryptedBlock.Length, count - totalBytesRead);
Array.Copy(decryptedBlock, 0, buffer, offset + totalBytesRead, copySize);
totalBytesRead += copySize;
}

if (totalBytesRead >= count)
break;
}

return totalBytesRead;
}
}

#region Stream Overrides

public override bool CanRead => _stream.CanRead;
public override bool CanSeek => _stream.CanSeek;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position
{
get => _stream.Position;
set => _stream.Position = value;
}

public override void Flush() => _stream.Flush();
public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
public override void SetLength(long value) => _stream.SetLength(value);

#endregion
}
18 changes: 18 additions & 0 deletions Network/RsaStreamStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TeraIO.Network
{
public enum RsaStreamStatus
{
NotStarted,
Handshaking,
Established,
Closed,
Failed = 100,
AuthenticationFailed = 101
}
}
2 changes: 1 addition & 1 deletion Network/WebDav/WebDavClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
using TeraIO.Runnable;
using TeraIO.Extension;

namespace TeraIO.Network.WebDav
{
Expand Down
10 changes: 0 additions & 10 deletions Runnable/EventHandler.cs

This file was deleted.

18 changes: 0 additions & 18 deletions Runnable/ExceptionEventArgs.cs

This file was deleted.

40 changes: 0 additions & 40 deletions Runnable/Extensions.cs

This file was deleted.

31 changes: 0 additions & 31 deletions Runnable/ILogger.cs

This file was deleted.

Loading

0 comments on commit 816fab5

Please sign in to comment.