From 3a9be6e108e4c754418fb2edff06e84eb731d85e Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:40:22 +0100 Subject: [PATCH 1/3] feat: Add CryptoNet.Examples project with various encryption examples - Introduced `Program.cs` to demonstrate DSA and AES examples. - Added test files for AES and RSA examples including `.docx`, `.pdf`, `.png`, and `.xlsx`. - Updated `CryptoNet.ExtPack` project to improve MD5 calculation and string comparison. - Modified solution file to include the new `CryptoNet.Examples` project. - Removed outdated contributing guidelines from `README.md`. - Deleted `RELEASE-NOTES` file as it was no longer needed. - Created various PowerShell scripts for build, code coverage, Docker setup, documentation generation, and NuGet validation. - Added migration guide for transitioning from CryptoNet v2.4.0 to v3.4.3, detailing breaking changes and updates. - Updated table of contents in documentation to include migration and examples sections. --- .github/copilot-instructions.md | 263 ++++++++++++++ CONTRIBUTING.md | 14 +- CryptoNet.Examples/CryptoNet.Examples.csproj | 29 ++ CryptoNet.Examples/ExampleAes.cs | 130 +++++++ CryptoNet.Examples/ExampleDsa.cs | 60 ++++ CryptoNet.Examples/ExampleRsa.cs | 168 +++++++++ CryptoNet.Examples/Program.cs | 38 ++ CryptoNet.Examples/TestFiles/test.docx | Bin 0 -> 17133 bytes CryptoNet.Examples/TestFiles/test.pdf | Bin 0 -> 175418 bytes CryptoNet.Examples/TestFiles/test.png | Bin 0 -> 15561 bytes CryptoNet.Examples/TestFiles/test.xlsx | Bin 0 -> 8708 bytes CryptoNet.ExtPack/CryptoNet.ExtPack.csproj | 2 +- CryptoNet.ExtPack/ExtensionPack.cs | 6 +- CryptoNet.sln | 6 + README.md | 11 - RELEASE-NOTES | 1 - run_build.ps1 => Scripts/run_build.ps1 | 0 .../run_codecoverage.ps1 | 0 .../run_docker_build.ps1 | 0 run_docs.ps1 => Scripts/run_docs.ps1 | 0 .../run_nuget_validation.ps1 | 0 run_release.ps1 => Scripts/run_release.ps1 | 0 .../run_update_index.ps1 | 0 docs/examples.md | 268 ++++++++++++++ docs/migration.md | 332 ++++++++++++++++++ docs/toc.yml | 6 +- 26 files changed, 1316 insertions(+), 18 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 CryptoNet.Examples/CryptoNet.Examples.csproj create mode 100644 CryptoNet.Examples/ExampleAes.cs create mode 100644 CryptoNet.Examples/ExampleDsa.cs create mode 100644 CryptoNet.Examples/ExampleRsa.cs create mode 100644 CryptoNet.Examples/Program.cs create mode 100644 CryptoNet.Examples/TestFiles/test.docx create mode 100644 CryptoNet.Examples/TestFiles/test.pdf create mode 100644 CryptoNet.Examples/TestFiles/test.png create mode 100644 CryptoNet.Examples/TestFiles/test.xlsx delete mode 100644 RELEASE-NOTES rename run_build.ps1 => Scripts/run_build.ps1 (100%) rename run_codecoverage.ps1 => Scripts/run_codecoverage.ps1 (100%) rename run_docker_build.ps1 => Scripts/run_docker_build.ps1 (100%) rename run_docs.ps1 => Scripts/run_docs.ps1 (100%) rename run_nuget_validation.ps1 => Scripts/run_nuget_validation.ps1 (100%) rename run_release.ps1 => Scripts/run_release.ps1 (100%) rename run_update_index.ps1 => Scripts/run_update_index.ps1 (100%) create mode 100644 docs/examples.md create mode 100644 docs/migration.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..bdc7981 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,263 @@ +# CryptoNet - AI Coding Agent Instructions + +## Project Overview +CryptoNet is a cross-platform .NET cryptography library providing RSA, AES, and DSA implementations. The core library targets .NET Standard 2.0 for maximum compatibility, while tests and examples use .NET 8.0. + +## Architecture + +### Project Structure +- **CryptoNet** (netstandard2.0): Core library with `CryptoNetRsa`, `CryptoNetAes`, `CryptoNetDsa` classes +- **CryptoNet.Models** (netstandard2.0): Shared models (`CryptoNetInfo`, `RsaDetail`, `AesDetail`, `DsaDetail`, `KeyType`, `EncryptionType`) +- **CryptoNet.ExtShared** (netstandard2.0): Internal utilities for certificate handling and byte operations +- **CryptoNet.ExtPack** (net8.0): Extension methods for MD5 hashing, PEM export, and content validation +- **CryptoNet.UnitTests** (net8.0): NUnit tests with NUnit, Moq, Shouldly assertions + +### Key Dependencies +All projects use **Central Package Management** via `Directory.Packages.props`. Never add package versions directly to `.csproj` files—only add `` without versions. + +### NuGet Packaging Pattern +The main `CryptoNet.csproj` uses `PrivateAssets="all"` for project references to `ExtShared` and `Models`, then explicitly includes their DLLs in the package via `IncludeReferencedDllsInPackage` target. This creates a self-contained NuGet package. + +## Development Workflows + +### Building & Testing +```powershell +# Standard build and test +dotnet build +dotnet test --configuration Release --no-build + +# Code coverage with HTML report +.\Scripts\run_codecoverage.ps1 # Runs tests, generates coverage, opens in Edge + +# Docker build +.\Scripts\run_docker_build.ps1 +# or: docker build . --file .\Dockerfile --tag cryptonet-service:latest +``` + +### Documentation Generation +Documentation uses **DocFX** to generate API docs from XML comments: +```powershell +.\Scripts\run_docs.ps1 # Cleans, builds, serves on localhost:8080 + +# Setup (one-time): dotnet tool install -g docfx +``` + +The `index.md` is kept in sync with `README.md`: +```powershell +.\Scripts\run_update_index.ps1 # Adds YAML header and appends README content +``` + +### Release Process +Releases are tag-based with version format `vX.Y.Z` or `vX.Y.Z-previewYYYYMMDDHmm`: +```powershell +# Preview release +.\Scripts\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $true + +# Production release +.\Scripts\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $false +``` + +Pushing tags triggers GitHub Actions workflows (`cd-release.yml`, `cd-release-preview.yml`) that build, test, pack, and publish to NuGet. + +## Coding Conventions + +### Code Quality Standards +- **Nullable reference types enabled**: Use `?` for nullable types, handle nulls explicitly +- **TreatWarningsAsErrors**: All warnings are errors—code must be warning-free +- **XML documentation required**: Public APIs must have `` tags (enforced by `GenerateDocumentationFile`) +- **Deterministic builds**: `true` in `Directory.Build.Props` + +### Interface Pattern +All main crypto classes implement interfaces (`ICryptoNetRsa`, `ICryptoNetAes`, `ICryptoNetDsa`) which inherit from `ICryptoNet`. This base interface defines: +```csharp +CryptoNetInfo Info { get; } +byte[] EncryptFromString(string content); +string DecryptToString(byte[] bytes); +byte[] EncryptFromBytes(byte[] bytes); +byte[] DecryptToBytes(byte[] bytes); +``` + +### Constructor Overloading Pattern +Crypto classes follow a consistent constructor pattern: +```csharp +// Self-generated keys +public CryptoNetRsa(int keySize = 2048) + +// String key (XML or PEM) +public CryptoNetRsa(string key, int keySize = 2048) + +// File-based key +public CryptoNetRsa(FileInfo fileInfo, int keySize = 2048) + +// X509 certificate +public CryptoNetRsa(X509Certificate2? certificate, KeyType keyType, int keySize = 2048) +``` + +### Testing Patterns +Tests use **NUnit** with **Shouldly** for assertions: +```csharp +result.ShouldBe(expected); // NOT Assert.AreEqual +ExtensionPack.CheckContent(original, decrypted).ShouldBeTrue(); // MD5-based content comparison +``` + +Test resource files are in `Resources/` with `Always`. + +## Common Pitfalls + +### Version Management +- **Never** hardcode versions in individual `.csproj` files +- Update versions in `Directory.Build.Props` (global) and `Directory.Packages.props` (package versions) +- Release versions must match the format `^\d+\.\d+\.\d+$` in `run_release.ps1` + +### Cross-Platform Compatibility +- Core library must remain .NET Standard 2.0 compatible +- Avoid .NET 8+ specific APIs in `CryptoNet`, `CryptoNet.Models`, `CryptoNet.ExtShared` +- Use `System.Security.Cryptography` types from .NET Standard 2.0 + +### GitHub Actions Workflows +- **Never add `runs-on` to caller jobs** when using reusable workflows (see `CONTRIBUTING.md`) +- Reusable workflows declare `on: workflow_call` and define their own `runs-on` +- Example: `cd-build-test-pack.yml` is a reusable workflow called by release workflows + +### File Header Conventions +All C# files include copyright headers: +```csharp +// +// Copyright (c) 2021 All Rights Reserved +// +// Maytham Fahmi +// DD-MM-YYYY HH:MM:SS +// part of CryptoNet project +``` + +## Key Files +- `Directory.Build.Props`: Global MSBuild properties (version, warnings, symbols) +- `Directory.Packages.props`: Central package version management +- `docfx.json`: DocFX configuration for API documentation +- `RELEASE-NOTES`: Changelog file read during NuGet packing (see `PrepareReleaseNotes` target) +- `coverlet.runsettings`: Code coverage configuration + +## Development Tips +- Use `ExtShared.LoadFileToString()` for reading key files consistently +- Use `ExtensionPack.CheckContent()` for MD5-based content validation in tests +- Key files are stored/loaded using `SaveKey(FileInfo, bool isPrivate)` and constructor overloads +- The `Info` property exposes algorithm details, key types, and loaded keys for inspection + +## Migration Guide: Old API → Latest CryptoNet v3+ + +### API Changes Summary +- **Removed**: `ExportKey()`, `ExportKeyAndSave()` methods +- **Replaced with**: `GetKey()` and `SaveKey()` methods +- **Namespace change**: `CryptoNetUtils` → `ExtShared.ExtShared` +- All crypto classes now expose `Info` property with algorithm details + +### AES Examples + +#### Old: Encrypt/Decrypt with Symmetric Key +```csharp +// OLD API (v2.x) +ICryptoNet cryptoNet = new CryptoNetAes(); +var key = cryptoNet.ExportKey(); +ICryptoNet encryptClient = new CryptoNetAes(key); +var encrypt = encryptClient.EncryptFromString(data); +ICryptoNet decryptClient = new CryptoNetAes(key); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +#### New: Encrypt/Decrypt with Symmetric Key +```csharp +// NEW API (v3.x) +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var key = cryptoNet.GetKey(); // Changed from ExportKey() + +ICryptoNetAes encryptClient = new CryptoNetAes(key); +var encrypt = encryptClient.EncryptFromString(data); + +ICryptoNetAes decryptClient = new CryptoNetAes(key); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +#### Old: Export and Import Symmetric Key from File +```csharp +// OLD API (v2.x) +ICryptoNet cryptoNet = new CryptoNetAes(); +var file = new FileInfo("symmetric.key"); +cryptoNet.ExportKeyAndSave(file); +var encrypt = cryptoNet.EncryptFromString(data); +ICryptoNet cryptoNetImport = new CryptoNetAes(file); +var decrypt = cryptoNetImport.DecryptToString(encrypt); +``` + +#### New: Save and Load Symmetric Key from File +```csharp +// NEW API (v3.x) +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var file = new FileInfo("symmetric.key"); +cryptoNet.SaveKey(file); // Changed from ExportKeyAndSave() + +var encrypt = cryptoNet.EncryptFromString(data); + +ICryptoNetAes cryptoNetImport = new CryptoNetAes(file); +var decrypt = cryptoNetImport.DecryptToString(encrypt); +``` + +### RSA Examples + +#### Old: Generate RSA Key Pair and Save +```csharp +// OLD API (v2.x) +ICryptoNet cryptoNet = new CryptoNetRsa(); +cryptoNet.ExportKeyAndSave(new FileInfo("private.key"), true); +cryptoNet.ExportKeyAndSave(new FileInfo("public.key"), false); +ICryptoNet pubKeyCrypto = new CryptoNetRsa(new FileInfo("public.key")); +var encrypt = pubKeyCrypto.EncryptFromString(data); +ICryptoNet priKeyCrypto = new CryptoNetRsa(new FileInfo("private.key")); +var decrypt = priKeyCrypto.DecryptToString(encrypt); +``` + +#### New: Generate RSA Key Pair and Save +```csharp +// NEW API (v3.x) +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); +cryptoNet.SaveKey(new FileInfo("private.key"), true); // Changed from ExportKeyAndSave() +cryptoNet.SaveKey(new FileInfo("public.key"), false); // Changed from ExportKeyAndSave() + +ICryptoNetRsa pubKeyCrypto = new CryptoNetRsa(new FileInfo("public.key")); +var encrypt = pubKeyCrypto.EncryptFromString(data); + +ICryptoNetRsa priKeyCrypto = new CryptoNetRsa(new FileInfo("private.key")); +var decrypt = priKeyCrypto.DecryptToString(encrypt); +``` + +### X509 Certificate Examples + +#### Old: Use X509 Certificate for Encryption +```csharp +// OLD API (v2.x) +X509Certificate2? cert = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); +ICryptoNet publicKeyCrypto = new CryptoNetRsa(cert, KeyType.PublicKey); +var encrypt = publicKeyCrypto.EncryptFromString(data); +ICryptoNet privateKeyCrypto = new CryptoNetRsa(cert, KeyType.PrivateKey); +var decrypt = privateKeyCrypto.DecryptToString(encrypt); +``` + +#### New: Use X509 Certificate for Encryption +```csharp +// NEW API (v3.x) +using CryptoNet.ExtShared; // Namespace change: CryptoNetUtils → ExtShared + +X509Certificate2? cert = ExtShared.GetCertificateFromStore("CN=Maytham"); // Changed from CryptoNetUtils + +ICryptoNetRsa publicKeyCrypto = new CryptoNetRsa(cert, KeyType.PublicKey); +var encrypt = publicKeyCrypto.EncryptFromString(data); + +ICryptoNetRsa privateKeyCrypto = new CryptoNetRsa(cert, KeyType.PrivateKey); +var decrypt = privateKeyCrypto.DecryptToString(encrypt); +``` + +### Key Migration Points +1. **Method renames**: `ExportKey()` → `GetKey()`, `ExportKeyAndSave()` → `SaveKey()` +2. **Namespace**: `CryptoNetUtils` → `CryptoNet.ExtShared.ExtShared` +3. **Interface specificity**: Prefer `ICryptoNetRsa`, `ICryptoNetAes`, `ICryptoNetDsa` over generic `ICryptoNet` +4. **Info property**: Access algorithm details via `cryptoNet.Info` (includes key types, sizes, loaded keys) +5. **File operations**: Both `FileInfo` and `string` filename overloads available for `SaveKey()` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a965657..ff51c79 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - # … + # � ``` ## Versioning and Git Tag Guidelines @@ -98,3 +98,15 @@ Use via: `${{ steps.sanitize.outputs.sanitized_version }}` ## Maintenance - Keep conventions aligned with GitHub Actions and NuGet rules. + +## Contributing + +You are more than welcome to contribute in one of the following ways: + +1. Basic: Give input, and suggestions for improvement by creating an issue and labeling it https://github.com/itbackyard/CryptoNet/issues +2. Advance: if you have good knowledge of C# and Cryptography just grab one of the issues, or features, or create a new one and refactor and add a pull request. +3. Documentation: Add, update, or improve documentation, by making a pull request. + +### How to contribute: + +[Here](https://www.dataschool.io/how-to-contribute-on-github/) is a link to learn how to contribute if you are not aware of how to do it. diff --git a/CryptoNet.Examples/CryptoNet.Examples.csproj b/CryptoNet.Examples/CryptoNet.Examples.csproj new file mode 100644 index 0000000..aefb1c2 --- /dev/null +++ b/CryptoNet.Examples/CryptoNet.Examples.csproj @@ -0,0 +1,29 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/CryptoNet.Examples/ExampleAes.cs b/CryptoNet.Examples/ExampleAes.cs new file mode 100644 index 0000000..9c8d02b --- /dev/null +++ b/CryptoNet.Examples/ExampleAes.cs @@ -0,0 +1,130 @@ +// +// Copyright (c) 2021 All Rights Reserved +// +// Maytham Fahmi +// 17-12-2021 12:18:44 +// part of CryptoNet project + +using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; +using CryptoNet.Models; + +namespace CryptoNet.Examples; + +public static class ExampleAes +{ + private const string ConfidentialDummyData = @"Some Secret Data"; + + private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; + private readonly static string SymmetricKeyFile = Path.Combine(BaseFolder, $"{KeyType.SymmetricKey}.xml"); + + public static void Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey() + { + ICryptoNetAes cryptoNet = new CryptoNetAes(); + var key = cryptoNet.GetKey(); + + ICryptoNet encryptClient = new CryptoNetAes(key); + var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + + ICryptoNet decryptClient = new CryptoNetAes(key); + var decrypt = decryptClient.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_2_SelfGenerated_And_Save_SymmetricKey() + { + ICryptoNetAes cryptoNet = new CryptoNetAes(); + var file = new FileInfo(SymmetricKeyFile); + cryptoNet.SaveKey(file); + + Debug.Assert(File.Exists(file.FullName)); + + var encrypt = cryptoNet.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNetKeyImport = new CryptoNetAes(file); + var decrypt = cryptoNetKeyImport.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey() + { + var symmetricKey = "12345678901234567890123456789012"; + if (symmetricKey.Length != 32) + { + Console.WriteLine("key should be 32 character long"); + Environment.Exit(0); + } + + var secret = "1234567890123456"; + if (secret.Length != 16) + { + Console.WriteLine("key should be 16 character long"); + Environment.Exit(1); + } + + var key = Encoding.UTF8.GetBytes(symmetricKey); + var iv = Encoding.UTF8.GetBytes(secret); + + ICryptoNet encryptClient = new CryptoNetAes(key, iv); + var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + + ICryptoNet decryptClient = new CryptoNetAes(key, iv); + var decrypt = decryptClient.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey() + { + var symmetricKey = UniqueKeyGenerator("symmetricKey"); + var secret = new string(UniqueKeyGenerator("password").Take(16).ToArray()); + + var key = Encoding.UTF8.GetBytes(symmetricKey); + var iv = Encoding.UTF8.GetBytes(secret); + + ICryptoNet encryptClient = new CryptoNetAes(key, iv); + var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + + ICryptoNet decryptClient = new CryptoNetAes(key, iv); + var decrypt = decryptClient.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test(string filename) + { + ICryptoNetAes cryptoNet = new CryptoNetAes(); + var key = cryptoNet.GetKey(); + + FileInfo fi = new FileInfo(filename); + + ICryptoNet encryptClient = new CryptoNetAes(key); + string pdfFilePath = Path.Combine(BaseFolder, filename); + byte[] pdfFileBytes = File.ReadAllBytes(pdfFilePath); + var encrypt = encryptClient.EncryptFromBytes(pdfFileBytes); + + ICryptoNet decryptClient = new CryptoNetAes(key); + var decrypt = decryptClient.DecryptToBytes(encrypt); + string pdfDecryptedFilePath = $"TestFiles\\{Path.GetFileNameWithoutExtension(fi.Name)}-decrypted.{fi.Extension}"; + File.WriteAllBytes(pdfDecryptedFilePath, decrypt); + + var isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(pdfFileBytes, decrypt); + Debug.Assert(isIdenticalFile); + } + + public static string UniqueKeyGenerator(string input) + { + byte[] inputBytes = Encoding.ASCII.GetBytes(input); + byte[] hash = MD5.HashData(inputBytes); + + StringBuilder sb = new StringBuilder(); + foreach (var t in hash) + { + sb.Append(t.ToString("X2")); + } + return sb.ToString(); + } +} \ No newline at end of file diff --git a/CryptoNet.Examples/ExampleDsa.cs b/CryptoNet.Examples/ExampleDsa.cs new file mode 100644 index 0000000..4e30ff1 --- /dev/null +++ b/CryptoNet.Examples/ExampleDsa.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2021 All Rights Reserved +// +// Maytham Fahmi +// 07-12-2025 20:04:00 +// part of CryptoNet project + +using System.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using CryptoNet.Models; + +namespace CryptoNet.Examples; + +public static class ExampleDsa +{ + private const string ConfidentialDummyData = @"Some Secret Data"; + private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; + + internal static readonly string PrivateKeyFile = Path.Combine(BaseFolder, "privateKey"); + internal static readonly string PublicKeyFile = Path.Combine(BaseFolder, "publicKey.pub"); + + public static void Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey() + { + ICryptoNetDsa client = new CryptoNetDsa(); + var privateKey = client.GetKey(true); + + ICryptoNetDsa signatureClient = new CryptoNetDsa(privateKey); + var signature = signatureClient.CreateSignature(ConfidentialDummyData); + + ICryptoNetDsa verifyClient = new CryptoNetDsa(privateKey); + var confidentialAsBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); + + bool isVerified = verifyClient.IsContentVerified(confidentialAsBytes, signature); + + Debug.Assert(isVerified == true); + } + + public static void Example_2_SelfGenerated_And_Save_AsymmetricKey() + { + ICryptoNetDsa cryptoNet = new CryptoNetDsa(); + + cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); + cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + + Debug.Assert(File.Exists(new FileInfo(PrivateKeyFile).FullName)); + Debug.Assert(File.Exists(new FileInfo(PublicKeyFile).FullName)); + + ICryptoNetDsa dsaWithPrivateKey = new CryptoNetDsa(new FileInfo(PrivateKeyFile)); + var signature = dsaWithPrivateKey.CreateSignature(ConfidentialDummyData); + + ICryptoNetDsa dsaWithPublicKey = new CryptoNetDsa(new FileInfo(PublicKeyFile)); + var confidentialAsBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); + var isVerified = dsaWithPublicKey.IsContentVerified(confidentialAsBytes, signature); + + Debug.Assert(isVerified == true); + } + +} \ No newline at end of file diff --git a/CryptoNet.Examples/ExampleRsa.cs b/CryptoNet.Examples/ExampleRsa.cs new file mode 100644 index 0000000..fd04096 --- /dev/null +++ b/CryptoNet.Examples/ExampleRsa.cs @@ -0,0 +1,168 @@ +// +// Copyright (c) 2021 All Rights Reserved +// +// Maytham Fahmi +// 17-12-2021 12:18:44 +// part of CryptoNet project + +using System.Diagnostics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using CryptoNet.Models; + +namespace CryptoNet.Examples; + +public static class ExampleRsa +{ + private const string ConfidentialDummyData = @"Some Secret Data"; + private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; + + internal static readonly string PrivateKeyFile = Path.Combine(BaseFolder, "privateKey"); + internal static readonly string PublicKeyFile = Path.Combine(BaseFolder, "publicKey.pub"); + + public static void Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey() + { + ICryptoNetRsa cryptoNet = new CryptoNetRsa(); + + var privateKey = cryptoNet.GetKey(true); + var publicKey = cryptoNet.GetKey(false); + + ICryptoNet encryptClient = new CryptoNetRsa(publicKey); + var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + + ICryptoNet decryptClient = new CryptoNetRsa(privateKey); + var decrypt = decryptClient.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_2_SelfGenerated_And_Save_AsymmetricKey() + { + ICryptoNetRsa cryptoNet = new CryptoNetRsa(); + + cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); + cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + + Debug.Assert(File.Exists(new FileInfo(PrivateKeyFile).FullName)); + Debug.Assert(File.Exists(new FileInfo(PublicKeyFile).FullName)); + + ICryptoNet cryptoNetPubKey = new CryptoNetRsa(new FileInfo(PublicKeyFile)); + var encrypt = cryptoNetPubKey.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNetPriKey = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); + var decrypt = cryptoNetPriKey.DecryptToString(encrypt); + + Debug.Assert(ConfidentialDummyData == decrypt); + } + + public static void Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content() + { + ICryptoNet cryptoNetWithPublicKey = new CryptoNetRsa(new FileInfo(PublicKeyFile)); + var encryptWithPublicKey = cryptoNetWithPublicKey.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNetWithPrivateKey = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); + var decryptWithPrivateKey = cryptoNetWithPrivateKey.DecryptToString(encryptWithPublicKey); + + Debug.Assert(ConfidentialDummyData == decryptWithPrivateKey); + } + + public static void Example_4_Using_X509_Certificate() + { + // Find and replace CN=localhost with your own certificate + X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + + ICryptoNet cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); + var encryptWithPublicKey = cryptoNetWithPublicKey.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNetWithPrivateKey = new CryptoNetRsa(certificate, KeyType.PrivateKey); + var decryptWithPrivateKey = cryptoNetWithPrivateKey.DecryptToString(encryptWithPublicKey); + + Debug.Assert(ConfidentialDummyData == decryptWithPrivateKey); + } + + public static void Example_5_Export_Public_Key_For_X509_Certificate() + { + // Find and replace CN=localhost with your own certificate + X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + + ICryptoNetRsa cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); + var publicKey = cryptoNetWithPublicKey.GetKey(false); + + Debug.Assert(!string.IsNullOrEmpty(publicKey)); + } + + /// + /// CryptoNet interact with .net 5/6 for customization, like import/export PEM + /// Work in Progress, not finished + /// + public static void Example_7_Customize() + { + X509Certificate2? cert = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + + var pubKeyPem = ExportPemKey(cert!, false); + var priKeyPem = ExportPemKey(cert!); + + var password = "password"; + var encryptedPriKeyBytes = ExportPemKeyWithPassword(cert!, password); + + ICryptoNet cryptoNet1 = ImportPemKeyWithPassword(encryptedPriKeyBytes, password); + var encrypt1 = cryptoNet1.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNet2 = ImportPemKey(pubKeyPem); + var encrypt2 = cryptoNet2.EncryptFromString(ConfidentialDummyData); + + ICryptoNet cryptoNet3 = ImportPemKey(priKeyPem); + var decrypt2 = cryptoNet3.DecryptToString(encrypt2); + + Debug.Assert(ConfidentialDummyData == decrypt2); + + var decrypt1 = cryptoNet3.DecryptToString(encrypt1); + + Debug.Assert(ConfidentialDummyData == decrypt1); + } + + public static char[] ExportPemCertificate(X509Certificate2 cert) + { + byte[] certBytes = cert!.RawData; + char[] certPem = PemEncoding.Write("CERTIFICATE", certBytes); + return certPem; + } + + public static char[] ExportPemKey(X509Certificate2 cert, bool privateKey = true) + { + AsymmetricAlgorithm rsa = cert.GetRSAPrivateKey()!; + + if (privateKey) + { + byte[] priKeyBytes = rsa.ExportPkcs8PrivateKey(); + return PemEncoding.Write("PRIVATE KEY", priKeyBytes); + } + + byte[] pubKeyBytes = rsa.ExportSubjectPublicKeyInfo(); + return PemEncoding.Write("PUBLIC KEY", pubKeyBytes); + } + + public static ICryptoNet ImportPemKey(char[] key) + { + ICryptoNet cryptoNet = new CryptoNetRsa(); + cryptoNet.Info.RsaDetail!.Rsa?.ImportFromPem(key); + return cryptoNet; + } + + public static byte[] ExportPemKeyWithPassword(X509Certificate2 cert, string password) + { + AsymmetricAlgorithm rsa = cert.GetRSAPrivateKey()!; + byte[] pass = Encoding.UTF8.GetBytes(password); + byte[] encryptedPrivateKey = rsa.ExportEncryptedPkcs8PrivateKey(pass, + new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, iterationCount: 100_000)); + return encryptedPrivateKey; + } + + public static ICryptoNet ImportPemKeyWithPassword(byte[] encryptedPrivateKey, string password) + { + ICryptoNet cryptoNet = new CryptoNetRsa(); + cryptoNet.Info.RsaDetail?.Rsa?.ImportEncryptedPkcs8PrivateKey(password, encryptedPrivateKey, out _); + return cryptoNet; + } +} \ No newline at end of file diff --git a/CryptoNet.Examples/Program.cs b/CryptoNet.Examples/Program.cs new file mode 100644 index 0000000..397f65a --- /dev/null +++ b/CryptoNet.Examples/Program.cs @@ -0,0 +1,38 @@ +//using CryptoNet; +//using System.Diagnostics; + +//const string ConfidentialDummyData = "Top secret 🤣🤣"; // + +//ICryptoNetAes cryptoNet = new CryptoNetAes(); +//var key = cryptoNet.GetKey(); + +//ICryptoNet encryptClient = new CryptoNetAes(key); +//var encrypted = encryptClient.EncryptFromBytes(ConfidentialDummyData); + +//ICryptoNet decryptClient = new CryptoNetAes(key); +//var decrypted = decryptClient.DecryptToString(encrypted); + +//Debug.Assert(ConfidentialDummyData == decrypted); + +using CryptoNet.Examples; + +ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); +ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); + +//ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); +//ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); +//ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); +//ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); +//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.docx"); +//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.xlsx"); +//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.pdf"); +//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.png"); + +//ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); +//ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); +//ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); +//ExampleRsa.Example_4_Using_X509_Certificate(); +//ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); +//ExampleRsa.Example_7_Customize(); + + diff --git a/CryptoNet.Examples/TestFiles/test.docx b/CryptoNet.Examples/TestFiles/test.docx new file mode 100644 index 0000000000000000000000000000000000000000..48527738c25f45752219289e547dab2965191df2 GIT binary patch literal 17133 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 literal 0 HcmV?d00001 diff --git a/CryptoNet.Examples/TestFiles/test.pdf b/CryptoNet.Examples/TestFiles/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9b88b35e601f852cf249717e5ca7603c6feb4b1b GIT binary patch literal 175418 zcmZ^}19YWJ&@LR?p4hhS?AW%AiJggUClgO5wr$%J+nU(=GiT2EzW=UuZ?CL8sovey z)m2Yd^?vh`DTo5-nCLlRU|0zm3BMXy!B9$?nA$m8I(tyTnA(~A>0$ft6QNJh^nbcJ z|Lx}Gg<$|#+Blm!5;6d644q9yO^v^rn8Gl~nA({;TM%-wGc$AkCovJSaI!J|kySkG zO$iy4ogGaLZDIKNVVwT{<(_%w18sml@b=;6x><9v;kN0fSDCuUhk_yzwg(Ii0^9;Z zo+Qy*0RdfPq*@(eQE#;1*`Qj!U{r8Nh6_<2QPDt+@2+xHY%rj$wW?Hxe*M0gjwv^{ zsZV_^{Wf9Fl9Qg5zMhr!H0fs6*|rP`2DByt(_HC#eKa>Q>iZ=4bSVwAjU!)Gfs<}b z3+3-VC<0`3atpmn5m_?P6u@}}6z6l1&F?XKIo$4(D5zP7xs`A;C}W4!l6z@`j=$+= z4j*Nk#(VNxw+k0mK^x4V#o~2Ow8Uw9PZ|gev#?19i>Wd1ctCn{@J2u81zp*Ch%FmA z>x0j)

dJ01im}milP@liaXCnYyc4LHaPY*2D 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 literal 0 HcmV?d00001 diff --git a/CryptoNet.Examples/TestFiles/test.png b/CryptoNet.Examples/TestFiles/test.png new file mode 100644 index 0000000000000000000000000000000000000000..29c6eb4415dd484f27cda8ac5e21f84260f625a8 GIT binary patch literal 15561 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 HiU0orBLpTI literal 0 HcmV?d00001 diff --git a/CryptoNet.Examples/TestFiles/test.xlsx b/CryptoNet.Examples/TestFiles/test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..957546666e53cdc5bddbb808cbf870014a1ac883 GIT binary patch literal 8708 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 IIRN1Q0E7P!QUCw| literal 0 HcmV?d00001 diff --git a/CryptoNet.ExtPack/CryptoNet.ExtPack.csproj b/CryptoNet.ExtPack/CryptoNet.ExtPack.csproj index 5b1df01..59a81bb 100644 --- a/CryptoNet.ExtPack/CryptoNet.ExtPack.csproj +++ b/CryptoNet.ExtPack/CryptoNet.ExtPack.csproj @@ -33,7 +33,7 @@ true <_SkipUpgradeNetAnalyzersNuGetWarning>true true - True + true diff --git a/CryptoNet.ExtPack/ExtensionPack.cs b/CryptoNet.ExtPack/ExtensionPack.cs index 4d1bf31..636ee18 100644 --- a/CryptoNet.ExtPack/ExtensionPack.cs +++ b/CryptoNet.ExtPack/ExtensionPack.cs @@ -24,7 +24,7 @@ public static bool CheckContent(string originalContent, string decryptedContent) return originalContent == decryptedContent; } - return CalculateMd5(originalContent).Equals(CalculateMd5(decryptedContent)); + return CalculateMd5(originalContent).Equals(CalculateMd5(decryptedContent), StringComparison.Ordinal); } ///

