public static class Disposable {
public static TResult Using<TDisposable,TResult>(
Func<TDisposable> factory,
Func<TDisposable, TResult> map)
where TDisposable : IDisposable
{
using (var disposable = factory()){
return map(disposable);
}
}
}
void Main()
{
var currentTime = EpochTime.AddSeconds(Disposable
.Using(() => new HttpClient(),
client => JsonDocument.Parse(client.GetStringAsync(@"http://worldtimeapi.org/api/timezone/europe/oslo").Result))
.RootElement
.GetProperty("unixtime")
.GetInt64()).ToLocalTime(); //list of time zones available here: http://worldtimeapi.org/api/timezone
currentTime.Dump("CurrentTime");
}
public static DateTime EpochTime => new DateTime(1970, 1, 1);
The Disposable is abstracted away in the helper method called Using accepting a factory function to create a TDisposable that accepts an IDisposable.
We look up the current time using the WorldTimeApi and make use of extracting the UnixTime which is measured from Epoch as the number of seconds elapsed from 1st January 1970.
We make use of System.Text.Json here, which is part of .NET to parse the json retrieved.
Saturday, 9 March 2024
Functional programming - looking up current time and encapsulating usings
I looked at encapsulating Using statements today for functional programming and how to look up the current time with API available on the Internet.
Thursday, 7 March 2024
Currying functions in C#
This article will look into helper methods for currying functions in C#. The definition of Currying consists of splitting up a function with multiple arguments
into multiple functions accepting one argument. But you can also have some of the arguments provided via smaller functions, so be aware also of this alternative.
What is in the name currying? The name has nothing to do with cooking from India, but comes from the mathematician Haskell Brooks Curry (!)
https://en.wikipedia.org/wiki/Haskell_Curry
A reason for introducing support for currying is that you can build complex functions from simpler functions as building blocks. Currying is explained great here:
https://www.c-sharpcorner.com/UploadFile/rmcochran/functional-programming-in-C-Sharp-currying/
We will see in the examples that we can provide multiple arguments at once and the syntax will look a bit special compared to other C# code. Curryings benefits is to allow a more flexible way to call a method. You can store into variables calls to a function providing a subset of argument and use that variable to either specify an intermediate other call or get the final result. Note - The function will be called when ALL arguments are provided ONCE ! This helps a lot of avoiding surprising side effects. Let's first look at a sample set of methods we want to support currying.
f(x,g(y,z)) or f(x,g(y,h(z))) - there more arguments you get there is more variations of number of parameters and methods you can pass in. Here is another example how you can build up a calculation uing simpler methods.
https://en.wikipedia.org/wiki/Haskell_Curry
A reason for introducing support for currying is that you can build complex functions from simpler functions as building blocks. Currying is explained great here:
https://www.c-sharpcorner.com/UploadFile/rmcochran/functional-programming-in-C-Sharp-currying/
We will see in the examples that we can provide multiple arguments at once and the syntax will look a bit special compared to other C# code. Curryings benefits is to allow a more flexible way to call a method. You can store into variables calls to a function providing a subset of argument and use that variable to either specify an intermediate other call or get the final result. Note - The function will be called when ALL arguments are provided ONCE ! This helps a lot of avoiding surprising side effects. Let's first look at a sample set of methods we want to support currying.
int FooFourArgs(string st, float x, int j, int k)
{
Console.WriteLine($"Inside method FooFourArgs. Got parameters: st={st}, x={x}, j={j}, k={k}");
return 42;
}
int FooThreeArgs(string st, float x, int j)
{
Console.WriteLine($"Inside method FooThreeArgs. Got parameters: st={st}, x={x}, j={j}");
return 42;
}
int FooTwoArgs(string st, float x)
{
Console.WriteLine($"Inside method FooTwoArgs. Got parameters: st={st}, x={x}");
return 41;
}
int FooOneArgs(string st)
{
Console.WriteLine($"Inside method FooOneArgs. Got parameters: st={st}");
return 40;
}
We want to call the sample methods above in a more flexible way by splitting the number of arguments we provide.
Let's see the extension methods to call up to four arguments to a function. Note the use of chaining the lambda operator (=>) to provide
the support for currying.
public static class FunctionExtensions
{
public static Func<T1, TResult> Curried<T1, TResult>(this Func<T1, TResult> func)
{
return x1 => func(x1);
}
public static Func<T1, Func<T2, TResult>> Curried<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
public static Func<T1, Func<T2, Func<T3, TResult>>> Curried<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func)
{
return x1 => x2 => x3 => func(x1, x2, x3);
}
public static Func<T1, Func<T2, Func<T3, Func<T4, TResult>>>> Curried<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> func)
{
return x1 => x2 => x3 => x4 => func(x1, x2, x3,x4);
}
}
The following main method shows how to use these curry helper methods:
void Main()
{
var curryOneArgsDelegate = new Func<string, int>((st) => FooOneArgs(st)).Curried();
var curryOneArgsPhaseOne = curryOneArgsDelegate("hello");
var curryTwoArgsDelegate = new Func<string, float, int>((st, x) => FooTwoArgs(st,x)).Curried();
var curryTwoArgsPhaseOne = curryTwoArgsDelegate("hello");
var curryTwoArgsPhaseTwo = curryTwoArgsPhaseOne(3.14f);
var curryThreeArgsDelegate = new Func<string, float, int, int>((st, x, j) => FooThreeArgs(st, x, j)).Curried();
var curryThreeArgsPhaseOne = curryThreeArgsDelegate("hello");
var curryThreeArgsPhaseTwo = curryThreeArgsPhaseOne(3.14f);
var curryThreeArgsPhaseThree = curryThreeArgsPhaseTwo(123);
//Or call currying in a single call passing in two or more parametres
var curryThreeArgsPhaseOneToThree = curryThreeArgsDelegate("hello")(3.14f)(123);
var curryFourArgsDelegate = new Func<string, float, int, int, int>((st, x, j, k) => FooFourArgs(st, x, j, k)).Curried();
var curryFourArgsPhaseOne = curryFourArgsDelegate("hello");
var curryFourArgsNextPhases = curryFourArgsPhaseOne(3.14f)(123)(456); //just pass in the last arguments if they are known at this stage
curryFourArgsDelegate("hello")(3.14f)(123)(456); //you can pass in 1-4 parameters to FooFourArgs method - all in a single call for example or one by one
}
The output we get is this. Note that we only call the methods we defined when all parameters are sent in. The function call which had partial argument list provided did not result into a function call.
Inside method FooOneArgs. Got parameters: st=hello
Inside method FooTwoArgs. Got parameters: st=hello, x=3,14
Inside method FooThreeArgs. Got parameters: st=hello, x=3,14, j=123
Inside method FooThreeArgs. Got parameters: st=hello, x=3,14, j=123
Inside method FooFourArgs. Got parameters: st=hello, x=3,14, j=123, k=456
So from a higher level, currying a function f(x,y,z) means adding support that you could call the function like this:f(x,g(y,z)) or f(x,g(y,h(z))) - there more arguments you get there is more variations of number of parameters and methods you can pass in. Here is another example how you can build up a calculation uing simpler methods.
void Main()
{
Func Area = (x,y) => x*y;
Func CubicArea = (x,y,z) => Area.Curried()(Area(x,y))(z);
CubicArea(3,2,4); //supplying all arguments manully is okay
}
CubicArea expects THREE arguments. The implementation allows us to use the Area function and via currying we can use that method and provide the last third argument avoiding compilation error.
Currying makes your functions allow more flexible ways of being called.
Saturday, 24 February 2024
Using IronPython to execute Python code from .NET
Let's look at some code showing how to execute Python code from .NET using IronPython!
IronPython provides support for Python scripts to run inside .NET and utilizes the Dynamic Language Runtime - DLR.
The DLR together allows the caller to get dynamic typing and dynamic method dispatch, which is central in the dynamic languages such as Python.
IronPython was first released in 2004, some 20 years ago. It has continued to evolve slowly and provides seamless integration into .NET ecosystem for Python developers.
In this article, I will present some simple code that shows how you can run Python code inside a .NET 8 console application.
We will load up some tuples in an array in some simple Python code, using IronPython.
Tuples in Python
Tuples in Python are immutable (such as in C#) and are defined using parentheses and comma-separated. This is the same as in C#, but Python had tuple support over 20 years before C#.
We will have to add one Nuget package, the IronPython package, in a net8.0 application.
HelloIronPythonDemo1.csproj
customers.py
https://docs.python.org/release/1.4/tut/node37.html#SECTION00630000000000000000.
Bear in mind, this is way back in 1996, C# was over 20 years later with its tuple support. If you install IronPython, you get a terminal where you can enter Python code (plus more functionality with .NET) such as shown below, where tuples are created and tuples may be composed or 'packed' and also 'unpacked', which is called deconstructed in .NET tuples. To execute code to retrieve this array of tuples, first create a ScriptEngine and then create a ScriptScope, which we will use to retrieve the Python-declared variable customers. We create a ScriptSource, where we use the ScriptEngine to load up either a string or a file. A dynamic variable will be used to get the array of tuples and we can loop through this array with a foreach loop and output its content.
Program.cs
HelloIronPythonDemo1.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IronPython" Version="3.4.1" />
</ItemGroup>
<ItemGroup>
<None Update="customers.py">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Consider the following array of tuples in Python :
customers.py
customers = [
('Jenna', 42, 165),
('Thor', 40, 174),
('Christopher', 18, 170),
('Liz', 16, 168),
]
Python code is very compact and you declare variables without specifying type such as in C#, Python uses a simple way of creating variables and while C# got support in C# 7 in 2017, Python has had support for tuples since its early days. In the Python 1.4 version, we find it documented here: https://docs.python.org/release/1.4/tut/node37.html#SECTION00630000000000000000.
Bear in mind, this is way back in 1996, C# was over 20 years later with its tuple support. If you install IronPython, you get a terminal where you can enter Python code (plus more functionality with .NET) such as shown below, where tuples are created and tuples may be composed or 'packed' and also 'unpacked', which is called deconstructed in .NET tuples. To execute code to retrieve this array of tuples, first create a ScriptEngine and then create a ScriptScope, which we will use to retrieve the Python-declared variable customers. We create a ScriptSource, where we use the ScriptEngine to load up either a string or a file. A dynamic variable will be used to get the array of tuples and we can loop through this array with a foreach loop and output its content.
Program.cs
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using static System.Console;
IronPythonDemo1.OutputSomeExternallyLoadedTuples();
public class IronPythonDemo1
{
public static void OutputSomeExternallyLoadedTuples()
{
var engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
//ScriptSource source = engine.CreateScriptSourceFromString(tupleStatement);
ScriptSource source = engine.CreateScriptSourceFromFile("customers.py");
source.Execute(scope);
dynamic customers = scope.GetVariable("customers");
foreach (var customer in customers)
{
Console.WriteLine($"(Name = {StringExtensions.FixedLength(customer[0], 20)}, Age = {StringExtensions.FixedLength(customer[1].ToString(), 8)}, Height={StringExtensions.FixedLength(customer[2].ToString(), 8)})");
}
}
}
Documentation for named tuples are available here: https://docs.python.org/3/library/collections.html#collections.namedtuple
Here is sample coding showing script that although it is more verbose, shows more readability of which field is which for a named tuple. In an ordinary tuple, you use indexes to retrieve the nth field (0-based). But with named tuples, you use a field name instead.
from collections import namedtuple
Customer = namedtuple('Customer', ['Name', 'Age', 'Height'])
customers2 = [
Customer(Name = 'Jenna', Age = 42, Height = 165),
Customer(Name = 'Thor', Age = 38, Height = 174),
Customer(Name = 'Christopher', Age = 42, Height = 170),
Customer(Name = 'Liz', Age = 42, Height = 168),
]
for cust in customers2:
print(f"{cust.Name} with a height of {cust.Height}(cm)")
This outputs:
Jenna with a height of 165(cm)
Thor with a height of 174(cm)
Christopher with a height of 170(cm)
Liz with a height of 168(cm)
When your tuple gets many fields, having this readability should reduce bugs. Also, if you add more fields to your tuple, you do not have to fix up indexes in your script. So code is a bit more verbose, but it is also more open for change and readable.
The FixedLength extension method is a simple method to output text to a fixed width.
public static class StringExtensions
{
public static string FixedLength(this string input, int length, char paddingchar = ' ')
{
if (string.IsNullOrWhiteSpace(input))
{
return input;
}
if (input.Length > length)
return input.Substring(0, length);
else
return input.PadRight(length, paddingchar);
}
}
Thursday, 1 February 2024
Creating a data table from IEnumerable of T and defining column order explicitly in C#
This article shows code how you can create a DataTable from a collection of T (IEnumerable<T>) and defining explicitly the column order.
An extension method for this looks like the following:
public static class DataTableExtensions
{
public static DataTable CreateOrderedDataTable<T>(this IEnumerable<T> data)
{
var dataTable = new DataTable();
var orderedProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.OrderBy(prop => GetColumnOrder(prop)).ToList();
foreach (var prop in orderedProps){
dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
if (data != null)
{
dataTable.BeginLoadData();
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext()){
var item = enumerator.Current;
var rowValues = new List<object>();
foreach (var prop in orderedProps){
rowValues.Add(prop.GetValue(item, null));
}
dataTable.Rows.Add(rowValues.ToArray());
}
dataTable.AcceptChanges();
}
return dataTable;
}
static int GetColumnOrder(PropertyInfo prop)
{
var displayAttribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() as DisplayAttribute;
int orderKey = displayAttribute?.Order ?? prop.MetadataToken;
return orderKey;
}
}
We order first by DisplayAttribute and the Order value, and fallback to property's MetadataToken. This is an integer value that also returns the order the property was declared,
in case you want to order just by the way properties are defined. We get the enumerator here and fetch the row one by one. We could use a simple foreach loop here too. Note the use of BeginLoadData
and AcceptChanges.
Consider the two classes next. One class does not set any explicit order, the other class uses the Display attribute's Order value to define a custom order of columns for the DataTable.
public class Car
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Color { get; set; }
}
public class CarV2
{
[Display(Order = 4)]
public int Id { get; set; }
[Display(Order = 3)]
public string Make { get; set; }
[Display(Order = 2)]
public string Model { get; set; }
[Display(Order = 14)]
public bool IsElectric { get; set; }
[Display(Order = -188865)]
public string Color { get; set; }
}
Next, the following little program in Linqpad tests this extension method and displays the datatables resulting with column ordering set.
void Main()
{
var cars = new List<Car>{
new Car { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new Car { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new Car { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new Car { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTable = cars.CreateOrderedDataTable();
dataTable.Dump("Cars datatable, data type is: Car");
var carV2s = new List<CarV2>{
new CarV2 { Id = 1, Make = "Audi", Model = "A5", Color = "Blue" },
new CarV2 { Id = 2, Make = "Volvo", Model = "XC60", Color = "Silver" },
new CarV2 { Id = 3, Make = "Golf", Model = "GTI", Color = "White" },
new CarV2 { Id = 4, Make = "Audi", Model = "A5", Color = "Blue" },
};
var dataTableV2 = carV2s.CreateOrderedDataTable();
dataTableV2.Dump("Carsv2 datatable, datatype is CarV2");
}
Sunday, 14 January 2024
Generating repeated data into variable in SQL Server in T-SQL
Let's see how we can create repeated data into variable of SQL Server in T-SQL.
Use the REPLICATE function to create repeated data like this:
In case you want to set the variable to data which is longer than 8000 characters, you must convert the argument to NVARCHAR(MAX).
DECLARE @myVariable NVARCHAR(MAX)
SET @myVariable = REPLICATE('.', 10)
PRINT @myVariable
PRINT len(@myVariable)
In case you want to set the variable to data which is longer than 8000 characters, you must convert the argument to NVARCHAR(MAX).
DECLARE @myVariable NVARCHAR(MAX)
SET @myVariable = REPLICATE(CONVERT(NVARCHAR(MAX),'.'), 1024*1024*2)
PRINT len(@myVariable)
Creating random content is also easy in T-SQL:
DECLARE @myVariable NVARCHAR(MAX)
SET @myVariable = REPLICATE(CONVERT(NVARCHAR(MAX),REPLACE(NEWID(),'-', '')), 4)
PRINT len(@myVariable)
PRINT @myVariable
NEWID() creates a new guid, and we strip away the '-' letter, giving 32 chars which we replicate above four times. Since we were below 8000 chars, we chould have skipped using convert to nvarchar(max).
Sunday, 31 December 2023
Password hashing in .NET
This article will look on different ways to hash a password in .NET.
MD5 was developed by Ron Rivest in 1991 and was used a lot in the 90s, but in 2005 it was
revealed it contains collisions. MD5 and SHA-1 is not advised to used in sensitive hashing related to
security anymore.
Instead, a PBKDF or Password Derived Key-derivation function algorithm will be used.
A PBKDF2-based method in Rfc2898DeriveBytes will be used. It has been available since .NET 6.
Users of Asp.net Core Identity are recommended to use PasswordHasher instead :
https://andrewlock.net/exploring-the-asp-net-core-identity-passwordhasher/
An overview of the arithmetic flow of PBKDF2 is shown below. In the diagram, SHA-512 is indicated, but the code shown in this article
uses SHA-256.
First off, to do a MD5 hash we can use the following :
to be more and more difficult for attacks.
uses SHA-256.
First off, to do a MD5 hash we can use the following :
static string Md5(string input){
using (var md5 = MD5.Create()){
var byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
var hash = BitConverter.ToString(byteHash).Replace("-", "");
return hash;
}
}
And to test it out we can run the following:
void Md5Demo()
{
string inputPassword = "abc123";
string md5Hash = Md5(inputPassword);
Console.WriteLine("MD5 Demonstration in .NET");
Console.WriteLine("-------------------------");
Console.WriteLine($"Password to hash: {inputPassword}");
Console.WriteLine($"MD5 hashed password: {md5Hash}");
Console.WriteLine();
}
MD5 Demonstration in .NET
-------------------------
Password to hash: abc123
MD5 hashed password: E99A18C428CB38D5F260853678922E03
The MD5 hash above agrees with the online MD5 hash here:
https://www.md5hashgenerator.com/
MD5 method here does not mention any salt, but this could be concatenated with the password to prevent against rainbow table attacks, that is
dictionary attacks.
Next, to perform PDKDF2 hashing, the code below can be used. Note that this algorithm will be run iteratively to generate a hash value that is
increasingly more computationally expensive to calculate the hash of compared to the number of iterations and includes a salt, making it scalable to be more and more difficult for attacks.
static byte[] _salt = RandomNumberGenerator.GetBytes(32);
static void HashPassword(string passwordToHash, int numberOfRounds)
{
var sw = Stopwatch.StartNew();
var hashedPassword = Rfc2898DeriveBytes.Pbkdf2(
passwordToHash,
_salt,
numberOfRounds,
HashAlgorithmName.SHA256,
32);
sw.Stop();
Console.WriteLine();
Console.WriteLine("Password to hash : " + passwordToHash);
Console.WriteLine("Hashed Password : " + Convert.ToBase64String(hashedPassword));
Console.WriteLine("Iterations < " + numberOfRounds + "> Elapsed Time: " + sw.ElapsedMilliseconds + " ms");
}
The value 32 here is the desired output length of the hash, we can decide how long the hash we get out of the call to the method.
We can then test out the Pbkdf2 method using an increasing number of iterations.
void RunPbkdf2HashDemo()
{
const string passwordToHash = "abc123";
Console.WriteLine("Password Based Key Derivation Function Demonstration in .NET");
Console.WriteLine("------------------------------------------------------------");
Console.WriteLine();
Console.WriteLine("PBKDF2 Hashes using Rfc2898DeriveBytes");
Console.WriteLine();
HashPassword(passwordToHash, 1);
HashPassword(passwordToHash, 10);
HashPassword(passwordToHash, 100);
HashPassword(passwordToHash, 1000);
HashPassword(passwordToHash, 10000);
HashPassword(passwordToHash, 100000);
HashPassword(passwordToHash, 1000000);
HashPassword(passwordToHash, 5000000);
}
This gives the following output:
Password Based Key Derivation Function Demonstration in .NET
------------------------------------------------------------
PBKDF2 Hashes using Rfc2898DeriveBytes
Password to hash : abc123
Hashed Password : eqeul5z7l2dPrOo8WjH/oTt0RYHvlZ2lvk8SUoTjZq4=
Iterations (1) Elapsed Time: 0 ms
Password to hash : abc123
Hashed Password : wfd8qQobzBPZvdemqrtZczqctFe0JeAkKjU3IJ48cms=
Iterations (10) Elapsed Time: 0 ms
Password to hash : abc123
Hashed Password : VY45SxzhqjYronha0kt1mQx+JRDVlXj82prX3H7kjII=
Iterations (100) Elapsed Time: 0 ms
Password to hash : abc123
Hashed Password : B0LfHgRSslG/lWe7hbp4jb8dEqQ/bZwNtxsaqbVBZ2I=
Iterations (1000) Elapsed Time: 0 ms
Password to hash : abc123
Hashed Password : LAHwpS4bnbO7CQ1r7buYgUTrp10FyaRyeK6hCwGwv20=
Iterations (10000) Elapsed Time: 1 ms
Password to hash : abc123
Hashed Password : WDjyPySpULXtVOVmSR9cYlzAY4LWeJqDBhszKAfIaPc=
Iterations (100000) Elapsed Time: 13 ms
Password to hash : abc123
Hashed Password : sDx6sOrTl2b7cNZGUAecg7YO4Md/g3eAtfQSvh/vxpM=
Iterations (1000000) Elapsed Time: 127 ms
Password to hash : abc123
Hashed Password : ruywLaR0QApOU5bkqE/x2AAhYJzBj5y6D3P3IxlIF2I=
Iterations (5000000) Elapsed Time: 643 ms
Note that it takes many iterations before the computation takes significant time.
Sources / links :
- OWASP recommends using 600 000 iterations when using SHA256 as noted here: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
- Hashing passwords using PBKDF2 are explained by Stephen Haunts here: Hashing Passwords Safely using a Password Based Key Derivation Function (PBKDF2)
Etiketter:
c#,
Cryptography,
dotnet,
Password hashing,
Rfc2898DeriveBytes
AES Encryption with Galois Counter Mode (GCM) in C#
This article presents some helper methods for performing AES Encryption using Galois Counter Mode (GCM). AES or Advanced Encryption Standard is the most used encryption algorithm used today, having overtaken DES and Triple DES
since 2001. We will look into the GCM mode of AES in this article.
AES-GCM class AesGcm is supported in .NET Core 3.0 and newer .NET versions, plus in .NET Standard 2.1.
AES-GCM is authenticated encryption, compared to default AES-CBC (Cipher Block Chaining).
Benefits of using GCM mode of AES is the following:
- Data authenticity / integrity. This is provided via a tag that is outputted by the encryption and used while decrypting
- Provides support for sending additional data, used for example in newer TLS implementations to provide both encryption and a non-encrypted payload. This is called additional metadata
public static class AesGcmEncryption {
public static (byte[], byte[]) Encrypt(byte[] dataToEncrypt, byte[] key, byte[] nonce, byte[] associatedData = null)
{
using var aesGcm = new AesGcm(key);
//tag and ciphertext will be filled during encryption
var tag = new byte[16]; //tag is a hmac (hash-based message authentication code) to check that information has not been tampered with
var cipherText = new byte[dataToEncrypt.Length];
aesGcm.Encrypt(nonce, dataToEncrypt, cipherText, tag, associatedData);
return (cipherText, tag);
}
public static byte[] Decrypt(byte[] cipherText, byte[] key, byte[] nonce, byte[] tag, byte[] associatedData = null)
{
using var aesGcm = new AesGcm(key);
//tag and ciphertext will be filled during encryption
var decryptedData = new byte[cipherText.Length];
aesGcm.Decrypt(nonce, cipherText, tag, decryptedData, associatedData);
return decryptedData;
}
}
In the code above, the encrypt method returns a tuple with the ciperText and the tag. These are the encrypted data and the tag, both must be used while decrypting and the tag provides as mentioned a means of checking the integrity of data, i.e. that data has not been tampered with.
Note that the 16-byte tag and the ciphertext is filled after running the Encrypt method of the AesGcm class. The cipherText array must be the same length as the dataToEncrypt array inputted.
Here is sample code to use AES-GCM. Note that the metadata used here, while optional, must match in case it is set in the encryption and decryption. The nonce must be 12 bytes - 96 bits in length.The nonce is similar to a initialization vector, although it is used once for the particular encryption and decryption,
it is used to protect against replay attacks.
void TestAesGCM()
{
const string original = "Text to encrypt";
var key = RandomNumberGenerator.GetBytes(32); //256 bits key
var nonce = RandomNumberGenerator.GetBytes(12); //96 bits nonce
(byte[] cipherText, byte[] tag) result = AesGcmEncryption.Encrypt(Encoding.UTF8.GetBytes(original),
key, nonce, Encoding.UTF8.GetBytes("some metadata 123"));
byte[] decryptedText = AesGcmEncryption.Decrypt(result.cipherText, key, nonce, result.tag, Encoding.UTF8.GetBytes("some metadata 123"));
Console.WriteLine("AES Encryption demo GCM - Galois Counter Mode:");
Console.WriteLine("--------------");
Console.WriteLine("Original Text = " + original);
Console.WriteLine("Encrypted Text = " + Convert.ToBase64String(result.cipherText));
Console.WriteLine("Tag = " + Convert.ToBase64String(result.tag));
Console.WriteLine("Decrypted Text = " + Encoding.UTF8.GetString(decryptedText));
}
AES Encryption demo GCM - Galois Counter Mode:
--------------
Original Text = Text to encrypt
Encrypted Text = 9+2x0kctnRwiDDHBm0/H
Tag = sSDxsg17HFdjE4cuqRlroQ==
Decrypted Text = Text to encrypt
Use AES-GCM to provide integrity checking and allowing to send in metadata if desired to encrypt and decrypting with the AES algorithm.
We can protect the AES key using different methods, for example using the Data Protection API, this is only supported in Windows.
Let's look at a helper class for using Data Protection API.
public static class DataProtectionUtil {
public static byte[] Protect(byte[] dataToEncrypt, byte[] optionalEntropy, DataProtectionScope scope)
{
var encryptedData = ProtectedData.Protect(dataToEncrypt, optionalEntropy, scope);
return encryptedData;
}
public static byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope){
var decryptedData = ProtectedData.Unprotect(encryptedData, optionalEntropy, scope);
return decryptedData;
}
public static string Protect(string dataToEncrypt, string optionalEntropy, DataProtectionScope scope)
{
var encryptedData = ProtectedData.Protect(Encoding.UTF8.GetBytes(dataToEncrypt), optionalEntropy != null ? Encoding.UTF8.GetBytes(optionalEntropy) : null, scope);
return Convert.ToBase64String(encryptedData);
}
public static string Unprotect(string encryptedData, string optionalEntropy, DataProtectionScope scope)
{
var decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData), optionalEntropy != null ? Encoding.UTF8.GetBytes(optionalEntropy) : null, scope);
return Encoding.UTF8.GetString(decryptedData);
}
}
An example how to protect your AES key:
void EncryptAndDecryptWithProtectedKey(){
var original = "Text to encrypt";
Console.WriteLine($"Original Text = {original}");
//Create key and nnoce . Encrypt our text with AES
var gcmKey = RandomNumberGenerator.GetBytes(32);
var nonce = RandomNumberGenerator.GetBytes(12);
var result = EncryptText(original, gcmKey, nonce);
//Create some entropy and protect AES key
var entropy = RandomNumberGenerator.GetBytes(16);
var protectedKey = ProtectedData.Protect(gcmKey, entropy, DataProtectionScope.CurrentUser);
Console.WriteLine($"gcmKey = {Convert.ToBase64String(gcmKey)}, protectedKey = {Convert.ToBase64String(protectedKey)}");
// Decrypt the text with AES. the AES key has to be retrieved with DPAPI.
var decryptedText = DecryptText(result.encrypted, nonce, result.tag, protectedKey, entropy);
Console.WriteLine($"Decrypted Text using AES GCM with key retrieved via Data Protection API = {decryptedText}");
}
private static (byte[] encrypted, byte[] tag) EncryptText(string original, byte[] gcmKey, byte[] nonce){
return AesGcmEncryption.Encrypt(Encoding.UTF8.GetBytes(original), gcmKey, nonce, Encoding.UTF8.GetBytes("some meta"));
}
private static string DecryptText(byte[] encrypted, byte[] nonce, byte[] tag, byte[] protectedKey, byte[] entropy){
var key = DataProtectionUtil.Unprotect(protectedKey, entropy, DataProtectionScope.CurrentUser);
Console.WriteLine($"Inside DecryptText: gcmKey = {Convert.ToBase64String(key)}, protectedKey = {Convert.ToBase64String(protectedKey)}");
var decryptedText = AesGcmEncryption.Decrypt(encrypted, key, nonce, tag, Encoding.UTF8.GetBytes("some meta"));
return Encoding.UTF8.GetString(decryptedText);
}
Data Protection API is only supported on Windows platform, there are more possibilities to protect AES key but protecting your key is always a challenge when dealing with symmetric encryption algorithms such as AES.
Some more links:
- What is a Nonce ? https://www.youtube.com/watch?v=EOgpr73-pgc
- AES Explained - https://www.youtube.com/watch?v=O4xNJsjtN6E
- Galois/Counter Mode (GCM) - https://www.youtube.com/watch?v=V2TlG3JbGp0
- Authenticated Encryption in .NET with AES-GCM - https://www.scottbrady91.com/c-sharp/aes-gcm-dotnet
Thursday, 28 December 2023
Digital signatures with RSA in .NET
I have looked at Digital signatures with RSA in .NET today. Digital signatures are used to provide non-repudiation, an authenticity proof that the original sender is who the sender claims to be and
also that the data has not been hampered with.
We will return a tuple of both a SHA-256 computed hash of some document data and also its digital signature using the RSA algorithm.
I have used
export the public part of the X509 certificate to the receiver, which the receiver could install in a certificate store, only requred to know the thumbprint of the cert which is easy to see in MMC (Microsoft Management Console) or using Powershell and cd-ing into cert:\ folder . Let's first look at a helper class to get hold of a installed X509 certificate.
.netstandard 2.0 here, so the code can be used in most frameworks in both .NET Framework and .NET. We will use RSA here to do the digital signature signing and verification.
First off, here is a helper class to create a RSA encrypted signature of a SHA-256 hash, here we create a new RSA with key size 2048.
RsaDigitalSignature.cs
public class RsaDigitalSignature
{
private RSA _rsa;
public RsaDigitalSignature()
{
_rsa = RSA.Create();
_rsa.KeySize = 2048;
}
public static byte[] ComputeHashSha256(byte[] toBeHashed)
{
using (var sha256 = SHA256.Create())
{
return sha256.ComputeHash(toBeHashed);
}
}
public (byte[] Signature, byte[] HashOfData) SignData(byte[] dataToSign)
{
var hashOfDataToSign = ComputeHashSha256(dataToSign);
return (_rsa.SignHash(
hashOfDataToSign,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1),
hashOfDataToSign);
}
public bool VerifySignature(byte[] signature, byte[] hashOfDataToSign)
{
return _rsa.VerifyHash(hashOfDataToSign, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
In the code above, we receive some document data and create the SHA-255 hash, which is computed. We return a tuple with the signed hash from the computed SHA-256 hash and also the computed SHA-256 hash itself.
A console application that runs the sample code above is the following:
void Main()
{
SignAndVerifyData();
//Console.ReadLine();
}
private static void SignAndVerifyData()
{
Console.WriteLine("RSA-based sigital signature demo");
var document = Encoding.UTF8.GetBytes("Document to sign");
var digitalSignature = new RsaDigitalSignature();
var signature = digitalSignature.SignData(document);
bool isValidSignature = digitalSignature.VerifySignature(signature.Signature, signature.HashOfData);
Console.WriteLine($"\nInput Document:\n{Convert.ToBase64String(document)}\nIs the digital signature valid? {isValidSignature} \nSignature: {Convert.ToBase64String(signature.Signature)} \nHash of data:\n{ Convert.ToBase64String(signature.HashOfData)}");
}
Our verification of the signature shows that the verification of the digital signature passes.
Input Document:
RG9jdW1lbnQgdG8gc2lnbg==
Is the digital signature valid? True
Signature: Gok1x8Wxm9u5jTRcqrgPsI45ie3WPZLi/FNbaJMGTHqBmNbpJTEYjsXix97aIF6uPjgrxQWJKCegc8S4yASdut7TpJafO9wSRqvScc2SuOGK9BqnX+9GwRRQNti8ynm0ARRar+Z4hTpYY/XngFZ+ovvqIT3KRMK/7tsMmTg87mY0KelteFX7z7G7wPB9kKjT6ORYK4lVr35fihrbxei0XQP59YuEdALy+vbvKUm3JNv4sBU0lc9ZKpp2XF0rud8UnY1Nz4/XH7ZoaKfca5HXs9yq89DJRaPBRi1+Wv41vTCf8zFKPWZJrw6rm6kBMNHMENYbeBNdZyiCspTsHZmsVA==
Hash of data:
VPPxOVW2A38lCB810vuZbBH50KQaPSCouN0+tOpYDYs=
The code above uses a RSA created on the fly and is not so easy to share between a sender and a receiver. Let's look at how we can use X509 certificates to do the RSA encyption. It should be possible to share the source code below between the sender and the receiver and for exampleexport the public part of the X509 certificate to the receiver, which the receiver could install in a certificate store, only requred to know the thumbprint of the cert which is easy to see in MMC (Microsoft Management Console) or using Powershell and cd-ing into cert:\ folder . Let's first look at a helper class to get hold of a installed X509 certificate.
public class CertStoreUtil
{
public static System.Security.Cryptography.X509Certificates.X509Certificate2 GetCertificateFromStore(
System.Security.Cryptography.X509Certificates.StoreLocation storeLocation,
string thumbprint, bool validOnly = true) {
var store = new X509Store(storeLocation);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly).FirstOrDefault();
store.Close();
return cert;
}
}
Next up, a helper class to create a RSA-based digital signature like in the previous example, but using a certificate.
public class RsaFromCertDigitalSignature
{
private RSA _privateKey;
private RSA _publicKey;
public RsaFromCertDigitalSignature(StoreLocation storeLocation, string thumbprint)
{
_privateKey = CertStoreUtil.GetCertificateFromStore(StoreLocation.LocalMachine, thumbprint).GetRSAPrivateKey();
_publicKey = CertStoreUtil.GetCertificateFromStore(StoreLocation.LocalMachine, thumbprint).GetRSAPrivateKey();
}
public static byte[] ComputeHashSha256(byte[] toBeHashed)
{
using (var sha256 = SHA256.Create())
{
return sha256.ComputeHash(toBeHashed);
}
}
public (byte[] Signature, byte[] HashOfData) SignData(byte[] dataToSign)
{
var hashOfDataToSign = ComputeHashSha256(dataToSign);
return (_privateKey.SignHash(
hashOfDataToSign,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1),
hashOfDataToSign);
}
public bool VerifySignature(byte[] signature, byte[] hashOfDataToSign)
{
return _publicKey.VerifyHash(hashOfDataToSign, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
A console app that tests out the code above is shown next, I have selected a random cert on my dev pc here.
void Main()
{
SignAndVerifyData();
//Console.ReadLine();
}
private static void SignAndVerifyData()
{
Console.WriteLine("RSA-based sigital signature demo");
var document = Encoding.UTF8.GetBytes("Document to sign");
//var x509CertLocalHost = CertStoreUtil.GetCertificateFromStore(StoreLocation.LocalMachine, "1f0b749ff936abddad89f4bbea7c30ed64e3dd07");
var digitalSignatureWithCert = new RsaFromCertDigitalSignature(StoreLocation.LocalMachine, "1f0b749ff936abddad89f4bbea7c30ed64e3dd07");
var signatureWithCert = digitalSignatureWithCert.SignData(document);
bool isValidSignatureFromCert = digitalSignatureWithCert.VerifySignature(signatureWithCert.Signature, signatureWithCert.HashOfData);
Console.WriteLine(
$@"Input Document:
{Convert.ToBase64String(document)}
Is the digital signature signed with private key of CERT valid according to public key of CERT? {isValidSignatureFromCert}
Signature: {Convert.ToBase64String(signatureWithCert.Signature)}
Hash of data:\n{Convert.ToBase64String(signatureWithCert.HashOfData)}");
}
Now here is an important concept in digital signatures :
- For digital signatures, we MUST use a private key (e.g. private key of RSA instance, which can either be made on the fly or retrieved from for example a X509 certificate. Or a Json web key in a more modern example.
- For digital signature, to verify a signature we can use either the public or the private key, usually just the public key (which can be shared). For X509 certiifcates, we usually share a public cert (.cert or similar format) and keep our private cert ourselves (.pfx).
RSA-based sigital signature demo
Input Document:
RG9jdW1lbnQgdG8gc2lnbg==
Is the digital signature signed with private key of CERT valid according to public key of CERT? True
Signature: ZHWzJeZnwbfI109uK0T4ubq4B+CHedQPIDgPREz+Eq9BR6A9y6kQEvSrxqUHvOppSDN5kDt5bTiWv1pvDPow+czb7N6kmFf1zQUxUs3ip4WPovBtQKmfpf9/i3DNkRILcoMLdZdKnn0aSaK66f0oxkSIc4nEkb3O9PbejVso6wLqSdDCh96d71gbHqOjyiZLBj2VlqalWvEPuo9GB0s2Uz2fxtFGMUQiZvH3jKR+9F4LwvKCc1K0E/+J4Np57JSfKgmid9QyL2r7nO19SVoVL3yBY7D8UxVIRw8sT/+JKXlnyh8roK7kaxDtW4+FMK6LT/QPvi8LkiNmA+eVv3kk9w==
Hash of data:\nVPPxOVW2A38lCB810vuZbBH50KQaPSCouN0+tOpYDYs=
Subscribe to:
Comments (Atom)



