I need to correct an encryption method written in C#.
First, a little background: I am taking charge of an existing web application with an ecma6 / html frontend and c# web api .net standard 4.6 backend.
It has many integrations with different customers for user identification. Some of the integrations simply navigate to a customers URL to do a login process on the infrastructure of the customer, and then return to the app with an encripted user token in the URL's query string.
This token is encripted using AES256 encryption.
The backend is correctly decrypting the tokens, but when I tried to use the encryption routine to build a unit test, I discovered something is wrong. When I encrypt and then decrypt a message, the decryption routine throws the following error:
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Length of the data to decrypt is invalid.
Input message is "key1=value1;key2=value2" (without the quotes)
Encrypted message I get is NzcrOTc3Kzk3Nys5NzcrOTc3Kzk3Nys5NzcrOVpsVHZ2NzF3NzcrOUZ6UVlRZ3Z2djcxSVlPKy92U0V6NzcrOVNqZFY3Nys5VHpBZA==
I need to correct the implementation error in the encryption method. The implementation of the decryption method shows expected behavior, and you'll notice a double Base64 decoding done on the encrypted string: this is given, as we are integrated with an already developed encryption routine done by a customer in PERL which we detected did double encoding.
I inspected the order of operations to see a mismatch among the encryption and decryption and I was unable to detect an inconsistency, so the need to ask for help.
The code I synthesized for this test is:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class MainClass {
public static void Main (string[] args) {
var secretKey = "This is my secret key";
var secretIV = "This is my secret iv";
var originalMessage = "key1=value1;key2=value2";
var userToken = Cryptography.EncryptAES256CBCBase64x2(originalMessage, secretKey, secretIV);
Console.WriteLine(userToken);
var unencryptedToken = Cryptography.DecryptAES256CBCBase64x2(userToken, secretKey, secretIV);
if (originalMessage == unencryptedToken)
Console.WriteLine("All fine!");
else
Console.WriteLine("Error!");
}
}
public static class Cryptography
{
public static string DecryptAES256CBCBase64x2(string base64EncryptedString, string secretKey, string secretIV)
{
base64EncryptedString = SaveBase64String(base64EncryptedString);
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes =
Convert.FromBase64String(
Encoding.UTF8.GetString(
Convert.FromBase64String(base64EncryptedString)));
// Create a decryptor to perform the stream transform.
var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(encryptedBytes))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
return srDecrypt.ReadToEnd();
}
}
}
}
}
public static string EncryptAES256CBCBase64x2(string baseString, string secretKey, string secretIV)
{
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var ivBytes = Encoding.UTF8.GetBytes(secretIV);
var hash = SHA256.Create();
var keyHash = hash.ComputeHash(keyBytes);
Array.Resize(ref keyHash, 32);
var keyHashString = string.Empty;
foreach (byte x in keyHash)
keyHashString += string.Format("{0:x2}", x);
keyHash = Encoding.UTF8.GetBytes(keyHashString.Substring(0, 32));
var ivHash = hash.ComputeHash(ivBytes);
Array.Resize(ref ivHash, 16);
var ivHashString = string.Empty;
foreach (byte x in ivHash)
ivHashString += string.Format("{0:x2}", x);
ivHash = Encoding.UTF8.GetBytes(ivHashString.Substring(0, 16));
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new RijndaelManaged())
{
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.Mode = CipherMode.CBC;
rijAlg.Key = keyHash;
rijAlg.IV = ivHash;
var encryptedBytes = Encoding.UTF8.GetBytes(baseString);
// Create a encryptor to perform the stream transform.
var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (var msEncrypt = new MemoryStream(encryptedBytes))
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Read))
{
using (var srEncrypt = new StreamReader(csEncrypt))
{
// Read the encrypted bytes from the encrypting stream
// and place them in a string.
var result = srEncrypt.ReadToEnd();
return Convert.ToBase64String(
Encoding.UTF8.GetBytes(
Convert.ToBase64String(
Encoding.UTF8.GetBytes(result))));
}
}
}
}
}
public static string SaveBase64String(string data)
{
data = data.Replace("-", "+").Replace("_", "/");
var mod = data.Length % 4;
if (mod > 2)
mod = 1;
return data + string.Empty.PadRight(mod, '=');
}
}
At the following link an online example is available for you to try: https://repl.it/@ormasoftchile/Test-encrypt-decrypt
Thank you everyone.