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..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:
@@ -47,8 +47,8 @@ jobs:
steps:
- 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 }}`
@@ -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..64a0dc9
--- /dev/null
+++ b/CryptoNet.Examples/ExampleAes.cs
@@ -0,0 +1,157 @@
+//
+// 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;
+
+///
+/// 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 BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
+ private static readonly string SymmetricKeyFilePath = Path.Combine(BaseDirectory, $"{KeyType.SymmetricKey}.xml");
+
+ ///
+ /// Generate a symmetric key, encrypt and decrypt a string using self-generated key.
+ ///
+ public static void EncryptDecryptWithSelfGeneratedSymmetricKey()
+ {
+ ICryptoNetAes cryptoNet = new CryptoNetAes();
+ string key = cryptoNet.GetKey();
+
+ ICryptoNet encryptClient = new CryptoNetAes(key);
+ byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet decryptClient = new CryptoNetAes(key);
+ string decrypted = decryptClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// 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();
+ FileInfo keyFile = new FileInfo(SymmetricKeyFilePath);
+ cryptoNet.SaveKey(keyFile);
+
+ Debug.Assert(File.Exists(keyFile.FullName));
+
+ byte[] encrypted = cryptoNet.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet cryptoNetImported = new CryptoNetAes(keyFile);
+ string decrypted = cryptoNetImported.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// Encrypt and decrypt using a provided raw symmetric key and IV.
+ ///
+ public static void EncryptDecryptWithProvidedSymmetricKey()
+ {
+ string symmetricKey = "12345678901234567890123456789012";
+ if (symmetricKey.Length != 32)
+ {
+ Console.WriteLine("Key should be 32 characters long");
+ Environment.Exit(0);
+ }
+
+ string secret = "1234567890123456";
+ if (secret.Length != 16)
+ {
+ Console.WriteLine("IV should be 16 characters long");
+ Environment.Exit(1);
+ }
+
+ byte[] key = Encoding.UTF8.GetBytes(symmetricKey);
+ byte[] iv = Encoding.UTF8.GetBytes(secret);
+
+ ICryptoNet encryptClient = new CryptoNetAes(key, iv);
+ byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet decryptClient = new CryptoNetAes(key, iv);
+ string decrypted = decryptClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// Generate a human-readable key/secret pair and use them for encryption/decryption.
+ ///
+ public static void EncryptDecryptWithHumanReadableKeySecret()
+ {
+ string symmetricKey = GenerateUniqueKey("symmetricKey");
+ string secret = new string(GenerateUniqueKey("password").Take(16).ToArray());
+
+ byte[] key = Encoding.UTF8.GetBytes(symmetricKey);
+ byte[] iv = Encoding.UTF8.GetBytes(secret);
+
+ ICryptoNet encryptClient = new CryptoNetAes(key, iv);
+ byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet decryptClient = new CryptoNetAes(key, iv);
+ string decrypted = decryptClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// 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();
+ string key = cryptoNet.GetKey();
+
+ FileInfo fileInfo = new FileInfo(filename);
+
+ ICryptoNet encryptClient = new CryptoNetAes(key);
+ string sourceFilePath = Path.Combine(BaseDirectory, filename);
+ byte[] fileBytes = File.ReadAllBytes(sourceFilePath);
+ byte[] encrypted = encryptClient.EncryptFromBytes(fileBytes);
+
+ ICryptoNet decryptClient = new CryptoNetAes(key);
+ byte[] decrypted = decryptClient.DecryptToBytes(encrypted);
+ string decryptedFilePath = $"TestFiles\\{Path.GetFileNameWithoutExtension(fileInfo.Name)}-decrypted{fileInfo.Extension}";
+ File.WriteAllBytes(decryptedFilePath, decrypted);
+
+ bool isIdenticalFile = ExtShared.ExtShared.ByteArrayCompare(fileBytes, decrypted);
+ Debug.Assert(isIdenticalFile);
+ }
+
+ ///
+ /// 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 (byte b in hash)
+ {
+ 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
new file mode 100644
index 0000000..88f6b61
--- /dev/null
+++ b/CryptoNet.Examples/ExampleDsa.cs
@@ -0,0 +1,69 @@
+//
+// 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;
+
+///
+/// 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 BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
+
+ internal static readonly string PrivateKeyFilePath = Path.Combine(BaseDirectory, "privateKey");
+ internal static readonly string PublicKeyFilePath = Path.Combine(BaseDirectory, "publicKey.pub");
+
+ ///
+ /// Demonstrates signing content with a self-generated DSA key and validating the signature.
+ ///
+ public static void SignAndValidateWithSelfGeneratedKey()
+ {
+ ICryptoNetDsa client = new CryptoNetDsa();
+ string privateKeyXml = client.GetKey(true);
+
+ ICryptoNetDsa signatureClient = new CryptoNetDsa(privateKeyXml);
+ byte[] signature = signatureClient.CreateSignature(ConfidentialDummyData);
+
+ ICryptoNetDsa verifyClient = new CryptoNetDsa(privateKeyXml);
+ byte[] contentBytes = ExtShared.ExtShared.StringToBytes(ConfidentialDummyData);
+
+ bool isVerified = verifyClient.IsContentVerified(contentBytes, signature);
+
+ Debug.Assert(isVerified == true);
+ }
+
+ ///
+ /// 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(PrivateKeyFilePath), true);
+ cryptoNet.SaveKey(new FileInfo(PublicKeyFilePath), false);
+
+ Debug.Assert(File.Exists(new FileInfo(PrivateKeyFilePath).FullName));
+ Debug.Assert(File.Exists(new FileInfo(PublicKeyFilePath).FullName));
+
+ ICryptoNetDsa dsaWithPrivateKey = new CryptoNetDsa(new FileInfo(PrivateKeyFilePath));
+ byte[] signature = dsaWithPrivateKey.CreateSignature(ConfidentialDummyData);
+
+ 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
new file mode 100644
index 0000000..6baa1c0
--- /dev/null
+++ b/CryptoNet.Examples/ExampleRsa.cs
@@ -0,0 +1,240 @@
+//
+// 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;
+
+///
+/// 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 BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
+
+ internal static readonly string PrivateKeyFilePath = Path.Combine(BaseDirectory, "privateKey");
+ internal static readonly string PublicKeyFilePath = Path.Combine(BaseDirectory, "publicKey.pub");
+
+ ///
+ /// 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();
+
+ string privateKeyXml = cryptoNet.GetKey(true);
+ string publicKeyXml = cryptoNet.GetKey(false);
+
+ ICryptoNet encryptClient = new CryptoNetRsa(publicKeyXml);
+ byte[] encrypted = encryptClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet decryptClient = new CryptoNetRsa(privateKeyXml);
+ string decrypted = decryptClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// 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(PrivateKeyFilePath), true);
+ cryptoNet.SaveKey(new FileInfo(PublicKeyFilePath), false);
+
+ Debug.Assert(File.Exists(new FileInfo(PrivateKeyFilePath).FullName));
+ Debug.Assert(File.Exists(new FileInfo(PublicKeyFilePath).FullName));
+
+ ICryptoNet publicKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFilePath));
+ byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet privateKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFilePath));
+ string decrypted = privateKeyClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// Encrypts with a public key file and decrypts with a private key file.
+ ///
+ public static void EncryptWithPublicKeyAndDecryptWithPrivateKey()
+ {
+ ICryptoNet publicKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFilePath));
+ byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet privateKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFilePath));
+ string decrypted = privateKeyClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// 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()
+ {
+ // Replace subject with your certificate subject (for example "CN=localhost")
+ X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost");
+
+ ICryptoNet publicKeyClient = new CryptoNetRsa(certificate, KeyType.PublicKey);
+ byte[] encrypted = publicKeyClient.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet privateKeyClient = new CryptoNetRsa(certificate, KeyType.PrivateKey);
+ string decrypted = privateKeyClient.DecryptToString(encrypted);
+
+ Debug.Assert(ConfidentialDummyData == decrypted);
+ }
+
+ ///
+ /// Exports the public key portion from an X509 certificate using the CryptoNetRsa GetKey API.
+ ///
+ public static void ExportPublicKeyFromX509Certificate()
+ {
+ // Replace subject with your certificate subject (for example "CN=localhost")
+ X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost");
+
+ ICryptoNetRsa certClient = new CryptoNetRsa(certificate, KeyType.PublicKey);
+ string publicKeyXml = certClient.GetKey(false);
+
+ Debug.Assert(!string.IsNullOrEmpty(publicKeyXml));
+ }
+
+ ///
+ /// Demonstrates encrypting/decrypting content that includes special (emoji) characters.
+ /// Ensures UTF-8 byte-level operations are used.
+ ///
+ public static void WorkWithSpecialCharacterText()
+ {
+ string confidentialWithSpecialChars = "Top secret 😃😃";
+
+ ICryptoNetRsa cryptoNet = new CryptoNetRsa();
+ string privateKeyXml = cryptoNet.GetKey(true);
+ string publicKeyXml = cryptoNet.GetKey(false);
+
+ ICryptoNet encryptClient = new CryptoNetRsa(publicKeyXml);
+ byte[] encrypted = encryptClient.EncryptFromBytes(Encoding.UTF8.GetBytes(confidentialWithSpecialChars));
+
+ ICryptoNet decryptClient = new CryptoNetRsa(privateKeyXml);
+ byte[] decryptedBytes = decryptClient.DecryptToBytes(encrypted);
+ string decryptedString = Encoding.UTF8.GetString(decryptedBytes);
+
+ Debug.Assert(confidentialWithSpecialChars == decryptedString);
+ }
+
+ ///
+ /// Work-in-progress: demonstrates PEM export/import and encrypted PEM import with password.
+ ///
+ public static void CustomizePemExamples()
+ {
+ X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost");
+
+ char[] pubKeyPem = ExportPemKey(certificate!, privateKey: false);
+ char[] priKeyPem = ExportPemKey(certificate!);
+
+ string password = "password";
+ byte[] encryptedPriKeyBytes = ExportPemKeyWithPassword(certificate!, password);
+
+ ICryptoNet decryptClientWithPassword = ImportPemKeyWithPassword(encryptedPriKeyBytes, password);
+ byte[] encrypted1 = decryptClientWithPassword.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet encryptClientFromPubPem = ImportPemKey(pubKeyPem);
+ byte[] encrypted2 = encryptClientFromPubPem.EncryptFromString(ConfidentialDummyData);
+
+ ICryptoNet decryptClientFromPriPem = ImportPemKey(priKeyPem);
+ string decrypted2 = decryptClientFromPriPem.DecryptToString(encrypted2);
+
+ Debug.Assert(ConfidentialDummyData == decrypted2);
+
+ string decrypted1 = decryptClientFromPriPem.DecryptToString(encrypted1);
+
+ 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;
+ char[] certPem = PemEncoding.Write("CERTIFICATE", certBytes);
+ 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[] privateKeyBytes = rsa.ExportPkcs8PrivateKey();
+ return PemEncoding.Write("PRIVATE KEY", privateKeyBytes);
+ }
+
+ 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();
+ cryptoNet.Info.RsaDetail!.Rsa?.ImportFromPem(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[] 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();
+ 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..61117ff
--- /dev/null
+++ b/CryptoNet.Examples/Program.cs
@@ -0,0 +1,24 @@
+using CryptoNet.Examples;
+
+ExampleDsa.SignAndValidateWithSelfGeneratedKey();
+ExampleDsa.GenerateAndSaveDsaKeyPair();
+
+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");
+
+// 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.Examples/TestFiles/test.docx b/CryptoNet.Examples/TestFiles/test.docx
new file mode 100644
index 0000000..4852773
Binary files /dev/null and b/CryptoNet.Examples/TestFiles/test.docx differ
diff --git a/CryptoNet.Examples/TestFiles/test.pdf b/CryptoNet.Examples/TestFiles/test.pdf
new file mode 100644
index 0000000..9b88b35
Binary files /dev/null and b/CryptoNet.Examples/TestFiles/test.pdf differ
diff --git a/CryptoNet.Examples/TestFiles/test.png b/CryptoNet.Examples/TestFiles/test.png
new file mode 100644
index 0000000..29c6eb4
Binary files /dev/null and b/CryptoNet.Examples/TestFiles/test.png differ
diff --git a/CryptoNet.Examples/TestFiles/test.xlsx b/CryptoNet.Examples/TestFiles/test.xlsx
new file mode 100644
index 0000000..9575466
Binary files /dev/null and b/CryptoNet.Examples/TestFiles/test.xlsx differ
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.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/CryptoNet.sln b/CryptoNet.sln
index ca2ae6a..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
@@ -53,6 +53,18 @@ 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
+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
@@ -99,6 +111,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..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,136 +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.
-
-## 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..31fd84c
--- /dev/null
+++ b/docs/examples.md
@@ -0,0 +1,243 @@
+# 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;
+~~~
+
+---
+
+## 1. AES – Symmetric Encryption (`ExampleAes`)
+
+### 1.1. Encrypt/Decrypt a String with a Self-Generated Key
+
+~~~csharp
+// EncryptDecryptWithSelfGeneratedKey
+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);
+~~~
+
+### 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);
+~~~
+
+### 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);
+~~~
+
+### 1.4. Human-Readable Key + IV
+
+~~~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));
+~~~
+
+### 1.5. Encrypt/Decrypt File
+
+~~~csharp
+byte[] fileBytes = File.ReadAllBytes(filename);
+var encrypt = encryptClient.EncryptFromBytes(fileBytes);
+var decrypt = decryptClient.DecryptToBytes(encrypt);
+~~~
+
+---
+
+## 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);
+~~~
+
+### 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);
+~~~
+
+---
+
+## 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);
+~~~
+
+### 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);
+~~~
+
+### 3.3. Encrypt with Public Key, Decrypt with Private Key
+
+~~~csharp
+ICryptoNet pubKeyClient = new CryptoNetRsa(new FileInfo(PublicKeyFile));
+var encrypted = pubKeyClient.EncryptFromString(ConfidentialDummyData);
+
+ICryptoNet priKeyClient = new CryptoNetRsa(new FileInfo(PrivateKeyFile));
+var decrypted = priKeyClient.DecryptToString(encrypted);
+~~~
+
+### 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);
+~~~
+
+### 3.5. Export RSA Public Key from Certificate
+
+~~~csharp
+X509Certificate2? certificate = ExtShared.ExtShared.GetCertificateFromStore("CN=localhost");
+
+ICryptoNetRsa certClient = new CryptoNetRsa(certificate, KeyType.PublicKey);
+var publicKey = certClient.GetKey(isPrivate: false);
+~~~
+
+### 3.6. Working with Special Characters
+
+~~~csharp
+string confidentialWithSpecialChars = "Top secret 😃😃";
+
+ICryptoNetRsa cryptoNet = new CryptoNetRsa();
+var privateKeyXml = cryptoNet.GetKey(isPrivate: true);
+var publicKeyXml = cryptoNet.GetKey(isPrivate: false);
+
+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.7. PEM Import/Export (Advanced)
+> Note: This example requires the CryptoNet.Extensions package. and still in development.
+
+~~~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");
+
+ICryptoNet importedFromPub = ExampleRsa.ImportPemKey(pubPem);
+ICryptoNet importedFromPri = ExampleRsa.ImportPemKey(priPem);
+ICryptoNet importedFromEncryptedPri = ExampleRsa.ImportPemKeyWithPassword(encryptedPriPem, "password");
+~~~
+
+---
+
+## Running All Examples (quick reference)
+
+~~~csharp
+using CryptoNet.Examples;
+
+// 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();
+~~~
+
+---
+
+## 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
new file mode 100644
index 0000000..1580434
--- /dev/null
+++ b/docs/migration.md
@@ -0,0 +1,331 @@
+# 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
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