diff --git a/Encrypter Tests/Encrypter Tests.csproj b/Encrypter Tests/Encrypter Tests.csproj
index 5680f21..072f091 100644
--- a/Encrypter Tests/Encrypter Tests.csproj
+++ b/Encrypter Tests/Encrypter Tests.csproj
@@ -10,7 +10,11 @@
-
+
+
+
+
+
diff --git a/Encrypter Tests/EncrypterTests.cs b/Encrypter Tests/EncrypterTests.cs
new file mode 100644
index 0000000..bc1d3b8
--- /dev/null
+++ b/Encrypter Tests/EncrypterTests.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Encrypter;
+using NUnit.Framework;
+
+namespace Encrypter_Tests
+{
+ public sealed class EncrypterTests
+ {
+ [Test]
+ public async Task TestSimpleEnAndDecryption()
+ {
+ var message = "This is a test with umlauts äüö.";
+ var password = "test password";
+
+ var encryptedData = await CryptoProcessor.EncryptString(message, password);
+ Assert.That(encryptedData.Length, Is.AtLeast(message.Length)); // Note: Encrypted data contains salt as well!
+
+ var decryptedMessage = await CryptoProcessor.DecryptString(encryptedData, password);
+ Assert.That(decryptedMessage, Is.EqualTo(message));
+ }
+ }
+}
diff --git a/Encrypter/CryptoProcessor.cs b/Encrypter/CryptoProcessor.cs
new file mode 100644
index 0000000..8d20ed9
--- /dev/null
+++ b/Encrypter/CryptoProcessor.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Encrypter
+{
+ public static class CryptoProcessor
+ {
+ private const int ITERATIONS = 6_000_000;
+
+ ///
+ /// Encrypts a string by means of AES. The result gets base64 encoded.
+ /// Due to the necessary millions of SHA512 iterations, the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ /// This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ /// data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ /// the present memory, this method could be used.
+ ///
+ /// The UTF8 encoded string to encrypt.
+ /// The password. Must consists of 6 chars or more.
+ /// The base64 encoded and encrypted string. The string is ASCII encoding.
+ public static async Task EncryptString(string data, string password)
+ {
+ if (string.IsNullOrWhiteSpace(password) || password.Length < 6)
+ throw new CryptographicException("The password was empty or shorter than 6 characters.");
+
+ if(data == null)
+ throw new CryptographicException("The data cannot be null.");
+
+ // Generate new random salt:
+ var saltBytes = Guid.NewGuid().ToByteArray();
+
+ // Derive key and iv vector:
+ var key = new byte[32];
+ var iv = new byte[16];
+
+ // The following operations take several seconds. Thus, using a task:
+ await Task.Run(() =>
+ {
+ using var keyVectorObj = new Rfc2898DeriveBytes(password, saltBytes, ITERATIONS, HashAlgorithmName.SHA512);
+ key = keyVectorObj.GetBytes(32); // the max valid key length = 256 bit = 32 bytes
+ iv = keyVectorObj.GetBytes(16); // the only valid block size = 128 bit = 16 bytes
+ });
+
+ // Create AES encryption:
+ using var aes = Aes.Create();
+ aes.Padding = PaddingMode.PKCS7;
+ aes.Key = key;
+ aes.IV = iv;
+
+ using var encryption = aes.CreateEncryptor();
+
+ // Copy the given string data into a memory stream
+ await using var plainDataStream = new MemoryStream(Encoding.UTF8.GetBytes(data));
+
+ // A memory stream for the final, encrypted data:
+ await using var encryptedAndEncodedData = new MemoryStream();
+
+ // A base64 stream for the encoding:
+ await using var base64Stream = new CryptoStream(encryptedAndEncodedData, new ToBase64Transform(), CryptoStreamMode.Write);
+
+ // Write the salt into the base64 stream:
+ await base64Stream.WriteAsync(saltBytes);
+
+ // Create the encryption stream:
+ await using var cryptoStream = new CryptoStream(base64Stream, encryption, CryptoStreamMode.Write);
+
+ // Write the payload into the encryption stream:
+ await plainDataStream.CopyToAsync(cryptoStream);
+
+ // Flush the final block. Please note, that it is not enough to call the regular flush method!
+ cryptoStream.FlushFinalBlock();
+
+ // Clears all sensitive information:
+ aes.Clear();
+ Array.Clear(key, 0, key.Length);
+ Array.Clear(iv, 0, iv.Length);
+ password = string.Empty;
+
+ // Convert the base64 encoded data back into a string. Uses GetBuffer due to the advantage, that
+ // it does not create another copy of the data. ToArray would create another copy of the data!
+ return Encoding.ASCII.GetString(encryptedAndEncodedData.GetBuffer()[..(int)encryptedAndEncodedData.Length]);
+ }
+
+ ///
+ /// Decrypts an base64 encoded and encrypted string. Due to the necessary millions of SHA512 iterations,
+ /// the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ /// This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ /// data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ /// the present memory, this method could be used.
+ ///
+ /// The base64 encoded and AES encrypted string. This string must be ASCII encoded.
+ /// The password. Must consists of 6 chars or more.
+ /// The decrypted UTF8 encoded string.
+ public static async Task DecryptString(string base64EncodedAndEncryptedData, string password)
+ {
+ if (string.IsNullOrWhiteSpace(password) || password.Length < 6)
+ throw new CryptographicException("The password was empty or shorter than 6 characters.");
+
+ if (base64EncodedAndEncryptedData == null)
+ throw new CryptographicException("The data cannot be null.");
+
+ // Build a memory stream to access the given base64 encoded data:
+ await using var encodedEncryptedStream = new MemoryStream(Encoding.ASCII.GetBytes(base64EncodedAndEncryptedData));
+
+ // Wrap around the base64 decoder stream:
+ await using var base64Stream = new CryptoStream(encodedEncryptedStream, new FromBase64Transform(), CryptoStreamMode.Read);
+
+ // A buffer for the salt's bytes:
+ var saltBytes = new byte[16]; // 16 bytes = Guid
+
+ // Read the salt's bytes out of the stream:
+ await base64Stream.ReadAsync(saltBytes, 0, saltBytes.Length);
+
+ // Derive key and iv vector:
+ var key = new byte[32];
+ var iv = new byte[16];
+
+ // The following operations take several seconds. Thus, using a task:
+ await Task.Run(() =>
+ {
+ using var keyVectorObj = new Rfc2898DeriveBytes(password, saltBytes, ITERATIONS, HashAlgorithmName.SHA512);
+ key = keyVectorObj.GetBytes(32); // the max valid key length = 256 bit = 32 bytes
+ iv = keyVectorObj.GetBytes(16); // the only valid block size = 128 bit = 16 bytes
+ });
+
+ // Create AES decryption:
+ using var aes = Aes.Create();
+ aes.Padding = PaddingMode.PKCS7;
+ aes.Key = key;
+ aes.IV = iv;
+
+ using var decryption = aes.CreateDecryptor();
+
+ // A memory stream for the final, decrypted data:
+ await using var decryptedData = new MemoryStream();
+
+ // The crypto stream:
+ await using var cryptoStream = new CryptoStream(base64Stream, decryption, CryptoStreamMode.Read);
+
+ // Reads all remaining data trough the decrypt stream. Note, that this operation
+ // starts at the current position, i.e. after the salt bytes:
+ await cryptoStream.CopyToAsync(decryptedData);
+
+ // Clears all sensitive information:
+ aes.Clear();
+ Array.Clear(key, 0, key.Length);
+ Array.Clear(iv, 0, iv.Length);
+ password = string.Empty;
+
+ // Convert the decrypted data back into a string. Uses GetBuffer due to the advantage, that
+ // it does not create another copy of the data. ToArray would create another copy of the data!
+ return Encoding.UTF8.GetString(decryptedData.GetBuffer()[..(int)decryptedData.Length]);
+ }
+ }
+}
diff --git a/Encrypter/Encrypter.csproj b/Encrypter/Encrypter.csproj
index 0b15e76..b4016db 100644
--- a/Encrypter/Encrypter.csproj
+++ b/Encrypter/Encrypter.csproj
@@ -13,6 +13,10 @@
LICENSE
+
+ C:\Users\Thorsten\Downloads\repos\Encrypter\Encrypter\Encrypter.xml
+
+
True
diff --git a/Encrypter/Encrypter.xml b/Encrypter/Encrypter.xml
new file mode 100644
index 0000000..b5dfcbd
--- /dev/null
+++ b/Encrypter/Encrypter.xml
@@ -0,0 +1,56 @@
+
+
+
+ Encrypter
+
+
+
+
+ Encrypts a string by means of AES. The result gets base64 encoded.
+ Due to the necessary millions of SHA512 iterations, the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ the present memory, this method could be used.
+
+ The UTF8 encoded string to encrypt.
+ The password. Must consists of 6 chars or more.
+ The base64 encoded and encrypted string. The string is ASCII encoding.
+
+
+
+ Decrypts an base64 encoded and encrypted string. Due to the necessary millions of SHA512 iterations,
+ the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ the present memory, this method could be used.
+
+ The base64 encoded and AES encrypted string. This string must be ASCII encoded.
+ The password. Must consists of 6 chars or more.
+ The decrypted UTF8 encoded string.
+
+
+
+ Encrypts this string by means of AES. The result gets base64 encoded.
+ Due to the necessary millions of SHA512 iterations, the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ the present memory, this method could be used.
+
+ This UTF8 encoded string to encrypt.
+ The password. Must consists of 6 chars or more.
+ The base64 encoded and encrypted string. The string is ASCII encoding.
+
+
+
+ Decrypts an base64 encoded and encrypted string. Due to the necessary millions of SHA512 iterations,
+ the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ the present memory, this method could be used.
+
+ The base64 encoded and AES encrypted string. This string must be ASCII encoded.
+ The password. Must consists of 6 chars or more.
+ The decrypted UTF8 encoded string.
+
+
+
diff --git a/Encrypter/Extensions.cs b/Encrypter/Extensions.cs
new file mode 100644
index 0000000..534a655
--- /dev/null
+++ b/Encrypter/Extensions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Encrypter
+{
+ public static class Extensions
+ {
+ ///
+ /// Encrypts this string by means of AES. The result gets base64 encoded.
+ /// Due to the necessary millions of SHA512 iterations, the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ /// This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ /// data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ /// the present memory, this method could be used.
+ ///
+ /// This UTF8 encoded string to encrypt.
+ /// The password. Must consists of 6 chars or more.
+ /// The base64 encoded and encrypted string. The string is ASCII encoding.
+ public static async Task Encrypt(this string data, string password)
+ {
+ return await CryptoProcessor.EncryptString(data, password);
+ }
+
+ ///
+ /// Decrypts an base64 encoded and encrypted string. Due to the necessary millions of SHA512 iterations,
+ /// the methods runs at least several seconds in the year 2020 (approx. 5-7s).
+ /// This method suits for small data such as telegrams, JSON data, text notes, passwords, etc. For larger
+ /// data, might use the stream overload. Rule of thumb: If the data could be stored three times in
+ /// the present memory, this method could be used.
+ ///
+ /// The base64 encoded and AES encrypted string. This string must be ASCII encoded.
+ /// The password. Must consists of 6 chars or more.
+ /// The decrypted UTF8 encoded string.
+ public static async Task Decrypt(this string data, string password)
+ {
+ return await CryptoProcessor.DecryptString(data, password);
+ }
+ }
+}