@@ -34,8 +34,8 @@ public static bool CheckContent(string originalContent, string decryptedContent) /// The MD5 hash of the content as a lowercase hexadecimal string. public static string CalculateMd5(string content) { - var hash = MD5.HashData(Encoding.UTF8.GetBytes(content)); - return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + byte[] hash = MD5.HashData(Encoding.UTF8.GetBytes(content)); + return Convert.ToHexString(hash).ToLowerInvariant(); } /// diff --git a/CryptoNet.sln b/CryptoNet.sln index ca2ae6a..b9c53e8 100644 --- a/CryptoNet.sln +++ b/CryptoNet.sln @@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DSAExample", "Examples\DSAE EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CryptoNet.ConsumerTest", "CryptoNet.ConsumerTest\CryptoNet.ConsumerTest.csproj", "{6BBC4487-140E-44E1-9703-F9F48E805589}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CryptoNet.Examples", "CryptoNet.Examples\CryptoNet.Examples.csproj", "{CAEB7F60-5632-2F90-05B7-D72D7AD177FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,6 +101,10 @@ Global {6BBC4487-140E-44E1-9703-F9F48E805589}.Debug|Any CPU.Build.0 = Debug|Any CPU {6BBC4487-140E-44E1-9703-F9F48E805589}.Release|Any CPU.ActiveCfg = Release|Any CPU {6BBC4487-140E-44E1-9703-F9F48E805589}.Release|Any CPU.Build.0 = Release|Any CPU + {CAEB7F60-5632-2F90-05B7-D72D7AD177FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAEB7F60-5632-2F90-05B7-D72D7AD177FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAEB7F60-5632-2F90-05B7-D72D7AD177FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAEB7F60-5632-2F90-05B7-D72D7AD177FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index b8275ee..f928c82 100644 --- a/README.md +++ b/README.md @@ -163,14 +163,3 @@ dotnet tool install --global dotnet-reportgenerator-globaltool Once set up, you can use these tools to analyze and generate detailed code coverage reports to ensure thorough testing of your application. -## Contributing - -You are more than welcome to contribute in one of the following ways: - -1. Basic: Give input, and suggestions for improvement by creating an issue and labeling it https://github.com/itbackyard/CryptoNet/issues -2. Advance: if you have good knowledge of C# and Cryptography just grab one of the issues, or features, or create a new one and refactor and add a pull request. -3. Documentation: Add, update, or improve documentation, by making a pull request. - -### How to contribute: - -[Here](https://www.dataschool.io/how-to-contribute-on-github/) is a link to learn how to contribute if you are not aware of how to do it. diff --git a/RELEASE-NOTES b/RELEASE-NOTES deleted file mode 100644 index 8b13789..0000000 --- a/RELEASE-NOTES +++ /dev/null @@ -1 +0,0 @@ - diff --git a/run_build.ps1 b/Scripts/run_build.ps1 similarity index 100% rename from run_build.ps1 rename to Scripts/run_build.ps1 diff --git a/run_codecoverage.ps1 b/Scripts/run_codecoverage.ps1 similarity index 100% rename from run_codecoverage.ps1 rename to Scripts/run_codecoverage.ps1 diff --git a/run_docker_build.ps1 b/Scripts/run_docker_build.ps1 similarity index 100% rename from run_docker_build.ps1 rename to Scripts/run_docker_build.ps1 diff --git a/run_docs.ps1 b/Scripts/run_docs.ps1 similarity index 100% rename from run_docs.ps1 rename to Scripts/run_docs.ps1 diff --git a/run_nuget_validation.ps1 b/Scripts/run_nuget_validation.ps1 similarity index 100% rename from run_nuget_validation.ps1 rename to Scripts/run_nuget_validation.ps1 diff --git a/run_release.ps1 b/Scripts/run_release.ps1 similarity index 100% rename from run_release.ps1 rename to Scripts/run_release.ps1 diff --git a/run_update_index.ps1 b/Scripts/run_update_index.ps1 similarity index 100% rename from run_update_index.ps1 rename to Scripts/run_update_index.ps1 diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..a21eae4 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,268 @@ +# CryptoNet – How to Use (AES, DSA, RSA) + +This document shows **how to use CryptoNet** with the built-in examples for: + +- AES (symmetric encryption) +- DSA (digital signatures) +- RSA (asymmetric encryption + certificates) + +All examples live under: + +```csharp +namespace CryptoNet.Examples; +``` + +and use the shared dummy content: + +```csharp +private const string ConfidentialDummyData = @"Some Secret Data"; +``` + +--- + +## 1. AES – Symmetric Encryption (`ExampleAes`) + +### 1.1. Encrypt/Decrypt a String with a Self-Generated Key + +```csharp +// Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var key = cryptoNet.GetKey(); + +ICryptoNet encryptClient = new CryptoNetAes(key); +var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + +ICryptoNet decryptClient = new CryptoNetAes(key); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); +``` + +--- + +### 1.2. Generate & Save AES Key to File + +```csharp +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var file = new FileInfo(SymmetricKeyFile); +cryptoNet.SaveKey(file); + +var encrypt = cryptoNet.EncryptFromString(ConfidentialDummyData); + +ICryptoNet cryptoNetKeyImport = new CryptoNetAes(file); +var decrypt = cryptoNetKeyImport.DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); +``` + +--- + +### 1.3. Encrypt/Decrypt Using Your Own Key and IV + +```csharp +var key = Encoding.UTF8.GetBytes("32-char-string-key................"); +var iv = Encoding.UTF8.GetBytes("16-char-secret.."); + +ICryptoNet encryptClient = new CryptoNetAes(key, iv); +var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + +ICryptoNet decryptClient = new CryptoNetAes(key, iv); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); +``` + +--- + +### 1.4. Human-Readable Key + IV + +```csharp +var symmetricKey = UniqueKeyGenerator("symmetricKey"); +var secret = new string(UniqueKeyGenerator("password").Take(16).ToArray()); + +ICryptoNet crypto = new CryptoNetAes( + Encoding.UTF8.GetBytes(symmetricKey), + Encoding.UTF8.GetBytes(secret)); +``` + +Run: + +```csharp +ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); +``` + +--- + +### 1.5. Encrypt/Decrypt File + +```csharp +byte[] fileBytes = File.ReadAllBytes(filename); +var encrypt = encryptClient.EncryptFromBytes(fileBytes); +var decrypt = decryptClient.DecryptToBytes(encrypt); +``` + +Run: + +```csharp +ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("file.pdf"); +``` + +--- + +## 2. DSA – Digital Signatures (`ExampleDsa`) + +### 2.1. Sign & Verify with Self-Generated Key + +```csharp +ICryptoNetDsa client = new CryptoNetDsa(); +var privateKey = client.GetKey(true); + +var signature = new CryptoNetDsa(privateKey).CreateSignature(ConfidentialDummyData); + +var verified = new CryptoNetDsa(privateKey) + .IsContentVerified(ExtShared.ExtShared.StringToBytes(ConfidentialDummyData), signature); +``` + +Run: + +```csharp +ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); +``` + +--- + +### 2.2. Generate, Save, and Reuse Keys + +```csharp +cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); +cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + +var signature = new CryptoNetDsa(new FileInfo(PrivateKeyFile)) + .CreateSignature(ConfidentialDummyData); + +var verified = new CryptoNetDsa(new FileInfo(PublicKeyFile)) + .IsContentVerified(ExtShared.ExtShared.StringToBytes(ConfidentialDummyData), signature); +``` + +Run: + +```csharp +ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); +``` + +--- + +## 3. RSA – Asymmetric Encryption (`ExampleRsa`) + +### 3.1. Self-Generated RSA Keys + +```csharp +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); +var privateKey = cryptoNet.GetKey(true); +var publicKey = cryptoNet.GetKey(false); + +var encrypt = new CryptoNetRsa(publicKey).EncryptFromString(ConfidentialDummyData); +var decrypt = new CryptoNetRsa(privateKey).DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); +``` + +--- + +### 3.2. Save RSA Keys & Reuse + +```csharp +cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); +cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + +var encrypt = new CryptoNetRsa(new FileInfo(PublicKeyFile)).EncryptFromString(ConfidentialDummyData); +var decrypt = new CryptoNetRsa(new FileInfo(PrivateKeyFile)).DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); +``` + +--- + +### 3.3. Encrypt with Public Key, Decrypt with Private Key + +Run: + +```csharp +ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); +``` + +--- + +### 3.4. Use X509 Certificate for RSA Encryption + +```csharp +var cert = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + +var encrypt = new CryptoNetRsa(cert, KeyType.PublicKey) + .EncryptFromString(ConfidentialDummyData); + +var decrypt = new CryptoNetRsa(cert, KeyType.PrivateKey) + .DecryptToString(encrypt); +``` + +Run: + +```csharp +ExampleRsa.Example_4_Using_X509_Certificate(); +``` + +--- + +### 3.5. Export RSA Public Key from Certificate + +Run: + +```csharp +ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); +``` + +--- + +### 3.6. PEM Import/Export (Advanced) + +Run: + +```csharp +ExampleRsa.Example_7_Customize(); +``` + +--- + +## 4. Running All Examples + +```csharp +using CryptoNet.Examples; + +ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); +ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); +ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); +``` + +--- + +## End diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000..2e70e0c --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,332 @@ +# Migration Guide: CryptoNet v2.4.0 → v3.4.3 + +This document describes the main breaking and behavioral changes for the **AES** and **RSA** examples when migrating from **v2.4.0** to **v3.4.3** of CryptoNet. + +--- + +## 1. Common Changes + +### 1.1 Utility changes + +Some helper methods moved from `CryptoNet.Utils` to `ExtShared.ExtShared`. + +- AES: byte array comparison. +- RSA: loading X509 certificates from the store, plus other shared helpers. + +You will typically: +- Remove `using CryptoNet.Utils;` +- Add a reference/using for `ExtShared` (e.g. `using ExtShared;` if needed in your project). + +--- + +## 2. AES Migration (ExampleAes) + +### 2.1 Overview + +**v2.4.0** + +```csharp +using CryptoNet.Models; +using CryptoNet.Utils; +``` + +**v3.4.3** + +```csharp +using CryptoNet.Models; +``` + +The AES encryption/decryption API (`ICryptoNet` + `CryptoNetAes`) remains mostly the same. The main breaking changes are related to **key management** and **utility usage**. + +### 2.2 AES key management API changes + +#### 2.2.1 Generating a key in memory + +```csharp +// v2.4.0 +ICryptoNet cryptoNet = new CryptoNetAes(); +var key = cryptoNet.ExportKey(); + +// v3.4.3 +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var key = cryptoNet.GetKey(); +``` + +**Changes:** +- Interface: `ICryptoNet` → `ICryptoNetAes` for key retrieval. +- Method: `ExportKey()` → `GetKey()`. + +**Action:** +- When you need to retrieve the AES key, use `ICryptoNetAes` and `GetKey()`. + +You still use `ICryptoNet` for encryption/decryption with the retrieved key: + +```csharp +ICryptoNet encryptClient = new CryptoNetAes(key); +var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + +ICryptoNet decryptClient = new CryptoNetAes(key); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +#### 2.2.2 Saving a symmetric key to file + +```csharp +// v2.4.0 +ICryptoNet cryptoNet = new CryptoNetAes(); +var file = new FileInfo(SymmetricKeyFile); +cryptoNet.ExportKeyAndSave(file); + +// v3.4.3 +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var file = new FileInfo(SymmetricKeyFile); +cryptoNet.SaveKey(file); +``` + +**Changes:** +- Interface: `ICryptoNet` → `ICryptoNetAes`. +- Method: `ExportKeyAndSave(file)` → `SaveKey(file)`. + +**Action:** +- Update both the interface type and the method name when saving AES keys to disk. + +### 2.3 AES Examples 3 & 4 (custom/human-readable keys) + +Examples 3 and 4 are functionally identical between v2.4.0 and v3.4.3: + +- Example 3: uses user-defined 32-char key and 16-char IV. +- Example 4: generates a human-readable key and IV via `UniqueKeyGenerator`. + +No changes are required for the encryption/decryption code in these examples. + +### 2.4 File encryption example (Example 5) + +#### 2.4.1 Method name + +```csharp +// v2.4.0 +public static void Example_5_Encrypt_And_Decrypt_PdfFile_With_SymmetricKey_Test(string filename) + +// v3.4.3 +public static void Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test(string filename) +``` + +**Change:** Method name updated to reflect support for arbitrary files, not just PDFs. + +**Action:** Update any callers to use the new method name. + +#### 2.4.2 Key retrieval + +```csharp +// v2.4.0 +ICryptoNet cryptoNet = new CryptoNetAes(); +var key = cryptoNet.ExportKey(); + +// v3.4.3 +ICryptoNetAes cryptoNet = new CryptoNetAes(); +var key = cryptoNet.GetKey(); +``` + +Same pattern as in 2.2.1. + +#### 2.4.3 Byte array comparison + +```csharp +// v2.4.0 +var isIdenticalFile = CryptoNetUtils.ByteArrayCompare(pdfFileBytes, decrypt); + +// v3.4.3 +var isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(pdfFileBytes, decrypt); +``` + +**Change:** The helper moved from `CryptoNetUtils` to `ExtShared.ExtShared`. + +**Action:** Replace the call and ensure `ExtShared` is referenced in your project. + +### 2.5 UniqueKeyGenerator + +The `UniqueKeyGenerator` function is unchanged between versions; no migration is needed here. + +### 2.6 AES Migration Checklist + +- [ ] Update namespace to `CryptoNet.Examples`. +- [ ] Replace AES key export calls: + - `ICryptoNet` → `ICryptoNetAes` for key retrieval/saving. + - `ExportKey()` → `GetKey()`. + - `ExportKeyAndSave(file)` → `SaveKey(file)`. +- [ ] Update Example 5 method name if you reference it. +- [ ] Replace `CryptoNetUtils.ByteArrayCompare` with `ExtShared.ExtShared.ByteArrayCompare`. +- [ ] Run all AES examples and confirm `Debug.Assert` checks still pass. + +--- + +## 3. RSA Migration (ExampleRsa) + +### 3.1 Overview + +**v2.4.0** + +```csharp +using CryptoNet.Models; +using CryptoNet.Utils; + +namespace CryptoNet.Cli; +``` + +**v3.4.3** + +```csharp +using CryptoNet.Models; + +namespace CryptoNet.Examples; +``` + +The RSA encryption/decryption calls via `ICryptoNet` remain conceptually the same. The major changes are centralized in **key management APIs** and **certificate utilities**. + +### 3.2 RSA key management API changes + +#### 3.2.1 Generating keys in memory + +```csharp +// v2.4.0 +ICryptoNet cryptoNet = new CryptoNetRsa(); + +var privateKey = cryptoNet.ExportKey(true); +var publicKey = cryptoNet.ExportKey(false); + +// v3.4.3 +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); + +var privateKey = cryptoNet.GetKey(true); +var publicKey = cryptoNet.GetKey(false); +``` + +**Changes:** +- Interface: `ICryptoNet` → `ICryptoNetRsa` for RSA key retrieval. +- Method: `ExportKey(bool)` → `GetKey(bool)`. + +**Action:** +- When working with RSA keys (as strings), migrate to `ICryptoNetRsa` and `GetKey(...)`. + +Encryption/decryption continues to use `ICryptoNet` with `CryptoNetRsa(key)`: + +```csharp +ICryptoNet encryptClient = new CryptoNetRsa(publicKey); +var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + +ICryptoNet decryptClient = new CryptoNetRsa(privateKey); +var decrypt = decryptClient.DecryptToString(encrypt); +``` + +#### 3.2.2 Saving keys to disk + +```csharp +// v2.4.0 +ICryptoNet cryptoNet = new CryptoNetRsa(); + +cryptoNet.ExportKeyAndSave(new FileInfo(PrivateKeyFile), true); +cryptoNet.ExportKeyAndSave(new FileInfo(PublicKeyFile), false); + +// v3.4.3 +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); + +cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); +cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); +``` + +**Changes:** +- Interface: `ICryptoNet` → `ICryptoNetRsa`. +- Method: `ExportKeyAndSave(FileInfo, bool)` → `SaveKey(FileInfo, bool)`. + +**Action:** +- Update interface type and method names anywhere you persist RSA keys. + +### 3.3 X509 certificate usage + +#### 3.3.1 Certificate lookup (Examples 4, 5, 7) + +```csharp +// v2.4.0 +// Find and replace CN=Maytham with your own certificate +X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); + +// v3.4.3 +// Find and replace CN=localhost with your own certificate +X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); +``` + +**Changes:** +- Utility: `CryptoNetUtils.GetCertificateFromStore` → `ExtShared.ExtShared.GetCertificateFromStore`. +- Example subject string changed from `"CN=Maytham"` → `"CN=localhost"` (documentation/sample only). + +**Action:** +- Replace all `CryptoNetUtils.GetCertificateFromStore` calls with `ExtShared.ExtShared.GetCertificateFromStore`. +- Adjust the subject string to match your certificate (e.g. `"CN=yourcert"`). + +This affects: + +- `Example_4_Using_X509_Certificate` +- `Example_5_Export_Public_Key_For_X509_Certificate` +- `Example_7_Customize` + +#### 3.3.2 Exporting public key from certificate (Example 5) + +```csharp +// v2.4.0 +X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); + +ICryptoNet cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); +var publicKey = cryptoNetWithPublicKey.ExportKey(false); + +// v3.4.3 +X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + +ICryptoNetRsa cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); +var publicKey = cryptoNetWithPublicKey.GetKey(false); +``` + +**Changes:** +- Certificate retrieval via `ExtShared.ExtShared` instead of `CryptoNetUtils`. +- Interface: `ICryptoNet` → `ICryptoNetRsa` for key extraction. +- Method: `ExportKey(false)` → `GetKey(false)`. + +**Action:** +- Use `ICryptoNetRsa` + `GetKey(false)` when exporting the RSA public key from an X509 certificate. + +### 3.4 Examples 3 & 7 and helpers + +- `Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content`: + - Constructs `CryptoNetRsa` with key files and uses `ICryptoNet` for encryption/decryption. + - No signature or behavioral change; no migration needed. + +- `Example_7_Customize`: + - `ExportPemCertificate`, `ExportPemKey`, `ImportPemKey`, `ExportPemKeyWithPassword`, `ImportPemKeyWithPassword` are unchanged. + - Only difference is certificate fetching: + - `CryptoNetUtils.GetCertificateFromStore` → `ExtShared.ExtShared.GetCertificateFromStore`. + +### 3.5 RSA Migration Checklist + +- [ ] Update namespace to `CryptoNet.Examples`. +- [ ] Replace RSA key-related usage: + - `ICryptoNet` → `ICryptoNetRsa` for key retrieval and saving. + - `ExportKey(bool)` → `GetKey(bool)`. + - `ExportKeyAndSave(FileInfo, bool)` → `SaveKey(FileInfo, bool)`. +- [ ] Update all certificate retrieval calls: + - `CryptoNetUtils.GetCertificateFromStore` → `ExtShared.ExtShared.GetCertificateFromStore`. +- [ ] For certificate-based public key export (Example 5), use `ICryptoNetRsa` and `GetKey(false)`. +- [ ] Run all RSA examples to confirm `Debug.Assert` checks still pass. + +--- + +## 4. Summary + +When migrating from **v2.4.0** to **v3.4.3**: + +- **Namespaces** moved from `CryptoNet.Cli` to `CryptoNet.Examples` for AES and RSA examples. +- **Key management responsibilities** were split into more specific interfaces: + - `ICryptoNetAes` for AES keys (`GetKey`, `SaveKey`). + - `ICryptoNetRsa` for RSA keys (`GetKey`, `SaveKey`). +- **Utility functions** were consolidated into `ExtShared.ExtShared` (byte array comparison, certificate lookup, etc.). +- Core encryption/decryption usage via `ICryptoNet` and `CryptoNetAes`/`CryptoNetRsa` with provided keys remains largely the same. + +After updating types and method names as outlined, you should be able to build against **v3.4.3** and run the AES and RSA examples successfully. diff --git a/docs/toc.yml b/docs/toc.yml index d7e9ea8..617d121 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -1,4 +1,8 @@ - name: Introduction href: introduction.md - name: Getting Started - href: getting-started.md \ No newline at end of file + href: getting-started.md +- name: Migration Guide from v2.x to v3.x + href: migration.md +- name: Examples of Use + href: examples.md \ No newline at end of file From f3b8fb319160ac21226512bfcf26ab33cbbf028c Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Mon, 8 Dec 2025 20:49:19 +0100 Subject: [PATCH 2/3] #163 Support UTF-8 and special character encryption Added `Example_6_Wokring_With_Special_Character_Text` to demonstrate encryption and decryption of text with special characters (e.g., emojis) using UTF-8 encoding. Updated `Program.cs` to include the new example and re-enable previously commented-out AES and RSA examples. Modified `BytesToString` and `StringToBytes` in `ExtShared.cs` to use UTF-8 encoding instead of ASCII for better handling of non-ASCII characters. Updated `migration.md` with a TODO note about encrypting strings with special characters and suggested using byte arrays for clarity. --- CryptoNet.Examples/ExampleRsa.cs | 18 ++++++++++++ CryptoNet.Examples/Program.cs | 49 +++++++++++--------------------- CryptoNet.ExtShared/ExtShared.cs | 12 ++++---- docs/migration.md | 1 + 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/CryptoNet.Examples/ExampleRsa.cs b/CryptoNet.Examples/ExampleRsa.cs index fd04096..30f361c 100644 --- a/CryptoNet.Examples/ExampleRsa.cs +++ b/CryptoNet.Examples/ExampleRsa.cs @@ -92,6 +92,24 @@ public static void Example_5_Export_Public_Key_For_X509_Certificate() Debug.Assert(!string.IsNullOrEmpty(publicKey)); } + public static void Example_6_Wokring_With_Special_Character_Text() + { + string ConfidentialDummyDataWithSpecialText = "Top secret 😃😃"; // + + ICryptoNetRsa cryptoNet = new CryptoNetRsa(); + var privateKey = cryptoNet.GetKey(true); + var publicKey = cryptoNet.GetKey(false); + + ICryptoNet encryptClient = new CryptoNetRsa(publicKey); + var encrypted = encryptClient.EncryptFromBytes(Encoding.UTF8.GetBytes(ConfidentialDummyDataWithSpecialText)); + + ICryptoNet decryptClient = new CryptoNetRsa(privateKey); + var decrypted = decryptClient.DecryptToBytes(encrypted); + var decryptedString = Encoding.UTF8.GetString(decrypted); + + Debug.Assert(ConfidentialDummyDataWithSpecialText == decryptedString); + } + /// /// CryptoNet interact with .net 5/6 for customization, like import/export PEM /// Work in Progress, not finished diff --git a/CryptoNet.Examples/Program.cs b/CryptoNet.Examples/Program.cs index 397f65a..6fa5bd6 100644 --- a/CryptoNet.Examples/Program.cs +++ b/CryptoNet.Examples/Program.cs @@ -1,38 +1,23 @@ -//using CryptoNet; -//using System.Diagnostics; - -//const string ConfidentialDummyData = "Top secret 🤣🤣"; // - -//ICryptoNetAes cryptoNet = new CryptoNetAes(); -//var key = cryptoNet.GetKey(); - -//ICryptoNet encryptClient = new CryptoNetAes(key); -//var encrypted = encryptClient.EncryptFromBytes(ConfidentialDummyData); - -//ICryptoNet decryptClient = new CryptoNetAes(key); -//var decrypted = decryptClient.DecryptToString(encrypted); - -//Debug.Assert(ConfidentialDummyData == decrypted); - -using CryptoNet.Examples; +using CryptoNet.Examples; ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); -//ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); -//ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); -//ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); -//ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); -//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.docx"); -//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.xlsx"); -//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.pdf"); -//ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.png"); - -//ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); -//ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); -//ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); -//ExampleRsa.Example_4_Using_X509_Certificate(); -//ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); -//ExampleRsa.Example_7_Customize(); +ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); +ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); +ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); +ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); +ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.docx"); +ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.xlsx"); +ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.pdf"); +ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.png"); + +ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); +ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); +ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); +ExampleRsa.Example_4_Using_X509_Certificate(); +ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); +ExampleRsa.Example_6_Wokring_With_Special_Character_Text(); +ExampleRsa.Example_7_Customize(); diff --git a/CryptoNet.ExtShared/ExtShared.cs b/CryptoNet.ExtShared/ExtShared.cs index 88263c6..a4b3f28 100644 --- a/CryptoNet.ExtShared/ExtShared.cs +++ b/CryptoNet.ExtShared/ExtShared.cs @@ -106,23 +106,23 @@ public static RSAParameters GetParameters(X509Certificate2? certificate, KeyType } /// - /// Converts a byte array to an ASCII-encoded string. + /// Converts a byte array to an UTF8-encoded string. /// /// The byte array to convert. - /// An ASCII-encoded string representation of the byte array. + /// An UTF8-encoded string representation of the byte array. public static string BytesToString(byte[] bytes) { - return Encoding.ASCII.GetString(bytes); + return Encoding.UTF8.GetString(bytes); } /// - /// Converts an ASCII-encoded string to a byte array. + /// Converts an UTF8-encoded string to a byte array. /// /// The string to convert. - /// A byte array representing the ASCII-encoded string. + /// A byte array representing the UTF8-encoded string. public static byte[] StringToBytes(string content) { - return Encoding.ASCII.GetBytes(content); + return Encoding.UTF8.GetBytes(content); } /// diff --git a/docs/migration.md b/docs/migration.md index 2e70e0c..5b2f412 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -3,6 +3,7 @@ This document describes the main breaking and behavioral changes for the **AES** and **RSA** examples when migrating from **v2.4.0** to **v3.4.3** of CryptoNet. --- +TODO: Issue encrypting 🤣🤣 in case of string, use bytes. an example to clarify this. ## 1. Common Changes From be41d41ceb4a0b2e112c6d8f9efc77629759ac3f Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Mon, 8 Dec 2025 21:27:50 +0100 Subject: [PATCH 3/3] #163 Refactor examples, docs, and improve developer tooling Refactored example methods to use PascalCase, added XML comments, and improved variable naming for clarity. Standardized key management APIs across AES, DSA, and RSA examples. Enhanced file encryption examples to support arbitrary file types. Updated documentation with detailed examples, migration guides, and a new `tooling.md` for developer workflows. Improved CI/CD documentation and reusable workflow examples in `CONTRIBUTING.md`. Added support for special characters in encryption examples and advanced PEM operations. Updated project structure to include a "Docs" project and additional documentation files. Enhanced code readability, consistency, and developer experience. --- CONTRIBUTING.md | 12 +- CryptoNet.Examples/ExampleAes.cs | 119 ++++++++++------ CryptoNet.Examples/ExampleDsa.cs | 51 ++++--- CryptoNet.Examples/ExampleRsa.cs | 198 ++++++++++++++++---------- CryptoNet.Examples/Program.cs | 35 ++--- CryptoNet.sln | 12 +- README.md | 124 +---------------- docs/examples.md | 231 ++++++++++++++----------------- docs/getting-started.md | 16 +-- docs/introduction.md | 50 ++++--- docs/migration.md | 62 ++++----- docs/tooling.md | 150 ++++++++++++++++++++ 12 files changed, 585 insertions(+), 475 deletions(-) create mode 100644 docs/tooling.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff51c79..144eac0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,18 +18,18 @@ contributors must follow to avoid common build and packaging failures. ### Example: Caller (no `runs-on`) -``` yaml +~~~ yaml jobs: call-build: uses: ./.github/workflows/cd-build-test-pack.yml with: version: ${{ github.ref_name }} configuration: Release -``` +~~~ ### Example: Reusable Workflow -``` yaml +~~~ yaml on: workflow_call: inputs: @@ -48,7 +48,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 # � -``` +~~~ ## Versioning and Git Tag Guidelines @@ -78,7 +78,7 @@ jobs: ### Sanitization Example -``` yaml +~~~ yaml - name: Sanitize version id: sanitize run: | @@ -86,7 +86,7 @@ jobs: v="${v#v}" v="${v#V}" echo "sanitized_version=$v" >> "$GITHUB_OUTPUT" -``` +~~~ Use via: `${{ steps.sanitize.outputs.sanitized_version }}` diff --git a/CryptoNet.Examples/ExampleAes.cs b/CryptoNet.Examples/ExampleAes.cs index 9c8d02b..64a0dc9 100644 --- a/CryptoNet.Examples/ExampleAes.cs +++ b/CryptoNet.Examples/ExampleAes.cs @@ -12,119 +12,146 @@ namespace CryptoNet.Examples; +/// +/// Example usage scenarios for AES symmetric encryption using the CryptoNet library. +/// Demonstrates generating, saving/loading symmetric keys and encrypting/decrypting +/// both content and files. +/// public static class ExampleAes { private const string ConfidentialDummyData = @"Some Secret Data"; - private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; - private readonly static string SymmetricKeyFile = Path.Combine(BaseFolder, $"{KeyType.SymmetricKey}.xml"); + private static readonly string BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string SymmetricKeyFilePath = Path.Combine(BaseDirectory, $"{KeyType.SymmetricKey}.xml"); - public static void Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey() + /// + /// Generate a symmetric key, encrypt and decrypt a string using self-generated key. + /// + public static void EncryptDecryptWithSelfGeneratedSymmetricKey() { ICryptoNetAes cryptoNet = new CryptoNetAes(); - var key = cryptoNet.GetKey(); + string key = cryptoNet.GetKey(); ICryptoNet encryptClient = new CryptoNetAes(key); - var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key); - var decrypt = decryptClient.DecryptToString(encrypt); + string decrypted = decryptClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_2_SelfGenerated_And_Save_SymmetricKey() + /// + /// Generate a symmetric key and save it to disk, then load it to decrypt previously encrypted content. + /// + public static void GenerateAndSaveSymmetricKey() { ICryptoNetAes cryptoNet = new CryptoNetAes(); - var file = new FileInfo(SymmetricKeyFile); - cryptoNet.SaveKey(file); + FileInfo keyFile = new FileInfo(SymmetricKeyFilePath); + cryptoNet.SaveKey(keyFile); - Debug.Assert(File.Exists(file.FullName)); + Debug.Assert(File.Exists(keyFile.FullName)); - var encrypt = cryptoNet.EncryptFromString(ConfidentialDummyData); + byte[] encrypted = cryptoNet.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNetKeyImport = new CryptoNetAes(file); - var decrypt = cryptoNetKeyImport.DecryptToString(encrypt); + ICryptoNet cryptoNetImported = new CryptoNetAes(keyFile); + string decrypted = cryptoNetImported.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey() + /// + /// Encrypt and decrypt using a provided raw symmetric key and IV. + /// + public static void EncryptDecryptWithProvidedSymmetricKey() { - var symmetricKey = "12345678901234567890123456789012"; + string symmetricKey = "12345678901234567890123456789012"; if (symmetricKey.Length != 32) { - Console.WriteLine("key should be 32 character long"); + Console.WriteLine("Key should be 32 characters long"); Environment.Exit(0); } - var secret = "1234567890123456"; + string secret = "1234567890123456"; if (secret.Length != 16) { - Console.WriteLine("key should be 16 character long"); + Console.WriteLine("IV should be 16 characters long"); Environment.Exit(1); } - var key = Encoding.UTF8.GetBytes(symmetricKey); - var iv = Encoding.UTF8.GetBytes(secret); + byte[] key = Encoding.UTF8.GetBytes(symmetricKey); + byte[] iv = Encoding.UTF8.GetBytes(secret); ICryptoNet encryptClient = new CryptoNetAes(key, iv); - var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key, iv); - var decrypt = decryptClient.DecryptToString(encrypt); + string decrypted = decryptClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey() + /// + /// Generate a human-readable key/secret pair and use them for encryption/decryption. + /// + public static void EncryptDecryptWithHumanReadableKeySecret() { - var symmetricKey = UniqueKeyGenerator("symmetricKey"); - var secret = new string(UniqueKeyGenerator("password").Take(16).ToArray()); + string symmetricKey = GenerateUniqueKey("symmetricKey"); + string secret = new string(GenerateUniqueKey("password").Take(16).ToArray()); - var key = Encoding.UTF8.GetBytes(symmetricKey); - var iv = Encoding.UTF8.GetBytes(secret); + byte[] key = Encoding.UTF8.GetBytes(symmetricKey); + byte[] iv = Encoding.UTF8.GetBytes(secret); ICryptoNet encryptClient = new CryptoNetAes(key, iv); - var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key, iv); - var decrypt = decryptClient.DecryptToString(encrypt); + string decrypted = decryptClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test(string filename) + /// + /// Encrypt and decrypt a file using a self-generated symmetric key and verify content equality. + /// + /// Relative path under TestFiles directory to the file to encrypt. + public static void EncryptAndDecryptFileWithSymmetricKeyTest(string filename) { ICryptoNetAes cryptoNet = new CryptoNetAes(); - var key = cryptoNet.GetKey(); + string key = cryptoNet.GetKey(); - FileInfo fi = new FileInfo(filename); + FileInfo fileInfo = new FileInfo(filename); ICryptoNet encryptClient = new CryptoNetAes(key); - string pdfFilePath = Path.Combine(BaseFolder, filename); - byte[] pdfFileBytes = File.ReadAllBytes(pdfFilePath); - var encrypt = encryptClient.EncryptFromBytes(pdfFileBytes); + string sourceFilePath = Path.Combine(BaseDirectory, filename); + byte[] fileBytes = File.ReadAllBytes(sourceFilePath); + byte[] encrypted = encryptClient.EncryptFromBytes(fileBytes); ICryptoNet decryptClient = new CryptoNetAes(key); - var decrypt = decryptClient.DecryptToBytes(encrypt); - string pdfDecryptedFilePath = $"TestFiles\\{Path.GetFileNameWithoutExtension(fi.Name)}-decrypted.{fi.Extension}"; - File.WriteAllBytes(pdfDecryptedFilePath, decrypt); + byte[] decrypted = decryptClient.DecryptToBytes(encrypted); + string decryptedFilePath = $"TestFiles\\{Path.GetFileNameWithoutExtension(fileInfo.Name)}-decrypted{fileInfo.Extension}"; + File.WriteAllBytes(decryptedFilePath, decrypted); - var isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(pdfFileBytes, decrypt); + bool isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(fileBytes, decrypted); Debug.Assert(isIdenticalFile); } - public static string UniqueKeyGenerator(string input) + /// + /// Create a deterministic unique key string from input using MD5 (example helper). + /// + /// Input string to derive the key from. + /// Hexadecimal string representation of MD5 hash. + public static string GenerateUniqueKey(string input) { byte[] inputBytes = Encoding.ASCII.GetBytes(input); byte[] hash = MD5.HashData(inputBytes); StringBuilder sb = new StringBuilder(); - foreach (var t in hash) + foreach (byte b in hash) { - sb.Append(t.ToString("X2")); + sb.Append(b.ToString("X2")); } + return sb.ToString(); } } \ No newline at end of file diff --git a/CryptoNet.Examples/ExampleDsa.cs b/CryptoNet.Examples/ExampleDsa.cs index 4e30ff1..88f6b61 100644 --- a/CryptoNet.Examples/ExampleDsa.cs +++ b/CryptoNet.Examples/ExampleDsa.cs @@ -13,48 +13,57 @@ namespace CryptoNet.Examples; +/// +/// Example usage scenarios for DSA operations using the CryptoNet library. +/// Demonstrates signing, verification and saving/loading DSA keys. +/// public static class ExampleDsa { private const string ConfidentialDummyData = @"Some Secret Data"; - private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; - internal static readonly string PrivateKeyFile = Path.Combine(BaseFolder, "privateKey"); - internal static readonly string PublicKeyFile = Path.Combine(BaseFolder, "publicKey.pub"); + internal static readonly string PrivateKeyFilePath = Path.Combine(BaseDirectory, "privateKey"); + internal static readonly string PublicKeyFilePath = Path.Combine(BaseDirectory, "publicKey.pub"); - public static void Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey() + /// + /// Demonstrates signing content with a self-generated DSA key and validating the signature. + /// + public static void SignAndValidateWithSelfGeneratedKey() { ICryptoNetDsa client = new CryptoNetDsa(); - var privateKey = client.GetKey(true); + string privateKeyXml = client.GetKey(true); - ICryptoNetDsa signatureClient = new CryptoNetDsa(privateKey); - var signature = signatureClient.CreateSignature(ConfidentialDummyData); + ICryptoNetDsa signatureClient = new CryptoNetDsa(privateKeyXml); + byte[] signature = signatureClient.CreateSignature(ConfidentialDummyData); - ICryptoNetDsa verifyClient = new CryptoNetDsa(privateKey); - var confidentialAsBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); + ICryptoNetDsa verifyClient = new CryptoNetDsa(privateKeyXml); + byte[] contentBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); - bool isVerified = verifyClient.IsContentVerified(confidentialAsBytes, signature); + bool isVerified = verifyClient.IsContentVerified(contentBytes, signature); Debug.Assert(isVerified == true); } - public static void Example_2_SelfGenerated_And_Save_AsymmetricKey() + /// + /// Demonstrates generating DSA key pair, saving them to files, and verifying signatures using loaded keys. + /// + public static void GenerateAndSaveDsaKeyPair() { ICryptoNetDsa cryptoNet = new CryptoNetDsa(); - cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); - cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + cryptoNet.SaveKey(new FileInfo(PrivateKeyFilePath), true); + cryptoNet.SaveKey(new FileInfo(PublicKeyFilePath), false); - Debug.Assert(File.Exists(new FileInfo(PrivateKeyFile).FullName)); - Debug.Assert(File.Exists(new FileInfo(PublicKeyFile).FullName)); + Debug.Assert(File.Exists(new FileInfo(PrivateKeyFilePath).FullName)); + Debug.Assert(File.Exists(new FileInfo(PublicKeyFilePath).FullName)); - ICryptoNetDsa dsaWithPrivateKey = new CryptoNetDsa(new FileInfo(PrivateKeyFile)); - var signature = dsaWithPrivateKey.CreateSignature(ConfidentialDummyData); + ICryptoNetDsa dsaWithPrivateKey = new CryptoNetDsa(new FileInfo(PrivateKeyFilePath)); + byte[] signature = dsaWithPrivateKey.CreateSignature(ConfidentialDummyData); - ICryptoNetDsa dsaWithPublicKey = new CryptoNetDsa(new FileInfo(PublicKeyFile)); - var confidentialAsBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); - var isVerified = dsaWithPublicKey.IsContentVerified(confidentialAsBytes, signature); + ICryptoNetDsa dsaWithPublicKey = new CryptoNetDsa(new FileInfo(PublicKeyFilePath)); + byte[] confidentialBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData); + bool isVerified = dsaWithPublicKey.IsContentVerified(confidentialBytes, signature); Debug.Assert(isVerified == true); } - } \ No newline at end of file diff --git a/CryptoNet.Examples/ExampleRsa.cs b/CryptoNet.Examples/ExampleRsa.cs index 30f361c..6baa1c0 100644 --- a/CryptoNet.Examples/ExampleRsa.cs +++ b/CryptoNet.Examples/ExampleRsa.cs @@ -13,133 +13,164 @@ namespace CryptoNet.Examples; +/// +/// Example usage scenarios for RSA operations using the CryptoNet library. +/// Methods demonstrate generating keys, saving/loading keys, encrypting/decrypting +/// content and working with X509 certificates and PEM formats. +/// public static class ExampleRsa { private const string ConfidentialDummyData = @"Some Secret Data"; - private static readonly string BaseFolder = AppDomain.CurrentDomain.BaseDirectory; + private static readonly string BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; - internal static readonly string PrivateKeyFile = Path.Combine(BaseFolder, "privateKey"); - internal static readonly string PublicKeyFile = Path.Combine(BaseFolder, "publicKey.pub"); + internal static readonly string PrivateKeyFilePath = Path.Combine(BaseDirectory, "privateKey"); + internal static readonly string PublicKeyFilePath = Path.Combine(BaseDirectory, "publicKey.pub"); - public static void Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey() + /// + /// Demonstrates generating a self-contained RSA key pair and using them to + /// encrypt and decrypt a string. + /// + public static void EncryptDecryptWithSelfGeneratedKey() { ICryptoNetRsa cryptoNet = new CryptoNetRsa(); - var privateKey = cryptoNet.GetKey(true); - var publicKey = cryptoNet.GetKey(false); + string privateKeyXml = cryptoNet.GetKey(true); + string publicKeyXml = cryptoNet.GetKey(false); - ICryptoNet encryptClient = new CryptoNetRsa(publicKey); - var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); + ICryptoNet encryptClient = new CryptoNetRsa(publicKeyXml); + byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData); - ICryptoNet decryptClient = new CryptoNetRsa(privateKey); - var decrypt = decryptClient.DecryptToString(encrypt); + ICryptoNet decryptClient = new CryptoNetRsa(privateKeyXml); + string decrypted = decryptClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_2_SelfGenerated_And_Save_AsymmetricKey() + /// + /// Demonstrates generating keys and saving them to files, then loading + /// those files to perform encryption/decryption. + /// + public static void GenerateAndSaveAsymmetricKey() { ICryptoNetRsa cryptoNet = new CryptoNetRsa(); - cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); - cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); + cryptoNet.SaveKey(new FileInfo(PrivateKeyFilePath), true); + cryptoNet.SaveKey(new FileInfo(PublicKeyFilePath), false); - Debug.Assert(File.Exists(new FileInfo(PrivateKeyFile).FullName)); - Debug.Assert(File.Exists(new FileInfo(PublicKeyFile).FullName)); + Debug.Assert(File.Exists(new FileInfo(PrivateKeyFilePath).FullName)); + Debug.Assert(File.Exists(new FileInfo(PublicKeyFilePath).FullName)); - ICryptoNet cryptoNetPubKey = new CryptoNetRsa(new FileInfo(PublicKeyFile)); - var encrypt = cryptoNetPubKey.EncryptFromString(ConfidentialDummyData); + ICryptoNet publicKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFilePath)); + byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNetPriKey = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); - var decrypt = cryptoNetPriKey.DecryptToString(encrypt); + ICryptoNet privateKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFilePath)); + string decrypted = privateKeyClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decrypt); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content() + /// + /// Encrypts with a public key file and decrypts with a private key file. + /// + public static void EncryptWithPublicKeyAndDecryptWithPrivateKey() { - ICryptoNet cryptoNetWithPublicKey = new CryptoNetRsa(new FileInfo(PublicKeyFile)); - var encryptWithPublicKey = cryptoNetWithPublicKey.EncryptFromString(ConfidentialDummyData); + ICryptoNet publicKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFilePath)); + byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNetWithPrivateKey = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); - var decryptWithPrivateKey = cryptoNetWithPrivateKey.DecryptToString(encryptWithPublicKey); + ICryptoNet privateKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFilePath)); + string decrypted = privateKeyClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decryptWithPrivateKey); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_4_Using_X509_Certificate() + /// + /// Demonstrates using an X509 certificate's public/private key for encryption/decryption. + /// Replace the subject name with a certificate available on your machine. + /// + public static void UseX509Certificate() { - // Find and replace CN=localhost with your own certificate + // Replace subject with your certificate subject (for example "CN=localhost") X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); - ICryptoNet cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); - var encryptWithPublicKey = cryptoNetWithPublicKey.EncryptFromString(ConfidentialDummyData); + ICryptoNet publicKeyClient = new CryptoNetRsa(certificate, KeyType.PublicKey); + byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNetWithPrivateKey = new CryptoNetRsa(certificate, KeyType.PrivateKey); - var decryptWithPrivateKey = cryptoNetWithPrivateKey.DecryptToString(encryptWithPublicKey); + ICryptoNet privateKeyClient = new CryptoNetRsa(certificate, KeyType.PrivateKey); + string decrypted = privateKeyClient.DecryptToString(encrypted); - Debug.Assert(ConfidentialDummyData == decryptWithPrivateKey); + Debug.Assert(ConfidentialDummyData == decrypted); } - public static void Example_5_Export_Public_Key_For_X509_Certificate() + /// + /// Exports the public key portion from an X509 certificate using the CryptoNetRsa GetKey API. + /// + public static void ExportPublicKeyFromX509Certificate() { - // Find and replace CN=localhost with your own certificate + // Replace subject with your certificate subject (for example "CN=localhost") X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); - ICryptoNetRsa cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); - var publicKey = cryptoNetWithPublicKey.GetKey(false); + ICryptoNetRsa certClient = new CryptoNetRsa(certificate, KeyType.PublicKey); + string publicKeyXml = certClient.GetKey(false); - Debug.Assert(!string.IsNullOrEmpty(publicKey)); + Debug.Assert(!string.IsNullOrEmpty(publicKeyXml)); } - public static void Example_6_Wokring_With_Special_Character_Text() + /// + /// Demonstrates encrypting/decrypting content that includes special (emoji) characters. + /// Ensures UTF-8 byte-level operations are used. + /// + public static void WorkWithSpecialCharacterText() { - string ConfidentialDummyDataWithSpecialText = "Top secret 😃😃"; // + string confidentialWithSpecialChars = "Top secret 😃😃"; ICryptoNetRsa cryptoNet = new CryptoNetRsa(); - var privateKey = cryptoNet.GetKey(true); - var publicKey = cryptoNet.GetKey(false); + string privateKeyXml = cryptoNet.GetKey(true); + string publicKeyXml = cryptoNet.GetKey(false); - ICryptoNet encryptClient = new CryptoNetRsa(publicKey); - var encrypted = encryptClient.EncryptFromBytes(Encoding.UTF8.GetBytes(ConfidentialDummyDataWithSpecialText)); + ICryptoNet encryptClient = new CryptoNetRsa(publicKeyXml); + byte[] encrypted = encryptClient.EncryptFromBytes(Encoding.UTF8.GetBytes(confidentialWithSpecialChars)); - ICryptoNet decryptClient = new CryptoNetRsa(privateKey); - var decrypted = decryptClient.DecryptToBytes(encrypted); - var decryptedString = Encoding.UTF8.GetString(decrypted); + ICryptoNet decryptClient = new CryptoNetRsa(privateKeyXml); + byte[] decryptedBytes = decryptClient.DecryptToBytes(encrypted); + string decryptedString = Encoding.UTF8.GetString(decryptedBytes); - Debug.Assert(ConfidentialDummyDataWithSpecialText == decryptedString); + Debug.Assert(confidentialWithSpecialChars == decryptedString); } /// - /// CryptoNet interact with .net 5/6 for customization, like import/export PEM - /// Work in Progress, not finished + /// Work-in-progress: demonstrates PEM export/import and encrypted PEM import with password. /// - public static void Example_7_Customize() + public static void CustomizePemExamples() { - X509Certificate2? cert = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); + X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); - var pubKeyPem = ExportPemKey(cert!, false); - var priKeyPem = ExportPemKey(cert!); + char[] pubKeyPem = ExportPemKey(certificate!, privateKey: false); + char[] priKeyPem = ExportPemKey(certificate!); - var password = "password"; - var encryptedPriKeyBytes = ExportPemKeyWithPassword(cert!, password); + string password = "password"; + byte[] encryptedPriKeyBytes = ExportPemKeyWithPassword(certificate!, password); - ICryptoNet cryptoNet1 = ImportPemKeyWithPassword(encryptedPriKeyBytes, password); - var encrypt1 = cryptoNet1.EncryptFromString(ConfidentialDummyData); + ICryptoNet decryptClientWithPassword = ImportPemKeyWithPassword(encryptedPriKeyBytes, password); + byte[] encrypted1 = decryptClientWithPassword.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNet2 = ImportPemKey(pubKeyPem); - var encrypt2 = cryptoNet2.EncryptFromString(ConfidentialDummyData); + ICryptoNet encryptClientFromPubPem = ImportPemKey(pubKeyPem); + byte[] encrypted2 = encryptClientFromPubPem.EncryptFromString(ConfidentialDummyData); - ICryptoNet cryptoNet3 = ImportPemKey(priKeyPem); - var decrypt2 = cryptoNet3.DecryptToString(encrypt2); + ICryptoNet decryptClientFromPriPem = ImportPemKey(priKeyPem); + string decrypted2 = decryptClientFromPriPem.DecryptToString(encrypted2); - Debug.Assert(ConfidentialDummyData == decrypt2); + Debug.Assert(ConfidentialDummyData == decrypted2); - var decrypt1 = cryptoNet3.DecryptToString(encrypt1); + string decrypted1 = decryptClientFromPriPem.DecryptToString(encrypted1); - Debug.Assert(ConfidentialDummyData == decrypt1); + Debug.Assert(ConfidentialDummyData == decrypted1); } + /// + /// Export a certificate as PEM-encoded character array. + /// + /// Certificate to export. + /// PEM characters for the certificate. public static char[] ExportPemCertificate(X509Certificate2 cert) { byte[] certBytes = cert!.RawData; @@ -147,20 +178,31 @@ public static char[] ExportPemCertificate(X509Certificate2 cert) return certPem; } + /// + /// Export a certificate's RSA key as PEM (public or private). + /// + /// Certificate containing the RSA key. + /// True to export private key, false for public key. + /// PEM characters for the key. public static char[] ExportPemKey(X509Certificate2 cert, bool privateKey = true) { AsymmetricAlgorithm rsa = cert.GetRSAPrivateKey()!; if (privateKey) { - byte[] priKeyBytes = rsa.ExportPkcs8PrivateKey(); - return PemEncoding.Write("PRIVATE KEY", priKeyBytes); + byte[] privateKeyBytes = rsa.ExportPkcs8PrivateKey(); + return PemEncoding.Write("PRIVATE KEY", privateKeyBytes); } - byte[] pubKeyBytes = rsa.ExportSubjectPublicKeyInfo(); - return PemEncoding.Write("PUBLIC KEY", pubKeyBytes); + byte[] publicKeyBytes = rsa.ExportSubjectPublicKeyInfo(); + return PemEncoding.Write("PUBLIC KEY", publicKeyBytes); } + /// + /// Import a PEM key into a newly created CryptoNetRsa instance. + /// + /// PEM characters representing the key. + /// ICryptoNet instance with the imported key. public static ICryptoNet ImportPemKey(char[] key) { ICryptoNet cryptoNet = new CryptoNetRsa(); @@ -168,15 +210,27 @@ public static ICryptoNet ImportPemKey(char[] key) return cryptoNet; } + /// + /// Export an encrypted PKCS#8 private key using a password and AES-256-CBC PBES. + /// + /// Certificate containing the private key to export. + /// Password to encrypt the private key. + /// Encrypted private key bytes. public static byte[] ExportPemKeyWithPassword(X509Certificate2 cert, string password) { AsymmetricAlgorithm rsa = cert.GetRSAPrivateKey()!; - byte[] pass = Encoding.UTF8.GetBytes(password); - byte[] encryptedPrivateKey = rsa.ExportEncryptedPkcs8PrivateKey(pass, + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + byte[] encryptedPrivateKey = rsa.ExportEncryptedPkcs8PrivateKey(passwordBytes, new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, iterationCount: 100_000)); return encryptedPrivateKey; } + /// + /// Import an encrypted PKCS#8 private key using the provided password into a new CryptoNetRsa instance. + /// + /// Encrypted PKCS#8 private key bytes. + /// Password used to decrypt the private key. + /// ICryptoNet instance with imported private key. public static ICryptoNet ImportPemKeyWithPassword(byte[] encryptedPrivateKey, string password) { ICryptoNet cryptoNet = new CryptoNetRsa(); diff --git a/CryptoNet.Examples/Program.cs b/CryptoNet.Examples/Program.cs index 6fa5bd6..61117ff 100644 --- a/CryptoNet.Examples/Program.cs +++ b/CryptoNet.Examples/Program.cs @@ -1,23 +1,24 @@ using CryptoNet.Examples; -ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); -ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); +ExampleDsa.SignAndValidateWithSelfGeneratedKey(); +ExampleDsa.GenerateAndSaveDsaKeyPair(); -ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); -ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); -ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); -ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); -ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.docx"); -ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.xlsx"); -ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.pdf"); -ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("TestFiles\\test.png"); +ExampleAes.EncryptDecryptWithSelfGeneratedSymmetricKey(); +ExampleAes.GenerateAndSaveSymmetricKey(); +ExampleAes.EncryptDecryptWithProvidedSymmetricKey(); +ExampleAes.EncryptDecryptWithHumanReadableKeySecret(); +ExampleAes.EncryptAndDecryptFileWithSymmetricKeyTest("TestFiles\\test.docx"); +ExampleAes.EncryptAndDecryptFileWithSymmetricKeyTest("TestFiles\\test.xlsx"); +ExampleAes.EncryptAndDecryptFileWithSymmetricKeyTest("TestFiles\\test.pdf"); +ExampleAes.EncryptAndDecryptFileWithSymmetricKeyTest("TestFiles\\test.png"); -ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); -ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); -ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); -ExampleRsa.Example_4_Using_X509_Certificate(); -ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); -ExampleRsa.Example_6_Wokring_With_Special_Character_Text(); -ExampleRsa.Example_7_Customize(); +// Updated RSA example method names to follow PascalCase and remove underscores +ExampleRsa.EncryptDecryptWithSelfGeneratedKey(); +ExampleRsa.GenerateAndSaveAsymmetricKey(); +ExampleRsa.EncryptWithPublicKeyAndDecryptWithPrivateKey(); +ExampleRsa.UseX509Certificate(); +ExampleRsa.ExportPublicKeyFromX509Certificate(); +ExampleRsa.WorkWithSpecialCharacterText(); +ExampleRsa.CustomizePemExamples(); diff --git a/CryptoNet.sln b/CryptoNet.sln index b9c53e8..821f3f6 100644 --- a/CryptoNet.sln +++ b/CryptoNet.sln @@ -20,7 +20,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevOps", "DevOps", "{1733D1 Directory.Build.Props = Directory.Build.Props DockerBuild.ps1 = DockerBuild.ps1 Dockerfile = Dockerfile - RELEASE-NOTES = RELEASE-NOTES run_codecoverage.ps1 = run_codecoverage.ps1 run_codecoverage.txt = run_codecoverage.txt run_release.ps1 = run_release.ps1 @@ -28,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevOps", "DevOps", "{1733D1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Info", "Info", "{6976E2ED-A139-48C5-A390-639F56A8B348}" ProjectSection(SolutionItems) = preProject + CONTRIBUTING.md = CONTRIBUTING.md LICENSE = LICENSE README.md = README.md EndProjectSection @@ -55,6 +55,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CryptoNet.ConsumerTest", "C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CryptoNet.Examples", "CryptoNet.Examples\CryptoNet.Examples.csproj", "{CAEB7F60-5632-2F90-05B7-D72D7AD177FA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{B3B9ABB3-DBB7-4E0F-970C-761B0B1A4B81}" + ProjectSection(SolutionItems) = preProject + docs\examples.md = docs\examples.md + docs\getting-started.md = docs\getting-started.md + docs\introduction.md = docs\introduction.md + docs\migration.md = docs\migration.md + docs\toc.yml = docs\toc.yml + docs\tooling.md = docs\tooling.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/README.md b/README.md index f928c82..ba525d4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It does not depend on other libraries. You can install the CryptoNet NuGet package via [NuGet](https://www.nuget.org/packages/CryptoNet). -## Website +## Documentation https://itbackyard.github.io/CryptoNet @@ -41,125 +41,3 @@ CryptoNet Extensions v3: Please report issues [here](https://github.com/itbackyard/CryptoNet/issues). -## How to use -Ref to docs. - -## Build and Testing -You have different options to build and run the unit tests from: - 1. Visual Studio 2019/2022. - 2. Visual Studio Code. - 3. dotnet command line. - 4. dotnet commands are preserved in a PowerShell script ```build.ps1```. - 5. Docker, run the following command from the solution folder: - -``` -docker build . --file .\Dockerfile --tag cryptonet-service:latest -``` - -or run preserved PowerShell: - -```powershell -./run_docker_build.ps1 -``` - -## How to release a new version? - -Preview -``` -.\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $true -``` - -Release -``` -.\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $false -``` - -## Documentation Creation - -There are static and dynamically generated documentation. Both are published automatically in the pipeline called `5-static.yml`. - -To work with the documentation locally, the following might be relevant: - -- **Static base documentation** is located in the `docs` folder. -- **Dynamically generated documentation** is created from code using a tool called **DocFX**. - -### Running Documentation Creation Locally - -To generate the documentation locally on your computer, run: - -```powershell -.\run_docs.ps1 -``` - -### Setup - -1. Install the DocFX tool (only needs to be done once): - -``` -dotnet tool install -g docfx -``` - -2. The following step is already configured in this repository. However, if you need to start over, run the following to initialize and configure DocFX: - -``` -docfx init -y -``` - -## Update `index.md` with `README.md` - -To update `index.md` with the latest content from `README.md`, run the following command: - -```powershell -./run_update_index.ps1 -``` -This script will: - -1. Add the following header to the top of `index.md`: - -```yaml ---- -_layout: landing ---- -``` - -2. Append the content of `README.md` to `index.md`. - -This ensures that index.md is always up to date with the latest changes in `README.md`. - -## Code Coverage - -Code coverage ensures that your tests adequately cover your codebase, improving overall quality, reliability, and maintainability. Follow these steps to set up and generate code coverage reports. - -### Running Code Coverage Locally - -To generate code coverage reports locally on your computer, run the following command in Windows: - -```powershell -.\run_codecoverage.ps1 -``` - -### Setup - -If the required tools and packages are not set up locally, follow the steps below to configure them: - -1. Navigate to your test project directory (e.g., `CryptoNet.UnitTests`): - -```bash -cd .\CryptoNet.UnitTests\ -``` - -2. Add the necessary coverage packages to your test project: - -```bash -dotnet add package coverlet.collector -dotnet add package coverlet.msbuild -``` - -3. Install the report generator tool (only needs to be done once): - -```bash -dotnet tool install --global dotnet-reportgenerator-globaltool -``` - -Once set up, you can use these tools to analyze and generate detailed code coverage reports to ensure thorough testing of your application. - diff --git a/docs/examples.md b/docs/examples.md index a21eae4..31fd84c 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -8,15 +8,9 @@ This document shows **how to use CryptoNet** with the built-in examples for: All examples live under: -```csharp +~~~csharp namespace CryptoNet.Examples; -``` - -and use the shared dummy content: - -```csharp -private const string ConfidentialDummyData = @"Some Secret Data"; -``` +~~~ --- @@ -24,8 +18,8 @@ private const string ConfidentialDummyData = @"Some Secret Data"; ### 1.1. Encrypt/Decrypt a String with a Self-Generated Key -```csharp -// Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey +~~~csharp +// EncryptDecryptWithSelfGeneratedKey ICryptoNetAes cryptoNet = new CryptoNetAes(); var key = cryptoNet.GetKey(); @@ -34,19 +28,11 @@ var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key); var decrypt = decryptClient.DecryptToString(encrypt); -``` - -Run: - -```csharp -ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); -``` - ---- +~~~ ### 1.2. Generate & Save AES Key to File -```csharp +~~~csharp ICryptoNetAes cryptoNet = new CryptoNetAes(); var file = new FileInfo(SymmetricKeyFile); cryptoNet.SaveKey(file); @@ -55,19 +41,11 @@ var encrypt = cryptoNet.EncryptFromString(ConfidentialDummyData); ICryptoNet cryptoNetKeyImport = new CryptoNetAes(file); var decrypt = cryptoNetKeyImport.DecryptToString(encrypt); -``` - -Run: - -```csharp -ExampleAes.Example_2_SelfGenerated_And_Save_SymmetricKey(); -``` - ---- +~~~ ### 1.3. Encrypt/Decrypt Using Your Own Key and IV -```csharp +~~~csharp var key = Encoding.UTF8.GetBytes("32-char-string-key................"); var iv = Encoding.UTF8.GetBytes("16-char-secret.."); @@ -76,48 +54,26 @@ var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key, iv); var decrypt = decryptClient.DecryptToString(encrypt); -``` - -Run: - -```csharp -ExampleAes.Example_3_Encrypt_Decrypt_Content_With_Own_SymmetricKey(); -``` - ---- +~~~ ### 1.4. Human-Readable Key + IV -```csharp -var symmetricKey = UniqueKeyGenerator("symmetricKey"); -var secret = new string(UniqueKeyGenerator("password").Take(16).ToArray()); +~~~csharp +var symmetricKey = GenerateUniqueKey("symmetricKey"); +var secret = new string(GenerateUniqueKey("password").Take(16).ToArray()); ICryptoNet crypto = new CryptoNetAes( Encoding.UTF8.GetBytes(symmetricKey), Encoding.UTF8.GetBytes(secret)); -``` - -Run: - -```csharp -ExampleAes.Example_4_Encrypt_Decrypt_Content_With_Human_Readable_Key_Secret_SymmetricKey(); -``` - ---- +~~~ ### 1.5. Encrypt/Decrypt File -```csharp +~~~csharp byte[] fileBytes = File.ReadAllBytes(filename); var encrypt = encryptClient.EncryptFromBytes(fileBytes); var decrypt = decryptClient.DecryptToBytes(encrypt); -``` - -Run: - -```csharp -ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("file.pdf"); -``` +~~~ --- @@ -125,7 +81,7 @@ ExampleAes.Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test("file.pdf") ### 2.1. Sign & Verify with Self-Generated Key -```csharp +~~~csharp ICryptoNetDsa client = new CryptoNetDsa(); var privateKey = client.GetKey(true); @@ -133,19 +89,11 @@ var signature = new CryptoNetDsa(privateKey).CreateSignature(ConfidentialDummyDa var verified = new CryptoNetDsa(privateKey) .IsContentVerified(ExtShared.ExtShared.StringToBytes(ConfidentialDummyData), signature); -``` - -Run: - -```csharp -ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); -``` - ---- +~~~ ### 2.2. Generate, Save, and Reuse Keys -```csharp +~~~csharp cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); @@ -154,13 +102,7 @@ var signature = new CryptoNetDsa(new FileInfo(PrivateKeyFile)) var verified = new CryptoNetDsa(new FileInfo(PublicKeyFile)) .IsContentVerified(ExtShared.ExtShared.StringToBytes(ConfidentialDummyData), signature); -``` - -Run: - -```csharp -ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); -``` +~~~ --- @@ -168,54 +110,38 @@ ExampleDsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); ### 3.1. Self-Generated RSA Keys -```csharp +~~~csharp ICryptoNetRsa cryptoNet = new CryptoNetRsa(); var privateKey = cryptoNet.GetKey(true); var publicKey = cryptoNet.GetKey(false); var encrypt = new CryptoNetRsa(publicKey).EncryptFromString(ConfidentialDummyData); var decrypt = new CryptoNetRsa(privateKey).DecryptToString(encrypt); -``` - -Run: - -```csharp -ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); -``` - ---- +~~~ ### 3.2. Save RSA Keys & Reuse -```csharp +~~~csharp cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); var encrypt = new CryptoNetRsa(new FileInfo(PublicKeyFile)).EncryptFromString(ConfidentialDummyData); var decrypt = new CryptoNetRsa(new FileInfo(PrivateKeyFile)).DecryptToString(encrypt); -``` - -Run: - -```csharp -ExampleRsa.Example_2_SelfGenerated_And_Save_AsymmetricKey(); -``` - ---- +~~~ ### 3.3. Encrypt with Public Key, Decrypt with Private Key -Run: +~~~csharp +ICryptoNet pubKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFile)); +var encrypted = pubKeyClient.EncryptFromString(ConfidentialDummyData); -```csharp -ExampleRsa.Example_3_Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content(); -``` - ---- +ICryptoNet priKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); +var decrypted = priKeyClient.DecryptToString(encrypted); +~~~ ### 3.4. Use X509 Certificate for RSA Encryption -```csharp +~~~csharp var cert = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); var encrypt = new CryptoNetRsa(cert, KeyType.PublicKey) @@ -223,46 +149,95 @@ var encrypt = new CryptoNetRsa(cert, KeyType.PublicKey) var decrypt = new CryptoNetRsa(cert, KeyType.PrivateKey) .DecryptToString(encrypt); -``` +~~~ + +### 3.5. Export RSA Public Key from Certificate -Run: +~~~csharp +X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); -```csharp -ExampleRsa.Example_4_Using_X509_Certificate(); -``` +ICryptoNetRsa certClient = new CryptoNetRsa(certificate, KeyType.PublicKey); +var publicKey = certClient.GetKey(isPrivate: false); +~~~ ---- +### 3.6. Working with Special Characters -### 3.5. Export RSA Public Key from Certificate +~~~csharp +string confidentialWithSpecialChars = "Top secret 😃😃"; -Run: +ICryptoNetRsa cryptoNet = new CryptoNetRsa(); +var privateKeyXml = cryptoNet.GetKey(isPrivate: true); +var publicKeyXml = cryptoNet.GetKey(isPrivate: false); -```csharp -ExampleRsa.Example_5_Export_Public_Key_For_X509_Certificate(); -``` +ICryptoNet encryptClient = new CryptoNetRsa(publicKeyXml); +var encrypted = encryptClient.EncryptFromBytes(Encoding.UTF8.GetBytes(confidentialWithSpecialChars)); ---- +ICryptoNet decryptClient = new CryptoNetRsa(privateKeyXml); +var decryptedBytes = decryptClient.DecryptToBytes(encrypted); +var decryptedString = Encoding.UTF8.GetString(decryptedBytes); +~~~ -### 3.6. PEM Import/Export (Advanced) +### 3.7. PEM Import/Export (Advanced) +> Note: This example requires the CryptoNet.Extensions package. and still in development. -Run: +~~~csharp +// Export PEM, encrypted PEM, import PEM (with/without password) +char[] pubPem = ExampleRsa.ExportPemKey(certificate, privateKey: false); +char[] priPem = ExampleRsa.ExportPemKey(certificate); +byte[] encryptedPriPem = ExampleRsa.ExportPemKeyWithPassword(certificate, "password"); -```csharp -ExampleRsa.Example_7_Customize(); -``` +ICryptoNet importedFromPub = ExampleRsa.ImportPemKey(pubPem); +ICryptoNet importedFromPri = ExampleRsa.ImportPemKey(priPem); +ICryptoNet importedFromEncryptedPri = ExampleRsa.ImportPemKeyWithPassword(encryptedPriPem, "password"); +~~~ --- -## 4. Running All Examples +## Running All Examples (quick reference) -```csharp +~~~csharp using CryptoNet.Examples; -ExampleAes.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_SymmetricKey(); -ExampleDsa.Example_1_Sign_Validate_Content_With_SelfGenerated_AsymmetricKey(); -ExampleRsa.Example_1_Encrypt_Decrypt_Content_With_SelfGenerated_AsymmetricKey(); -``` +// AES +ExampleAes.EncryptDecryptWithSelfGeneratedKey(); +ExampleAes.GenerateAndSaveSymmetricKey(); +ExampleAes.EncryptDecryptWithProvidedSymmetricKey(); +ExampleAes.EncryptDecryptWithHumanReadableKeySecret(); +ExampleAes.EncryptAndDecryptFileWithSymmetricKeyTest("TestFiles\\test.docx"); + +// DSA +ExampleDsa.SignAndValidateWithSelfGeneratedKey(); +ExampleDsa.GenerateAndSaveDsaKeyPair(); + +// RSA +ExampleRsa.EncryptDecryptWithSelfGeneratedKey(); +ExampleRsa.GenerateAndSaveAsymmetricKey(); +ExampleRsa.EncryptWithPublicKeyAndDecryptWithPrivateKey(); +ExampleRsa.UseX509Certificate(); +ExampleRsa.ExportPublicKeyFromX509Certificate(); +ExampleRsa.WorkWithSpecialCharacterText(); +ExampleRsa.CustomizePemExamples(); +~~~ --- -## End +## Examples coverage checklist + +~~~text +[ ] AES - EncryptDecryptWithSelfGeneratedKey +[ ] AES - GenerateAndSaveSymmetricKey +[ ] AES - EncryptDecryptWithProvidedSymmetricKey +[ ] AES - EncryptDecryptWithHumanReadableKeySecret +[ ] AES - EncryptAndDecryptFileWithSymmetricKeyTest +[ ] DSA - SignAndValidateWithSelfGeneratedKey +[ ] DSA - GenerateAndSaveDsaKeyPair +[ ] RSA - EncryptDecryptWithSelfGeneratedKey +[ ] RSA - GenerateAndSaveAsymmetricKey +[ ] RSA - EncryptWithPublicKeyAndDecryptWithPrivateKey +[ ] RSA - UseX509Certificate +[ ] RSA - ExportPublicKeyFromX509Certificate +[ ] RSA - WorkWithSpecialCharacterText +[ ] RSA - CustomizePemExamples +~~~ + +## End \ No newline at end of file diff --git a/docs/getting-started.md b/docs/getting-started.md index 26f533c..20d18ec 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -6,7 +6,7 @@ Here are some examples: ### Example: Encrypt and Decrypt Content With Symmetric Key In this example CryptoNetAes generates a random key and IV, hence we use the same instance we can both encrypt and decrypt. -```csharp +~~~csharp ICryptoNet cryptoNet = new CryptoNetAes(); var key = cryptoNet.ExportKey(); @@ -17,10 +17,10 @@ ICryptoNet decryptClient = new CryptoNetAes(key); var decrypt = decryptClient.DecryptToString(encrypt); Debug.Assert(ConfidentialDummyData == decrypt); -``` +~~~ ### Example: Encrypt and Decrypt Content With Export and Import Self-Generated Symmetric Key -```csharp +~~~csharp ICryptoNet cryptoNet = new CryptoNetAes(); var file = new FileInfo(SymmetricKeyFile); cryptoNet.ExportKeyAndSave(file); @@ -33,10 +33,10 @@ ICryptoNet cryptoNetKeyImport = new CryptoNetAes(file); var decrypt = cryptoNetKeyImport.DecryptToString(encrypt); Debug.Assert(ConfidentialDummyData == decrypt); -``` +~~~ ### Example: Generate Asymmetric RSA key pair, Export Private and Public, use Public key to encrypt with and Use Private key to decrypt with -```csharp +~~~csharp ICryptoNet cryptoNet = new CryptoNetRsa(); cryptoNet.ExportKeyAndSave(new FileInfo(PrivateKeyFile), true); @@ -52,10 +52,10 @@ ICryptoNet cryptoNetPriKey = new CryptoNetRsa(new FileInfo(PrivateKeyFile)); var decrypt = cryptoNetPriKey.DecryptToString(encrypt); Debug.Assert(ConfidentialDummyData == decrypt); -``` +~~~ ### Example: Use X509 certificate to Encrypt with Public Key and later Decrypt with Private Key -```csharp +~~~csharp // Find and replace CN=Maytham with your own certificate X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); @@ -66,4 +66,4 @@ ICryptoNet cryptoNetWithPrivateKey = new CryptoNetRsa(certificate, KeyType.Priva var decryptWithPrivateKey = cryptoNetWithPrivateKey.DecryptToString(encryptWithPublicKey); Debug.Assert(ConfidentialDummyData == decryptWithPrivateKey); -``` +~~~ diff --git a/docs/introduction.md b/docs/introduction.md index 904a79e..0e3a58b 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,34 +1,42 @@ # Introduction -### Short Intro +This document gives a short introduction to CryptoNet and how the library is typically used. -The library can be used in two ways: +Supported targets +- Core libraries target: .NET Standard 2.0 (for maximum compatibility). +- Examples and tests target: .NET 8 and .NET 10. -- **Symmetric encryption** -- **Asymmetric encryption** (public key encryption) +Overview -#### Symmetric Encryption +CryptoNet supports two primary cryptographic scenarios: -You use the same key (any secret key) for both encryption and decryption. +- Symmetric encryption — a single shared secret (key) is used for both encryption and decryption. +- Asymmetric encryption — a public/private key pair is used; the public key encrypts and the private key decrypts. -#### Asymmetric Encryption +Symmetric encryption +- Use a secret key (same key for encrypt/decrypt). +- Protect the secret key: anyone with the key can decrypt the data. -With asymmetric encryption, the library can use its own self-generated RSA key pairs (Private/Public keys) to encrypt and decrypt content. +Asymmetric encryption +- The library can generate RSA key pairs (private/public) for you, or it can use keys from an X.509 certificate. +- Typical pattern: + - Use the **Public key** to encrypt data. + - Use the **Private key** to decrypt data. -You can store the private key on one or more machines, while the public key can be easily distributed to all clients. +Key lifecycle and security notes +- Private keys must be kept confidential. Do not distribute private keys. +- If a private key is leaked, an attacker can decrypt any content encrypted with the corresponding public key. Rotate (revoke and reissue) the key pair if compromise is suspected. +- Conversely, if you lose a private key and you do not have a backup, you will not be able to decrypt content that was encrypted for that key—make secure backups as appropriate. -> **Important:** Do not distribute private keys publicly; keep them in a safe place. If a private key is mistakenly exposed, you need to reissue new keys. Content already encrypted with the compromised private key cannot be decrypted with a newly generated private key. Before updating or deleting the old private key, ensure all encrypted content is decrypted, or you risk losing access to that content. +Using X.509 certificates +- CryptoNet can use the public/private keys stored in X.509 certificates as an alternative to self-generated keys. This can simplify key distribution and lifecycle when using enterprise PKI. -Additionally, it is possible to use asymmetric keys from an X.509 certificate instead of generating your own keys. +Further reading +- Asymmetric encryption overview: https://www.cloudflare.com/learning/ssl/what-is-asymmetric-encryption/ +- Examples and usage (see the examples documentation): `docs/examples.md` -The main concept of asymmetric encryption is the use of a Private key and a Public key: -- Use the **Public key** to encrypt content. -- Use the **Private key** to decrypt the content. +Example code lives under the `CryptoNet.Examples` namespace and demonstrates AES (symmetric), DSA (signatures) and RSA (asymmetric) scenarios. -Read more about asymmetric (public key) encryption [here](https://www.cloudflare.com/learning/ssl/what-is-asymmetric-encryption/). - -You can find complete examples for: - -- [RSA asymmetric cryptographic algorithm](https://github.com/maythamfahmi/CryptoNet/blob/main/Examples/RSAExample/RSAExample.cs) -- [AES symmetric cryptographic algorithm](https://github.com/maythamfahmi/CryptoNet/blob/main/Examples/AESExample/AESExample.cs) -- [DSA asymmetric cryptographic algorithm](https://github.com/maythamfahmi/CryptoNet/blob/main/Examples/DSAExample/DSAExample.cs) +See also +- Documentation and generation: `docs/tooling.md` +- Examples reference: `docs/examples.md` \ No newline at end of file diff --git a/docs/migration.md b/docs/migration.md index 5b2f412..1580434 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -3,8 +3,6 @@ This document describes the main breaking and behavioral changes for the **AES** and **RSA** examples when migrating from **v2.4.0** to **v3.4.3** of CryptoNet. --- -TODO: Issue encrypting 🤣🤣 in case of string, use bytes. an example to clarify this. - ## 1. Common Changes ### 1.1 Utility changes @@ -26,16 +24,16 @@ You will typically: **v2.4.0** -```csharp +~~~csharp using CryptoNet.Models; using CryptoNet.Utils; -``` +~~~ **v3.4.3** -```csharp +~~~csharp using CryptoNet.Models; -``` +~~~ The AES encryption/decryption API (`ICryptoNet` + `CryptoNetAes`) remains mostly the same. The main breaking changes are related to **key management** and **utility usage**. @@ -43,7 +41,7 @@ The AES encryption/decryption API (`ICryptoNet` + `CryptoNetAes`) remains mostly #### 2.2.1 Generating a key in memory -```csharp +~~~csharp // v2.4.0 ICryptoNet cryptoNet = new CryptoNetAes(); var key = cryptoNet.ExportKey(); @@ -51,7 +49,7 @@ var key = cryptoNet.ExportKey(); // v3.4.3 ICryptoNetAes cryptoNet = new CryptoNetAes(); var key = cryptoNet.GetKey(); -``` +~~~ **Changes:** - Interface: `ICryptoNet` → `ICryptoNetAes` for key retrieval. @@ -62,17 +60,17 @@ var key = cryptoNet.GetKey(); You still use `ICryptoNet` for encryption/decryption with the retrieved key: -```csharp +~~~csharp ICryptoNet encryptClient = new CryptoNetAes(key); var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetAes(key); var decrypt = decryptClient.DecryptToString(encrypt); -``` +~~~ #### 2.2.2 Saving a symmetric key to file -```csharp +~~~csharp // v2.4.0 ICryptoNet cryptoNet = new CryptoNetAes(); var file = new FileInfo(SymmetricKeyFile); @@ -82,7 +80,7 @@ cryptoNet.ExportKeyAndSave(file); ICryptoNetAes cryptoNet = new CryptoNetAes(); var file = new FileInfo(SymmetricKeyFile); cryptoNet.SaveKey(file); -``` +~~~ **Changes:** - Interface: `ICryptoNet` → `ICryptoNetAes`. @@ -104,13 +102,13 @@ No changes are required for the encryption/decryption code in these examples. #### 2.4.1 Method name -```csharp +~~~csharp // v2.4.0 public static void Example_5_Encrypt_And_Decrypt_PdfFile_With_SymmetricKey_Test(string filename) // v3.4.3 public static void Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test(string filename) -``` +~~~ **Change:** Method name updated to reflect support for arbitrary files, not just PDFs. @@ -118,7 +116,7 @@ public static void Example_5_Encrypt_And_Decrypt_File_With_SymmetricKey_Test(str #### 2.4.2 Key retrieval -```csharp +~~~csharp // v2.4.0 ICryptoNet cryptoNet = new CryptoNetAes(); var key = cryptoNet.ExportKey(); @@ -126,19 +124,19 @@ var key = cryptoNet.ExportKey(); // v3.4.3 ICryptoNetAes cryptoNet = new CryptoNetAes(); var key = cryptoNet.GetKey(); -``` +~~~ Same pattern as in 2.2.1. #### 2.4.3 Byte array comparison -```csharp +~~~csharp // v2.4.0 var isIdenticalFile = CryptoNetUtils.ByteArrayCompare(pdfFileBytes, decrypt); // v3.4.3 var isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(pdfFileBytes, decrypt); -``` +~~~ **Change:** The helper moved from `CryptoNetUtils` to `ExtShared.ExtShared`. @@ -167,20 +165,20 @@ The `UniqueKeyGenerator` function is unchanged between versions; no migration is **v2.4.0** -```csharp +~~~csharp using CryptoNet.Models; using CryptoNet.Utils; namespace CryptoNet.Cli; -``` +~~~ **v3.4.3** -```csharp +~~~csharp using CryptoNet.Models; namespace CryptoNet.Examples; -``` +~~~ The RSA encryption/decryption calls via `ICryptoNet` remain conceptually the same. The major changes are centralized in **key management APIs** and **certificate utilities**. @@ -188,7 +186,7 @@ The RSA encryption/decryption calls via `ICryptoNet` remain conceptually the sam #### 3.2.1 Generating keys in memory -```csharp +~~~csharp // v2.4.0 ICryptoNet cryptoNet = new CryptoNetRsa(); @@ -200,7 +198,7 @@ ICryptoNetRsa cryptoNet = new CryptoNetRsa(); var privateKey = cryptoNet.GetKey(true); var publicKey = cryptoNet.GetKey(false); -``` +~~~ **Changes:** - Interface: `ICryptoNet` → `ICryptoNetRsa` for RSA key retrieval. @@ -211,17 +209,17 @@ var publicKey = cryptoNet.GetKey(false); Encryption/decryption continues to use `ICryptoNet` with `CryptoNetRsa(key)`: -```csharp +~~~csharp ICryptoNet encryptClient = new CryptoNetRsa(publicKey); var encrypt = encryptClient.EncryptFromString(ConfidentialDummyData); ICryptoNet decryptClient = new CryptoNetRsa(privateKey); var decrypt = decryptClient.DecryptToString(encrypt); -``` +~~~ #### 3.2.2 Saving keys to disk -```csharp +~~~csharp // v2.4.0 ICryptoNet cryptoNet = new CryptoNetRsa(); @@ -233,7 +231,7 @@ ICryptoNetRsa cryptoNet = new CryptoNetRsa(); cryptoNet.SaveKey(new FileInfo(PrivateKeyFile), true); cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); -``` +~~~ **Changes:** - Interface: `ICryptoNet` → `ICryptoNetRsa`. @@ -246,7 +244,7 @@ cryptoNet.SaveKey(new FileInfo(PublicKeyFile), false); #### 3.3.1 Certificate lookup (Examples 4, 5, 7) -```csharp +~~~csharp // v2.4.0 // Find and replace CN=Maytham with your own certificate X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); @@ -254,7 +252,7 @@ X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Mayth // v3.4.3 // Find and replace CN=localhost with your own certificate X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost"); -``` +~~~ **Changes:** - Utility: `CryptoNetUtils.GetCertificateFromStore` → `ExtShared.ExtShared.GetCertificateFromStore`. @@ -272,7 +270,7 @@ This affects: #### 3.3.2 Exporting public key from certificate (Example 5) -```csharp +~~~csharp // v2.4.0 X509Certificate2? certificate = CryptoNetUtils.GetCertificateFromStore("CN=Maytham"); @@ -284,7 +282,7 @@ X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN= ICryptoNetRsa cryptoNetWithPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); var publicKey = cryptoNetWithPublicKey.GetKey(false); -``` +~~~ **Changes:** - Certificate retrieval via `ExtShared.ExtShared` instead of `CryptoNetUtils`. diff --git a/docs/tooling.md b/docs/tooling.md new file mode 100644 index 0000000..d6cb886 --- /dev/null +++ b/docs/tooling.md @@ -0,0 +1,150 @@ +# Developer Tooling and Release Guide + +This document describes the recommended tooling, local workflows and scripts for building, testing, documenting and releasing the CryptoNet repository. + +Note: script and IDE actions are shown as-is; when running scripts from the repository root use the Scripts/ or repository root path as appropriate (for example: __run_docs.ps1__, __run_release.ps1__). + +--- + +## 1. Build & Test (local) + +You can build and run tests using any of the following: + +- Visual Studio: use __Build > Build Solution__ and the Test Explorer. +- Visual Studio Code: use the integrated terminal or Test Explorer extensions. +- dotnet CLI: + +~~~powershell +dotnet build +dotnet test --configuration Release --no-build +~~~ + +There are convenience PowerShell scripts in the repository root (or the Scripts folder): + +~~~powershell +# Build & test wrapper +.\run_build_and_test.ps1 # if present +# Docker-based build +.\run_docker_build.ps1 +~~~ + +Docker (from solution folder): + +~~~ +docker build . --file .\Dockerfile --tag cryptonet-service:latest +~~~ + +Or use the preserved PowerShell wrapper: + +~~~powershell +./run_docker_build.ps1 +~~~ + +--- + +## 2. Release + +Use the release helper script to create preview or production releases: + +Preview: +~~~powershell +.\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $true +~~~ + +Production: +~~~powershell +.\run_release.ps1 -VersionNumber 3.0.0 -IsPreview $false +~~~ + +Tags must conform to the version format expected by CI (see CONTRIBUTING.md and release scripts). + +--- + +## 3. Documentation (DocFX) + +Documentation is generated with DocFX and published by CI (pipeline `5-static.yml`). To work with docs locally: + +- Install DocFX (one-time): +~~~ +dotnet tool install -g docfx +~~~ + +- Generate docs locally: +~~~powershell +.\run_docs.ps1 +~~~ + +This script typically cleans, builds the code with XML docs, runs DocFX and serves the site locally. + +### Update `index.md` from `README.md` + +To sync the landing page: +~~~powershell +.\run_update_index.ps1 +~~~ + +The update script will add the required YAML front matter and append README content to `index.md`. + +--- + +## 4. Code Coverage + +Quick steps to run coverage locally (Windows PowerShell): + +~~~powershell +.\run_codecoverage.ps1 +# or +cd .\CryptoNet.UnitTests\ +dotnet test --collect:"XPlat Code Coverage" +reportgenerator -reports:"**/coverage.cobertura.xml" -targetdir:coverage-report +~~~ + +If the test project is missing coverage collectors, add them once: + +~~~bash +cd .\CryptoNet.UnitTests\ +dotnet add package coverlet.collector +dotnet add package coverlet.msbuild +dotnet tool install --global dotnet-reportgenerator-globaltool +~~~ + +--- + +## 5. Scripts & Paths + +Common scripts referenced in this repo: + +- __run_docs.ps1__ — generate documentation with DocFX. +- __run_release.ps1__ — create a release tag and invoke CI release workflows. +- __run_codecoverage.ps1__ — run tests and generate coverage reports. +- __run_update_index.ps1__ — synchronize README -> index.md for documentation landing. + +Scripts are typically in the Scripts/ directory or repository root. Check the script header for exact behavior and required parameters. + +--- + +## 6. Notes & Best Practices + +- Doc generation depends on XML comments. Ensure public APIs include XML `` tags before generating docs. +- The project uses Central Package Management (Directory.Packages.props). Do not hardcode package versions in individual .csproj files. +- Core libraries target .NET Standard 2.0 — avoid .NET 8+ APIs in those projects. +- Build and test locally before opening a PR. CI is configured to treat warnings as errors. + +--- + +## 7. Quick Local Setup Checklist + +~~~text +[ ] Install .NET SDK(s) required by the repository +[ ] Install DocFX tool: dotnet tool install -g docfx +[ ] Install report generator tool: dotnet tool install --global dotnet-reportgenerator-globaltool +[ ] Run dotnet build and dotnet test locally +[ ] Run .\run_docs.ps1 to validate documentation generation +[ ] Run .\run_codecoverage.ps1 to validate coverage generation (optional) +~~~ + +--- + +If you want, I can: +- apply the same formatting and script-name convention to other docs (for example `docs/examples.md`), or +- create a small PR with these changes. \ No newline at end of file