From a2e99cb263588f6cdf81c6d18fe08dd9f431acc9 Mon Sep 17 00:00:00 2001 From: Alexandru Macocian Date: Wed, 14 Jul 2021 12:17:31 +0300 Subject: [PATCH] SystemExtensions Security --- .github/workflows/cd.yaml | 9 + .github/workflows/ci.yaml | 4 + .../AesEncrypterTests.cs | 105 +++++++++ .../Sha256HashingServiceTests.cs | 48 ++++ ...tensions.NetStandard.Security.Tests.csproj | 21 ++ .../Encryption/AesEncrypter.cs | 215 ++++++++++++++++++ .../Encryption/ISymmetricEncrypter.cs | 25 ++ .../Hashing/IHashingService.cs | 12 + .../Hashing/Sha256HashingService.cs | 60 +++++ SystemExtensions.NetStandard.Security/LICENSE | 21 ++ ...stemExtensions.NetStandard.Security.csproj | 22 ++ .../Utilities/NotClosingCryptoStream.cs | 21 ++ SystemExtensions.sln | 12 + 13 files changed, 575 insertions(+) create mode 100644 SystemExtensions.NetStandard.Security.Tests/AesEncrypterTests.cs create mode 100644 SystemExtensions.NetStandard.Security.Tests/Sha256HashingServiceTests.cs create mode 100644 SystemExtensions.NetStandard.Security.Tests/SystemExtensions.NetStandard.Security.Tests.csproj create mode 100644 SystemExtensions.NetStandard.Security/Encryption/AesEncrypter.cs create mode 100644 SystemExtensions.NetStandard.Security/Encryption/ISymmetricEncrypter.cs create mode 100644 SystemExtensions.NetStandard.Security/Hashing/IHashingService.cs create mode 100644 SystemExtensions.NetStandard.Security/Hashing/Sha256HashingService.cs create mode 100644 SystemExtensions.NetStandard.Security/LICENSE create mode 100644 SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj create mode 100644 SystemExtensions.NetStandard.Security/Utilities/NotClosingCryptoStream.cs diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 8844e26..52692e3 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -48,6 +48,9 @@ jobs: - name: Build SystemExtensions.NetStandard.DependencyInjection project run: dotnet build SystemExtensions.NetStandard.DependencyInjection -c $env:Configuration + - name: Build SystemExtensions.NetStandard.Security project + run: dotnet build SystemExtensions.NetStandard.Security -c $env:Configuration + - name: Push SystemExtensions.NetStandard nuget package uses: brandedoutcast/publish-nuget@v2.5.5 with: @@ -58,4 +61,10 @@ jobs: uses: brandedoutcast/publish-nuget@v2.5.5 with: PROJECT_FILE_PATH: SystemExtensions.NetStandard.DependencyInjection\SystemExtensions.NetStandard.DependencyInjection.csproj + NUGET_KEY: ${{secrets.NUGET_API_KEY}} + + - name: Push SystemExtensions.NetStandard.Security nuget package + uses: brandedoutcast/publish-nuget@v2.5.5 + with: + PROJECT_FILE_PATH: SystemExtensions.NetStandard.Security\SystemExtensions.NetStandard.Security.csproj NUGET_KEY: ${{secrets.NUGET_API_KEY}} \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6282ce8..db55318 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,6 +22,7 @@ jobs: Solution_Path: SystemExtensions.sln Test_Project_Path: SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj DI_Test_Project_Path: SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj + Sec_Test_Project_Path: SystemExtensions.NetStandard.Security.Tests\SystemExtensions.NetStandard.Security.Tests.csproj Source_Project_Path: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj Actions_Allow_Unsecure_Commands: true @@ -45,6 +46,9 @@ jobs: - name: Execute DI Unit Tests run: dotnet test $env:DI_Test_Project_Path + - name: Execute Security Unit Tests + run: dotnet test $env:Sec_Test_Project_Path + - name: Restore Project run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier env: diff --git a/SystemExtensions.NetStandard.Security.Tests/AesEncrypterTests.cs b/SystemExtensions.NetStandard.Security.Tests/AesEncrypterTests.cs new file mode 100644 index 0000000..943cdab --- /dev/null +++ b/SystemExtensions.NetStandard.Security.Tests/AesEncrypterTests.cs @@ -0,0 +1,105 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using SystemExtensions.NetStandard.Security.Encryption; + +namespace VaultO.Tests +{ + [TestClass] + public class AesEncrypterTests + { + private ISymmetricEncrypter symmetricEncrypter; + private string keyString; + private byte[] keyBytes; + private byte[] ivBytes; + private string ivString; + private string toEncryptString; + private byte[] toEncryptBytes; + + [TestInitialize] + public void TestInitialize() + { + this.symmetricEncrypter = new AesEncrypter(); + this.keyBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 }; + this.keyString = Convert.ToBase64String(this.keyBytes); + this.ivBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 }; + this.ivString = Convert.ToBase64String(this.ivBytes); + this.toEncryptBytes = Encoding.UTF8.GetBytes("toEncrypt"); + this.toEncryptString = Convert.ToBase64String(this.toEncryptBytes); + } + + [TestMethod] + public async Task EncryptDecryptStringWithStringKeyStringIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, this.toEncryptString).ConfigureAwait(false); + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false); + + this.toEncryptString.Should().Be(decrypted); + } + + [TestMethod] + public async Task EncryptDecryptStringWithByteKeyByteIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, this.toEncryptString).ConfigureAwait(false); + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false); + + this.toEncryptString.Should().Be(decrypted); + } + + [TestMethod] + public async Task EncryptDecryptByteWithStringKeyStringIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, this.toEncryptBytes).ConfigureAwait(false); + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false); + + this.toEncryptBytes.Should().BeEquivalentTo(decrypted); + } + + [TestMethod] + public async Task EncryptDecryptByteWithByteKeyByteIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, this.toEncryptBytes).ConfigureAwait(false); + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false); + + this.toEncryptBytes.Should().BeEquivalentTo(decrypted); + } + + [TestMethod] + public async Task EncryptDecryptStreamWithStringKeyStringIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, new MemoryStream(this.toEncryptBytes)).ConfigureAwait(false); + encrypted.Position = 0; + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false); + + this.toEncryptBytes.Should().BeEquivalentTo(((MemoryStream)decrypted).ToArray()); + } + + [TestMethod] + public async Task EncryptDecryptStreamWithByteKeyByteIv() + { + var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, new MemoryStream(this.toEncryptBytes)).ConfigureAwait(false); + encrypted.Position = 0; + var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false); + + this.toEncryptBytes.Should().BeEquivalentTo(((MemoryStream)decrypted).ToArray()); + } + + [TestMethod] + public void GetEncryptionStreamReturnsCryptoStream() + { + var encryptionStream = this.symmetricEncrypter.GetEncryptionStream(this.keyBytes, this.ivBytes, new MemoryStream()); + encryptionStream.Should().BeAssignableTo(); + } + + [TestMethod] + public void GetDecryptionStreamReturnsCryptoStream() + { + var encryptionStream = this.symmetricEncrypter.GetDecryptionStream(this.keyBytes, this.ivBytes, new MemoryStream()); + encryptionStream.Should().BeAssignableTo(); + } + } +} diff --git a/SystemExtensions.NetStandard.Security.Tests/Sha256HashingServiceTests.cs b/SystemExtensions.NetStandard.Security.Tests/Sha256HashingServiceTests.cs new file mode 100644 index 0000000..4d593cf --- /dev/null +++ b/SystemExtensions.NetStandard.Security.Tests/Sha256HashingServiceTests.cs @@ -0,0 +1,48 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using SystemExtensions.NetStandard.Security.Hashing; + +namespace VaultO.Tests +{ + [TestClass] + public sealed class Sha256HashingServiceTests + { + private readonly IHashingService hashingService = new Sha256HashingService(); + private string toHashtString; + private byte[] toHashtBytes; + private Stream toHashStream; + + [TestInitialize] + public void TestInitialize() + { + this.toHashtBytes = Encoding.UTF8.GetBytes("toEncrypt"); + this.toHashtString = Convert.ToBase64String(this.toHashtBytes); + this.toHashStream = new MemoryStream(this.toHashtBytes); + } + + [TestMethod] + public async Task HashString() + { + var hash = await this.hashingService.Hash(this.toHashtString).ConfigureAwait(false); + hash.Should().BeOfType(); + } + + [TestMethod] + public async Task HashBytes() + { + var hash = await this.hashingService.Hash(this.toHashtBytes).ConfigureAwait(false); + hash.Should().BeOfType(); + } + + [TestMethod] + public async Task HashStream() + { + var hash = await this.hashingService.Hash(this.toHashStream).ConfigureAwait(false); + hash.Should().BeAssignableTo(); + } + } +} diff --git a/SystemExtensions.NetStandard.Security.Tests/SystemExtensions.NetStandard.Security.Tests.csproj b/SystemExtensions.NetStandard.Security.Tests/SystemExtensions.NetStandard.Security.Tests.csproj new file mode 100644 index 0000000..a9150ac --- /dev/null +++ b/SystemExtensions.NetStandard.Security.Tests/SystemExtensions.NetStandard.Security.Tests.csproj @@ -0,0 +1,21 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + diff --git a/SystemExtensions.NetStandard.Security/Encryption/AesEncrypter.cs b/SystemExtensions.NetStandard.Security/Encryption/AesEncrypter.cs new file mode 100644 index 0000000..95db137 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/Encryption/AesEncrypter.cs @@ -0,0 +1,215 @@ +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading.Tasks; +using SystemExtensions.NetStandard.Security.Utilities; + +namespace SystemExtensions.NetStandard.Security.Encryption +{ + public sealed class AesEncrypter : ISymmetricEncrypter + { + public async Task Decrypt(string key, string iv, string value) + { + using var valueStream = BytesToStream(StringToBytes(value)); + using var decryptedStream = await DecryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false); + return StreamToString(decryptedStream); + } + + public async Task Decrypt(byte[] key, byte[] iv, string value) + { + using var valueStream = BytesToStream(StringToBytes(value)); + using var decryptedStream = await DecryptInternal(key, iv, valueStream).ConfigureAwait(false); + return StreamToString(decryptedStream); + } + + public async Task Decrypt(string key, string iv, byte[] value) + { + using var valueStream = BytesToStream(value); + using var decryptedStream = await DecryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false); + return StreamToBytes(decryptedStream); + } + + public async Task Decrypt(byte[] key, byte[] iv, byte[] value) + { + using var valueStream = BytesToStream(value); + using var decryptedStream = await DecryptInternal(key, iv, valueStream).ConfigureAwait(false); + return StreamToBytes(decryptedStream); + } + + public async Task Decrypt(string key, string iv, Stream value) + { + return await DecryptInternal(StringToBytes(key), StringToBytes(iv), value).ConfigureAwait(false); + } + + public async Task Decrypt(byte[] key, byte[] iv, Stream value) + { + return await DecryptInternal(key, iv, value).ConfigureAwait(false); + } + + public async Task Encrypt(string key, string iv, string value) + { + using var valueStream = BytesToStream(StringToBytes(value)); + using var encryptedStream = await EncryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false); + return StreamToString(encryptedStream); + } + + public async Task Encrypt(byte[] key, byte[] iv, string value) + { + using var valueStream = BytesToStream(StringToBytes(value)); + using var encryptedStream = await EncryptInternal(key, iv, valueStream).ConfigureAwait(false); + return StreamToString(encryptedStream); + } + + public async Task Encrypt(string key, string iv, byte[] value) + { + using var valueStream = BytesToStream(value); + using var encryptedStream = await EncryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false); + return StreamToBytes(encryptedStream); + } + + public async Task Encrypt(byte[] key, byte[] iv, byte[] value) + { + using var valueStream = BytesToStream(value); + using var encryptedStream = await EncryptInternal(key, iv, valueStream).ConfigureAwait(false); + return StreamToBytes(encryptedStream); + } + + public async Task Encrypt(string key, string iv, Stream value) + { + if (value is null) throw new ArgumentNullException(nameof(value)); + + return await EncryptInternal(StringToBytes(key), StringToBytes(iv), value).ConfigureAwait(false); + } + + public async Task Encrypt(byte[] key, byte[] iv, Stream value) + { + if (value is null) throw new ArgumentNullException(nameof(value)); + + return await EncryptInternal(key, iv, value).ConfigureAwait(false); + } + + public Stream GetEncryptionStream(string key, string iv, Stream outStream) + { + return GetEncryptionStreamInternal(StringToBytes(key), StringToBytes(iv), outStream); + } + + public Stream GetEncryptionStream(byte[] key, byte[] iv, Stream outStream) + { + return GetEncryptionStreamInternal(key, iv, outStream); + } + + public Stream GetDecryptionStream(string key, string iv, Stream inStream) + { + return GetDecryptionStreamInternal(StringToBytes(key), StringToBytes(iv), inStream); + } + + public Stream GetDecryptionStream(byte[] key, byte[] iv, Stream inStream) + { + return GetDecryptionStreamInternal(key, iv, inStream); + } + + private static byte[] StreamToBytes(Stream value) + { + value.Position = 0; + byte[] buffer = new byte[1024]; + using var ms = new MemoryStream(); + int read = -1; + do + { + read = value.Read(buffer, 0, buffer.Length); + ms.Write(buffer, 0, read); + } while (read > 0); + return ms.ToArray(); + } + + private static string StreamToString(Stream value) + { + return Convert.ToBase64String(StreamToBytes(value)); + } + + private static byte[] StringToBytes(string value) + { + return Convert.FromBase64String(value); + } + + private static Stream BytesToStream(byte[] value) + { + return new MemoryStream(value); + } + + private static async Task EncryptInternal(byte[] key, byte[] iv, Stream toEncryptStream) + { + if (key.Length < 32) + { + throw new ArgumentException($"{nameof(key)} must be at least 32 bytes"); + } + + if (iv.Length < 16) + { + throw new ArgumentException($"{nameof(iv)} must be at least 16 bytes"); + } + + key = key.Take(32).ToArray(); + iv = iv.Take(16).ToArray(); + + + var encryptedMemoryStream = new MemoryStream(); + using var cryptoStream = GetEncryptionStreamInternal(key, iv, encryptedMemoryStream); + await toEncryptStream.CopyToAsync(cryptoStream).ConfigureAwait(false); + if (!cryptoStream.HasFlushedFinalBlock) + { + cryptoStream.FlushFinalBlock(); + } + + encryptedMemoryStream.Position = 0; + return encryptedMemoryStream; + } + + private static async Task DecryptInternal(byte[] key, byte[] iv, Stream toDecryptStream) + { + if (key.Length < 32) + { + throw new ArgumentException($"{nameof(key)} must be at least 32 bytes"); + } + + if (iv.Length < 16) + { + throw new ArgumentException($"{nameof(iv)} must be at least 16 bytes"); + } + + key = key.Take(32).ToArray(); + iv = iv.Take(16).ToArray(); + + + var decryptedMemoryStream = new MemoryStream(); + using var cryptoStream = GetDecryptionStreamInternal(key, iv, toDecryptStream); + await cryptoStream.CopyToAsync(decryptedMemoryStream).ConfigureAwait(false); + if (!cryptoStream.HasFlushedFinalBlock) + { + cryptoStream.FlushFinalBlock(); + } + + decryptedMemoryStream.Position = 0; + return decryptedMemoryStream; + } + + private static CryptoStream GetEncryptionStreamInternal(byte[] key, byte[] iv, Stream encryptedStream) + { + using var aes = Aes.Create(); + aes.Padding = PaddingMode.PKCS7; + var crypto = aes.CreateEncryptor(key, iv); + var cryptoStream = new NotClosingCryptoStream(encryptedStream, crypto, CryptoStreamMode.Write); + return cryptoStream; + } + + private static CryptoStream GetDecryptionStreamInternal(byte[] key, byte[] iv, Stream encryptedStream) + { + using var aes = Aes.Create(); + aes.Padding = PaddingMode.PKCS7; + var crypto = aes.CreateDecryptor(key, iv); + var cryptoStream = new NotClosingCryptoStream(encryptedStream, crypto, CryptoStreamMode.Read); + return cryptoStream; + } + } +} diff --git a/SystemExtensions.NetStandard.Security/Encryption/ISymmetricEncrypter.cs b/SystemExtensions.NetStandard.Security/Encryption/ISymmetricEncrypter.cs new file mode 100644 index 0000000..aa22f92 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/Encryption/ISymmetricEncrypter.cs @@ -0,0 +1,25 @@ +using System.IO; +using System.Threading.Tasks; + +namespace SystemExtensions.NetStandard.Security.Encryption +{ + public interface ISymmetricEncrypter + { + Stream GetEncryptionStream(string key, string iv, Stream outStream); + Stream GetEncryptionStream(byte[] key, byte[] iv, Stream outStream); + Stream GetDecryptionStream(string key, string iv, Stream inStream); + Stream GetDecryptionStream(byte[] key, byte[] iv, Stream inStream); + Task Encrypt(string key, string iv, string value); + Task Encrypt(byte[] key, byte[] iv, string value); + Task Encrypt(string key, string iv, byte[] value); + Task Encrypt(byte[] key, byte[] iv, byte[] value); + Task Encrypt(string key, string iv, Stream value); + Task Encrypt(byte[] key, byte[] iv, Stream value); + Task Decrypt(string key, string iv, string value); + Task Decrypt(byte[] key, byte[] iv, string value); + Task Decrypt(string key, string iv, byte[] value); + Task Decrypt(byte[] key, byte[] iv, byte[] value); + Task Decrypt(string key, string iv, Stream value); + Task Decrypt(byte[] key, byte[] iv, Stream value); + } +} diff --git a/SystemExtensions.NetStandard.Security/Hashing/IHashingService.cs b/SystemExtensions.NetStandard.Security/Hashing/IHashingService.cs new file mode 100644 index 0000000..08637e8 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/Hashing/IHashingService.cs @@ -0,0 +1,12 @@ +using System.IO; +using System.Threading.Tasks; + +namespace SystemExtensions.NetStandard.Security.Hashing +{ + public interface IHashingService + { + Task Hash(string raw); + Task Hash(byte[] raw); + Task Hash(Stream raw); + } +} diff --git a/SystemExtensions.NetStandard.Security/Hashing/Sha256HashingService.cs b/SystemExtensions.NetStandard.Security/Hashing/Sha256HashingService.cs new file mode 100644 index 0000000..832c417 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/Hashing/Sha256HashingService.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace SystemExtensions.NetStandard.Security.Hashing +{ + public sealed class Sha256HashingService : IHashingService + { + public async Task Hash(string raw) + { + using var rawStream = BytesToStream(StringToBytes(raw)); + using var hashedStream = await HashInternal(rawStream).ConfigureAwait(false); + return StreamToString(hashedStream); + } + public async Task Hash(byte[] raw) + { + using var rawStream = BytesToStream(raw); + using var hashedStream = await HashInternal(rawStream).ConfigureAwait(false); + return StreamToBytes(hashedStream); + } + public async Task Hash(Stream raw) + { + return await HashInternal(raw); + } + + private static byte[] StreamToBytes(Stream value) + { + value.Position = 0; + byte[] buffer = new byte[1024]; + using var ms = new MemoryStream(); + int read = -1; + do + { + read = value.Read(buffer, 0, buffer.Length); + ms.Write(buffer, 0, read); + } while (read > 0); + return ms.ToArray(); + } + private static string StreamToString(Stream value) + { + return Convert.ToBase64String(StreamToBytes(value)); + } + private static byte[] StringToBytes(string value) + { + return Convert.FromBase64String(value); + } + private static Stream BytesToStream(byte[] value) + { + return new MemoryStream(value); + } + private static async Task HashInternal(Stream raw) + { + if (raw is null) throw new ArgumentNullException(nameof(raw)); + + using var sha256 = SHA256.Create(); + return await Task.Run(() => new MemoryStream(sha256.ComputeHash(raw))); + } + } +} diff --git a/SystemExtensions.NetStandard.Security/LICENSE b/SystemExtensions.NetStandard.Security/LICENSE new file mode 100644 index 0000000..7b4b612 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Macocian Alexandru Victor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj b/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj new file mode 100644 index 0000000..d723ff9 --- /dev/null +++ b/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + latest + true + LICENSE + true + System + 1.0.0 + Alexandru Macocian + https://github.com/AlexMacocian/SystemExtensions + + + + + True + + + + + \ No newline at end of file diff --git a/SystemExtensions.NetStandard.Security/Utilities/NotClosingCryptoStream.cs b/SystemExtensions.NetStandard.Security/Utilities/NotClosingCryptoStream.cs new file mode 100644 index 0000000..f1d768a --- /dev/null +++ b/SystemExtensions.NetStandard.Security/Utilities/NotClosingCryptoStream.cs @@ -0,0 +1,21 @@ +using System.IO; +using System.Security.Cryptography; + +namespace SystemExtensions.NetStandard.Security.Utilities +{ + internal sealed class NotClosingCryptoStream : CryptoStream + { + public NotClosingCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode) + : base(stream, transform, mode) + { + } + + protected override void Dispose(bool disposing) + { + if (!this.HasFlushedFinalBlock) + this.FlushFinalBlock(); + + base.Dispose(false); + } + } +} diff --git a/SystemExtensions.sln b/SystemExtensions.sln index c5a0502..27d2ea5 100644 --- a/SystemExtensions.sln +++ b/SystemExtensions.sln @@ -14,6 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandard.DependencyInjection.Tests", "SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj", "{1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemExtensions.NetStandard.Security", "SystemExtensions.NetStandard.Security\SystemExtensions.NetStandard.Security.csproj", "{BFC5BEE7-2569-45EA-9713-CDB32EED57AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemExtensions.NetStandard.Security.Tests", "SystemExtensions.NetStandard.Security.Tests\SystemExtensions.NetStandard.Security.Tests.csproj", "{341ECE0F-A8DD-49A0-AE3D-B28D72E85130}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,6 +40,14 @@ Global {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Debug|Any CPU.Build.0 = Debug|Any CPU {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Release|Any CPU.Build.0 = Release|Any CPU + {BFC5BEE7-2569-45EA-9713-CDB32EED57AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFC5BEE7-2569-45EA-9713-CDB32EED57AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFC5BEE7-2569-45EA-9713-CDB32EED57AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFC5BEE7-2569-45EA-9713-CDB32EED57AA}.Release|Any CPU.Build.0 = Release|Any CPU + {341ECE0F-A8DD-49A0-AE3D-B28D72E85130}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {341ECE0F-A8DD-49A0-AE3D-B28D72E85130}.Debug|Any CPU.Build.0 = Debug|Any CPU + {341ECE0F-A8DD-49A0-AE3D-B28D72E85130}.Release|Any CPU.ActiveCfg = Release|Any CPU + {341ECE0F-A8DD-49A0-AE3D-B28D72E85130}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE