This week at Stratiteq, on our weekly tech talk we spoke about cryptography and concepts behind it with practical examples in .Net Core.
Cryptography (from Ancient Greek, cruptos = "hidden", graphein = "to write") is the study of techniques for preventing third parties from reading or manipulating private messages. Cryptography has been around for a long time and it was mostly used for military purposes. Technology behind it did not change much over time and "boom" happened in second part of 20th century, so nowadays we can find cryptography everywhere:
- Secure network communication (TLS/SSL)
- Authentication and digital signatures
- Contactless
- Block chain technology
- Bank / credit cards
- Hard disk encryption
- GSM
- E-mail, FTP, biometry, etc...
Cryptography is not just used in the technologies listed above, we can see many industry leaders talking about it, we see it disrupting industries and we can for sure say it is in some way changing the world around us.
Even it sounds complicated, it more or less boils down into three simple concepts. We'll not go into math and science behind it, but we are going to explain the concepts and go through examples which you can try by yourself.
Those three main concepts are:
- Hashing
- Encryption
- Signatures
Hashing
Hashing can be simply explained as translation from input text into resulting text of fixed size by use of the algorithm.
As you can see in above examples with just a slight change of input text, result will be completely different.
Let's take a look at an example in .Net Core with use of SHA-256 algorithm. For following examples we'll have to use System.Security.Cryptography namespace, you can find more info about it here.
using System.Security.Cryptography;
In following function we are hashing texts to demonstrate how hashing works, and how results change if we just make a slight change to the text.
internal static void CryptographicHashFunctionDemo()
{
Console.WriteLine("***** Cryptographic hash demo *****");
foreach (var message in new[] {
"Fox",
"The red fox jumps over the blue dog",
"The red fox jumps ouer the blue dog",
"The red fox jumps oevr the blue dog",
"The red fox jumps oer the blue dog"})
{
Console.WriteLine($"{ message } => { CryptographicHash.ComputeHash(message) }");
}
Console.Write(Environment.NewLine);
}
If we take a closer look at the ComputeHash we can see texts are encrypted with SHA-256 algorithm.
internal static string ComputeHash(string message)
{
using var sha256 = SHA256.Create();
var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(message));
return hashedBytes.ToHex();
}
By running the code, you'll get following results.
Encryption
Encryption is the process of sending information to a recipient that can't be interpreted unless having access to a key. Encryption always consist of two parts, an algorithm and a key.
Encryption can be symmetric and asymmetric. In symmetric encryption same key is used for encryption and decryption, while in asymmetric different keys are in use: public key and private key.
Symmetric encryption can be seen in the following example. We create a key, encrypt message using the key and decrypt message using the same key.
internal static void SymmetricEncryptionDemo()
{
Console.WriteLine("***** Symmetric encryption demo *****");
var unencryptedMessage = "To be or not to be, that is the question, whether tis nobler in the...";
Console.WriteLine("Unencrypted message: " + unencryptedMessage);
// 1. Create a key (shared key between sender and reciever).
byte[] key, iv;
using (Aes aesAlg = Aes.Create())
{
key = aesAlg.Key;
iv = aesAlg.IV;
}
// 2. Sender: Encrypt message using key
var encryptedMessage = SymmetricEncryption.Encrypt(unencryptedMessage, key, iv);
Console.WriteLine("Sending encrypted message: " + encryptedMessage.ToHex());
// 3. Receiver: Decrypt message using same key
var decryptedMessage = SymmetricEncryption.Decrypt(encryptedMessage, key, iv);
Console.WriteLine("Recieved and decrypted message: " + decryptedMessage);
Console.Write(Environment.NewLine);
}
In SymmetricEncryption class we have Encrypt and Decrypt defined as follows. You can notice we are using AES algorithm.
internal static byte[] Encrypt(string message, byte[] key, byte[] iv)
{
using var aesAlg = Aes.Create();
aesAlg.Key = key;
aesAlg.IV = iv;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using var ms = new MemoryStream();
using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
using (var sw = new StreamWriter(cs))
{
sw.Write(message); // Write all data to the stream.
}
return ms.ToArray();
}
internal static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
{
using var aesAlg = Aes.Create();
aesAlg.Key = key;
aesAlg.IV = iv;
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using var ms = new MemoryStream(cipherText);
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
using var sr = new StreamReader(cs);
return sr.ReadToEnd();
}
If you run the code, you'll get following result.
Asymmetric encryption has few common algorithms that are in the use:
- RSA; by Ron Rivest, Adi Shamir and Len Adleman; from 1977
- EC (Elliptic Curve); Neal Koblitz and Victor S. Miller; from 1985
These algorithms can be used for encryption and decryption, as well as digital signatures.
In another example we can see asymmetric encryption in action using the RSA algorithm.
internal static void AsymmetricEncryptionDemo()
{
Console.WriteLine("***** Asymmetric encryption demo *****");
var unencryptedMessage = "To be or not to be, that is the question, whether tis nobler in the...";
Console.WriteLine("Unencrypted message: " + unencryptedMessage);
// 1. Create a public / private key pair.
RSAParameters privateAndPublicKeys, publicKeyOnly;
using (var rsaAlg = RSA.Create())
{
privateAndPublicKeys = rsaAlg.ExportParameters(includePrivateParameters: true);
publicKeyOnly = rsaAlg.ExportParameters(includePrivateParameters: false);
}
// 2. Sender: Encrypt message using public key
var encryptedMessage = AsymmetricEncryption.Encrypt(unencryptedMessage, publicKeyOnly);
Console.WriteLine("Sending encrypted message: " + encryptedMessage.ToHex());
// 3. Receiver: Decrypt message using private key
var decryptedMessage = AsymmetricEncryption.Decrypt(encryptedMessage, privateAndPublicKeys);
Console.WriteLine("Recieved and decrypted message: " + decryptedMessage);
Console.Write(Environment.NewLine);
}
As you can see, in the AsymmetricEncryptionDemo function we create a public and a private key pair, we encrypt the message using the public key and we decrypt message using the private key.
Encrypt and Decrypt are defined as follows and we are using RSA algorithm.
internal static byte[] Encrypt(string message, RSAParameters rsaParameters)
{
using var rsaAlg = RSA.Create(rsaParameters);
return rsaAlg.Encrypt(Encoding.UTF8.GetBytes(message), RSAEncryptionPadding.Pkcs1);
}
internal static string Decrypt(byte[] cipherText, RSAParameters rsaParameters)
{
using var rsaAlg = RSA.Create(rsaParameters);
var decryptedMessage = rsaAlg.Decrypt(cipherText, RSAEncryptionPadding.Pkcs1);
return Encoding.UTF8.GetString(decryptedMessage);
}
Result in the console looks as follows.
Digital signatures
Digital signatures are built on top of asymmetric cryptography. They are used to send information to recipients who can verify that the information was sent from a trusted sender, using a public key.
Let's take a look at the example of digital signature:
internal static void MessageSignatureDemo()
{
Console.WriteLine("***** Message signature demo *****");
var message = "To be or not to be, that is the question, whether tis nobler in the...";
Console.WriteLine("Message to be verified: " + message);
// 1. Create a public / private key pair.
RSAParameters privateAndPublicKeys, publicKeyOnly;
using (var rsaAlg = RSA.Create())
{
privateAndPublicKeys = rsaAlg.ExportParameters(includePrivateParameters: true);
publicKeyOnly = rsaAlg.ExportParameters(includePrivateParameters: false);
}
// 2. Sender: Sign message using private key
var signature = AsymmetricEncryption.Sign(message, privateAndPublicKeys);
Console.WriteLine("Message signature: " + signature.ToHex());
// 3. Receiver: Verify message authenticity using public key
var isTampered = AsymmetricEncryption.Verify(message, signature, publicKeyOnly);
Console.WriteLine("Message is untampered: " + isTampered.ToString());
Console.Write(Environment.NewLine);
}
In the MessageSignatureDemo function we create a public and a private key pair, we sign the message with the private key and we verify the message for authenticity using public key.
Sign and Verify works in the following way, we're using the RSA algorithm.
internal static byte[] Sign(string message, RSAParameters rsaParameters)
{
using var rsaAlg = RSA.Create(rsaParameters);
return rsaAlg.SignData(Encoding.UTF8.GetBytes(message), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
internal static bool Verify(string message, byte[] signature, RSAParameters rsaParameters)
{
using var rsaAlg = RSA.Create(rsaParameters);
return rsaAlg.VerifyData(Encoding.UTF8.GetBytes(message), signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
Console will display following result for running digital signature example.
Project and all code from this blog post is available at the GitHub here. Thanks for reading!
Top comments (2)
Thanks for the article Paul! Awesome breakdown 🙌
Thanks for the article.
But I cannot find ToHex() method