mirror of
https://github.com/azaion/admin.git
synced 2026-04-22 11:16:33 +00:00
fix encryption
This commit is contained in:
@@ -18,7 +18,7 @@ public class ResourcesService(IOptions<ResourcesConfig> resourcesConfig) : IReso
|
|||||||
{
|
{
|
||||||
var fileStream = new FileStream(GetResourcePath(request.ResourceEnum), FileMode.Open, FileAccess.Read);
|
var fileStream = new FileStream(GetResourcePath(request.ResourceEnum), FileMode.Open, FileAccess.Read);
|
||||||
var key = Security.MakeEncryptionKey(request.Username, request.Password);
|
var key = Security.MakeEncryptionKey(request.Username, request.Password);
|
||||||
await fileStream.Encrypt(outputStream, key, cancellationToken);
|
await fileStream.EncryptTo(outputStream, key, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveResource(UploadResourceRequest request, CancellationToken cancellationToken = default)
|
public async Task SaveResource(UploadResourceRequest request, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Azaion.Services;
|
|||||||
|
|
||||||
public static class Security
|
public static class Security
|
||||||
{
|
{
|
||||||
private const int BUFFER_SIZE = 81920; // 80 KB buffer size
|
private const int BUFFER_SIZE = 524288; // 512 KB buffer size
|
||||||
|
|
||||||
public static string ToHash(this string str) =>
|
public static string ToHash(this string str) =>
|
||||||
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
|
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
|
||||||
@@ -13,9 +13,9 @@ public static class Security
|
|||||||
public static string MakeEncryptionKey(string username, string password) =>
|
public static string MakeEncryptionKey(string username, string password) =>
|
||||||
$"{username}-{password}---#%@AzaionKey@%#---";
|
$"{username}-{password}---#%@AzaionKey@%#---";
|
||||||
|
|
||||||
public static async Task Encrypt(this Stream stream, Stream outputStream, string key, CancellationToken cancellationToken = default)
|
public static async Task EncryptTo(this Stream stream, Stream toStream, string key, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (stream is { CanSeek: false }) throw new ArgumentNullException(nameof(stream));
|
if (stream is { CanRead: false }) throw new ArgumentNullException(nameof(stream));
|
||||||
if (key is not { Length: > 0 }) throw new ArgumentNullException(nameof(key));
|
if (key is not { Length: > 0 }) throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
using var aes = Aes.Create();
|
using var aes = Aes.Create();
|
||||||
@@ -23,10 +23,10 @@ public static class Security
|
|||||||
aes.GenerateIV();
|
aes.GenerateIV();
|
||||||
|
|
||||||
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||||
await using var cs = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
|
await using var cs = new CryptoStream(toStream, encryptor, CryptoStreamMode.Write, leaveOpen: true);
|
||||||
|
|
||||||
// Prepend IV to the encrypted data
|
// Prepend IV to the encrypted data
|
||||||
await outputStream.WriteAsync(aes.IV.AsMemory(0, aes.IV.Length), cancellationToken);
|
await toStream.WriteAsync(aes.IV.AsMemory(0, aes.IV.Length), cancellationToken);
|
||||||
|
|
||||||
var buffer = new byte[BUFFER_SIZE];
|
var buffer = new byte[BUFFER_SIZE];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
@@ -34,7 +34,7 @@ public static class Security
|
|||||||
await cs.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
await cs.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Decrypt(this Stream encryptedStream, Stream outputStream, string key, CancellationToken cancellationToken = default)
|
public static async Task DecryptTo(this Stream encryptedStream, Stream toStream, string key, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var aes = Aes.Create();
|
using var aes = Aes.Create();
|
||||||
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
|
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
|
||||||
@@ -45,12 +45,12 @@ public static class Security
|
|||||||
aes.IV = iv;
|
aes.IV = iv;
|
||||||
|
|
||||||
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||||
await using var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read);
|
await using var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read, leaveOpen: true);
|
||||||
|
|
||||||
// Read and write in chunks
|
// Read and write in chunks
|
||||||
var buffer = new byte[BUFFER_SIZE];
|
var buffer = new byte[BUFFER_SIZE];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
while ((bytesRead = await cryptoStream.ReadAsync(buffer, cancellationToken)) > 0)
|
while ((bytesRead = await cryptoStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||||
await outputStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
await toStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System.Text;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using Azaion.Services;
|
using Azaion.Services;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Azaion.Test;
|
namespace Azaion.Test;
|
||||||
@@ -15,16 +17,76 @@ public class SecurityTest
|
|||||||
var password = "testpw";
|
var password = "testpw";
|
||||||
var key = Security.MakeEncryptionKey(username, password);
|
var key = Security.MakeEncryptionKey(username, password);
|
||||||
|
|
||||||
await using var encryptedStream = new MemoryStream();
|
var encryptedStream = new MemoryStream();
|
||||||
await StringToStream(testString).Encrypt(encryptedStream, key);
|
await StringToStream(testString).EncryptTo(encryptedStream, key);
|
||||||
|
encryptedStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
await using var decryptedStream = new MemoryStream();
|
await using var decryptedStream = new MemoryStream();
|
||||||
await encryptedStream.Decrypt(decryptedStream, key);
|
await encryptedStream.DecryptTo(decryptedStream, key);
|
||||||
|
encryptedStream.Close();
|
||||||
|
|
||||||
var str = StreamToString(decryptedStream);
|
var str = StreamToString(decryptedStream);
|
||||||
str.Should().Be(testString);
|
str.Should().Be(testString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EncryptDecryptLargeFileTest()
|
||||||
|
{
|
||||||
|
var username = "user@azaion.com";
|
||||||
|
var password = "testpw";
|
||||||
|
var key = Security.MakeEncryptionKey(username, password);
|
||||||
|
|
||||||
|
var largeFilePath = "large.txt";
|
||||||
|
var largeFileDecryptedPath = "large_decrypted.txt";
|
||||||
|
|
||||||
|
var stream = await CreateLargeFile(largeFilePath);
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var encryptedStream = new MemoryStream();
|
||||||
|
|
||||||
|
await stream.EncryptTo(encryptedStream, key);
|
||||||
|
encryptedStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
File.Delete(largeFileDecryptedPath);
|
||||||
|
await using var decryptedStream = new FileStream(largeFileDecryptedPath, FileMode.OpenOrCreate, FileAccess.Write);
|
||||||
|
await encryptedStream.DecryptTo(decryptedStream, key);
|
||||||
|
|
||||||
|
encryptedStream.Close();
|
||||||
|
stream.Close();
|
||||||
|
decryptedStream.Close();
|
||||||
|
|
||||||
|
await CompareFiles(largeFilePath, largeFileDecryptedPath);
|
||||||
|
File.Delete(largeFilePath);
|
||||||
|
File.Delete(largeFileDecryptedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CompareFiles(string largeFilePath, string largeFileDecryptedPath)
|
||||||
|
{
|
||||||
|
await using var stream1 = new FileStream(largeFilePath, FileMode.Open, FileAccess.Read);
|
||||||
|
await using var stream2 = new FileStream(largeFileDecryptedPath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
var sha256Bytes1 = Encoding.UTF8.GetString(await SHA256.HashDataAsync(stream1));
|
||||||
|
var sha256Bytes2 = Encoding.UTF8.GetString(await SHA256.HashDataAsync(stream2));
|
||||||
|
sha256Bytes1.Should().Be(sha256Bytes2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Stream> CreateLargeFile(string largeTxtPath)
|
||||||
|
{
|
||||||
|
var max = 4000000;
|
||||||
|
File.Delete(largeTxtPath);
|
||||||
|
var stream = new FileStream(largeTxtPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||||
|
var numbersList = Enumerable.Range(1, max).Chunk(100000);
|
||||||
|
foreach (var numbers in numbersList)
|
||||||
|
{
|
||||||
|
var dict = numbers.ToDictionary(x => x, _ => DateTime.UtcNow);
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dict, Formatting.Indented));
|
||||||
|
await stream.WriteAsync(bytes);
|
||||||
|
Console.WriteLine($"Writing numbers from {(numbers.FirstOrDefault()*100 / (double)max):F1} %");
|
||||||
|
}
|
||||||
|
await stream.FlushAsync();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
private static string StreamToString(Stream stream)
|
private static string StreamToString(Stream stream)
|
||||||
{
|
{
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user