Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Commit

Permalink
Implement Non-windows RSA Algorithm (CSC-2) (#3)
Browse files Browse the repository at this point in the history
* Common base SecurityKey class

* Dependency Injection Adjustments

* Switch to BouncyCastle RSA Implementation

* Switch build agent to non-windows

* Update project file
  • Loading branch information
BlankDev117 authored Aug 27, 2023
1 parent 1f04437 commit ebf60a2
Show file tree
Hide file tree
Showing 23 changed files with 325 additions and 243 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ concurrency:
jobs:

build:
runs-on: windows-latest
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [ '6.0.x' ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public class IntegrationTests
#region End to End

[Fact]
public async Task EndToEnd_Testing()
public async Task EndToEnd_SecurityKeyExchange_Testing()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddCryptography();
var serviceProvider = serviceCollection.BuildServiceProvider();

var cryptographyService = serviceProvider.GetRequiredService<ICryptographyService>();
var personalKey = cryptographyService.CreateKey(1024, new RsaKeyGenerationParameters());
var otherKey = cryptographyService.CreateKey(1024, new RsaKeyGenerationParameters());
var personalKey = cryptographyService.CreateKey(512, new RsaKeyGenerationParameters());
var otherKey = cryptographyService.CreateKey(512, new RsaKeyGenerationParameters());

var personalExchangeKeyInformation = personalKey.KeyInformation.GetKeyExhangeInformation() as RsaKeyExchangeInformation;
var otherExchangeKeyInformation = otherKey.KeyInformation.GetKeyExhangeInformation() as RsaKeyExchangeInformation;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using Common.Security.Cryptography.Exceptions;
using Common.Security.Cryptography.Keys.Aes.Internal.Services;
using Common.Security.Cryptography.Keys.Aes.Models;
using Common.Security.Cryptography.Ports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Common.Security.Cryptography.UnitTests.Keys.Aes.Internal
Expand Down Expand Up @@ -52,10 +51,15 @@ public void Generate_GenerationParameters_InvalidKeySize_ThrowsKeySizeException(
public void Generate_GenerationParameters_GeneratesNewKey()
{
// Arrange/Act
using var key = _generator.GenerateKey(128, new AesKeyGenerationParameters());

var keys = new List<long>();
for (var i = 0; i < 100; i++)
{
using var key = _generator.GenerateKey(128, new AesKeyGenerationParameters());
keys.Add(BitConverter.ToInt64(key.KeyInformation.RawKey));
}

// Assert
Assert.NotNull(key);
Assert.Equal(keys.Count, keys.Distinct().Count());
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
using Common.Security.Cryptography.Exceptions;
using Common.Security.Cryptography.Keys.Aes.Internal.Services;
using Common.Security.Cryptography.Keys.Aes.Models;
using Common.Security.Cryptography.Keys.Rsa.Internal.Services;
using Common.Security.Cryptography.Keys.Aes.Internal.Services;
using Common.Security.Cryptography.Ports;
using Common.Security.Cryptography.SecurityKeys.Aes.Internal.Services;
using Common.Security.Cryptography.SecurityKeys.Aes.Models;
using System;
using System.Security.Cryptography;
using Xunit;

namespace Common.Security.Cryptography.UnitTests.SecurityKeys.Aes.Internal
namespace Common.Security.Cryptography.UnitTests.Keys.Aes.Internal
{
public class AesSecurityKeyTests: SecurityKeyBaseTests
public class AesSecurityKeyTests : SecurityKeyBaseTests
{
#region SecurityKeyBaseTests Overrides

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
using Common.Security.Cryptography.Exceptions;
using Common.Security.Cryptography.Keys.Aes.Internal.Services;
using Common.Security.Cryptography.Keys.Aes.Models;
using Common.Security.Cryptography.Keys.Rsa.Internal.Services;
using Common.Security.Cryptography.Keys.Rsa.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Common.Security.Cryptography.UnitTests.Keys.Rsa.Internal
Expand Down Expand Up @@ -54,10 +51,15 @@ public void Generate_GenerationParameters_InvalidKeySize_ThrowsKeySizeException(
public void Generate_GenerationParameters_GeneratesNewKey()
{
// Arrange/Act
using var key = _generator.GenerateKey(1024, new RsaKeyGenerationParameters());
var keys = new List<long>();
for (var i = 0; i < 1; i++)
{
using var key = _generator.GenerateKey(128, new RsaKeyGenerationParameters());
keys.Add(BitConverter.ToInt64(key.KeyInformation.RawKey));
}

// Assert
Assert.NotNull(key);
Assert.Equal(keys.Count, keys.Distinct().Count());
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
using Common.Security.Cryptography.Exceptions;
using Common.Security.Cryptography.Keys.Rsa.Internal.Services;
using Common.Security.Cryptography.Keys.Rsa.Models;
using Common.Security.Cryptography.Keys.Rsa.Internal.Services;
using Common.Security.Cryptography.Ports;
using Common.Security.Cryptography.SecurityKeys.Aes.Internal.Services;
using Common.Security.Cryptography.SecurityKeys.Aes.Models;
using Common.Security.Cryptography.SecurityKeys.Rsa.Internal.Services;
using Common.Security.Cryptography.SecurityKeys.Rsa.Models;
using System;
using System.Security.Cryptography;
using Xunit;

namespace Common.Security.Cryptography.UnitTests.SecurityKeys.Rsa.Internal
namespace Common.Security.Cryptography.UnitTests.Keys.Rsa.Internal
{
public class RsaSecurityKeyTests: SecurityKeyBaseTests
public class RsaSecurityKeyTests : SecurityKeyBaseTests
{
#region SecurityKeyBaseTests Overrides

protected override ISecurityKey GetSecurityKey()
=> RsaKeyGenerator.GenerateKey(1024);
=> RsaKeyGenerator.GenerateKey(128);

#endregion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Threading.Tasks;
using Xunit;

namespace Common.Security.Cryptography.UnitTests.SecurityKeys
namespace Common.Security.Cryptography.UnitTests.Keys
{
public abstract class SecurityKeyBaseTests
{
Expand Down Expand Up @@ -121,17 +121,21 @@ public async Task ValidateSignatureAsync_NullSignature_ThrowsArgumentNullExcepti
await Assert.ThrowsAsync<ArgumentNullException>(() => key.ValidateSignatureAsync(new byte[0], null, HashAlgorithmName.SHA256));
}

[Fact]
public async Task ValidateSignatureAsync_InvalidSignature_ReturnsFalse()
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task ValidateSignatureAsync_InvalidSignature_ReturnsFalse(bool wrongHash)
{
// Arrange
var key = GetSecurityKey();

var data = Encoding.UTF8.GetBytes("A day in the life of a unit test.");
var signedData = await key.SignAsync(data, HashAlgorithmName.SHA512);
var signedData = await key.SignAsync(data, HashAlgorithmName.SHA256);

// Act
var validationResult = await key.ValidateSignatureAsync(data, signedData, HashAlgorithmName.SHA256);
var validationResult = await key.ValidateSignatureAsync(data,
wrongHash ? signedData : new byte[0],
wrongHash ? HashAlgorithmName.SHA512 : HashAlgorithmName.SHA256);

// Assert
Assert.False(validationResult);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
<TargetFramework>netstandard2.1</TargetFramework>
<RepositoryUrl>https://github.com/BlankDev117/Common.Security.Cryptography.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Description>Easy to use and flexible cryptographic library for .NET. </Description>
<Description>Easy to use, flexible, and machine agnostic cryptographic library for .NET. </Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>Security, Cryptography, Encryption, Decryption, Sign, Validate, Crypto, Symmetric, Asymmetric, Keys</PackageTags>
<PackageTags>Security, Cryptography, Encryption, Decryption, Sign, Validate, Crypto, Symmetric, Asymmetric, Keys, AES, RSA</PackageTags>
<Authors>BlankDev117</Authors>
<Title>Common.Security.Cryptography</Title>
</PropertyGroup>
Expand All @@ -17,6 +17,7 @@

<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
using Common.Security.Cryptography.Keys.Aes.Models;
using Common.Security.Cryptography.Ports;
using Common.Security.Cryptography.SecurityKeys.Aes.Internal.Services;
using Common.Security.Cryptography.SecurityKeys.Aes.Models;
using System;

namespace Common.Security.Cryptography.Keys.Aes.Internal.Services
{
internal class AesKeyGenerator : SecurityKeyGenerator<AesKeyGenerationParameters, AesKeyInformation, AesKeyExchangeInformation>
{
#region Static

internal static ISecurityKey GenerateKey(int keySize)
{
return new AesKeyGenerator().GenerateKey(keySize, new AesKeyGenerationParameters());
}

#endregion

#region SecurityKeyGenerator Overrides

protected override ISecurityKey GenerateKey(int keySize, AesKeyGenerationParameters keyGenerationParameters)
{
if (keyGenerationParameters == null)
Expand All @@ -26,6 +35,14 @@ protected override ISecurityKey GenerateKey(int keySize, AesKeyGenerationParamet

protected override ISecurityKey GenerateKey(byte[] key, AesKeyExchangeInformation keyExchangeInformation)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (keyExchangeInformation == null)
{
throw new ArgumentNullException(nameof(keyExchangeInformation));
}
if (keyExchangeInformation.IV == null)
{
throw new ArgumentNullException(nameof(keyExchangeInformation.IV));
Expand All @@ -40,9 +57,6 @@ protected override ISecurityKey GenerateKey(AesKeyInformation keyInformation)
return new AesSecurityKey(keyInformation);
}

internal static ISecurityKey GenerateKey(int keySize)
{
return new AesKeyGenerator().GenerateKey(keySize, new AesKeyGenerationParameters());
}
#endregion
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,38 @@
using Common.Security.Cryptography.Model;
using Common.Security.Cryptography.Ports;
using Common.Security.Cryptography.SecurityKeys.Aes.Models;
using Common.Security.Cryptography.Keys.Aes.Models;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;

namespace Common.Security.Cryptography.SecurityKeys.Aes.Internal.Services
namespace Common.Security.Cryptography.Keys.Aes.Internal.Services
{
internal class AesSecurityKey : ISecurityKey
internal class AesSecurityKey : SecurityKey<AesKeyInformation>
{
#region Variables

private AesKeyInformation _securityKeyInformation;

#endregion

#region Constructors

public AesSecurityKey(AesKeyInformation aesKeyInformation)
: base(aesKeyInformation)
{
_securityKeyInformation = aesKeyInformation ?? throw new ArgumentNullException(nameof(aesKeyInformation));
}

#endregion

#region ISecurityKey

public SecurityKeyInformation KeyInformation => _securityKeyInformation;

public async Task<byte[]> EncryptAsync(byte[] data, CancellationToken cancellationToken = default)
public override async Task<byte[]> EncryptAsync(byte[] data, CancellationToken cancellationToken = default)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}

using var aes = new AesManaged();
aes.BlockSize = _securityKeyInformation.BlockSize;
aes.Padding = _securityKeyInformation.PaddingMode;
aes.Key = _securityKeyInformation.Key;
aes.IV = _securityKeyInformation.IV;
aes.Mode = _securityKeyInformation.CipherMode;
aes.BlockSize = SecurityKeyInformation.BlockSize;
aes.Padding = SecurityKeyInformation.PaddingMode;
aes.Key = SecurityKeyInformation.Key;
aes.IV = SecurityKeyInformation.IV;
aes.Mode = SecurityKeyInformation.CipherMode;

using var dataStream = new MemoryStream(data);
var encryptedDataStream = new MemoryStream();
Expand All @@ -53,19 +42,19 @@ public async Task<byte[]> EncryptAsync(byte[] data, CancellationToken cancellati
return encryptedDataStream.ToArray();
}

public async Task<byte[]> DecryptAsync(byte[] data, CancellationToken cancellationToken = default)
public override async Task<byte[]> DecryptAsync(byte[] data, CancellationToken cancellationToken = default)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}

using var aes = new AesManaged();
aes.BlockSize = _securityKeyInformation.BlockSize;
aes.Padding = _securityKeyInformation.PaddingMode;
aes.Key = _securityKeyInformation.Key;
aes.IV = _securityKeyInformation.IV;
aes.Mode = _securityKeyInformation.CipherMode;
aes.BlockSize = SecurityKeyInformation.BlockSize;
aes.Padding = SecurityKeyInformation.PaddingMode;
aes.Key = SecurityKeyInformation.Key;
aes.IV = SecurityKeyInformation.IV;
aes.Mode = SecurityKeyInformation.CipherMode;

using var encryptedDataStream = new MemoryStream(data);
using var cryptoStream = new CryptoStream(encryptedDataStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
Expand All @@ -74,53 +63,15 @@ public async Task<byte[]> DecryptAsync(byte[] data, CancellationToken cancellati
return decryptedStream.ToArray();
}

public Task<byte[]> SignAsync(byte[] data, HashAlgorithmName hashAlgorithmName, CancellationToken cancellationToken = default)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}

var hashAlgorithm = HashAlgorithm.Create(hashAlgorithmName.Name);
if (hashAlgorithm == null)
{
throw new InvalidOperationException($"The hash algorithm {hashAlgorithmName.Name} is not supported.");
}

var dataHash = hashAlgorithm.ComputeHash(data);
return EncryptAsync(dataHash, cancellationToken);
}

public async Task<bool> ValidateSignatureAsync(byte[] data, byte[] signedData, HashAlgorithmName hashAlgorithmName, CancellationToken cancellationToken = default)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (signedData == null)
{
throw new ArgumentNullException(nameof(signedData));
}

var hashAlgorithm = HashAlgorithm.Create(hashAlgorithmName.Name);
if (hashAlgorithm == null)
{
throw new InvalidOperationException($"The hash algorithm {hashAlgorithmName.Name} is not supported.");
}

var decryptedDataHash = await DecryptAsync(signedData, cancellationToken);
return decryptedDataHash.SequenceEqual(hashAlgorithm.ComputeHash(data));
}

public void Dispose()
public override void Dispose()
{
if (_securityKeyInformation == null)
if (SecurityKeyInformation == null)
{
throw new ObjectDisposedException(nameof(AesSecurityKey));
}

Array.Clear(_securityKeyInformation.Key, 0, _securityKeyInformation.Key.Length);
_securityKeyInformation = null;
Array.Clear(SecurityKeyInformation.Key, 0, SecurityKeyInformation.Key.Length);
SecurityKeyInformation = null;
}

#endregion
Expand Down
Loading

0 comments on commit ebf60a2

Please sign in to comment.