Encrypt & Decrypt querystring values using AES 256 - c#

I am using the following code to Encrypt/Decrypt a querystring and pass it from one page to another. The resulting output is missing a '+' (see at the bottom of the question). What can I do to make sure the '+' comes thru as I am already using urlencode/urldecode?
//Encryption page
protected void Page_Load(object sender, EventArgs e)
{
string text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Response.Write("256:" + Decrypt256(Encrypt256(text)));
Response.Write(string.Format("<br/>{0}", HttpUtility.UrlEncode(Encrypt256(text))));
}
private const string AesIV256 = #"!QAZ2WSX#EDC4RFV";
private const string AesKey256 = #"5TGB&YHN7UJM(IK<5TGB&YHN7UJM(IK<";
private string Encrypt256(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes(AesIV256);
aes.Key = Encoding.UTF8.GetBytes(AesKey256);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
/// <summary>
/// AES decryption
/// </summary>
private string Decrypt256(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes(AesIV256);
aes.Key = Encoding.UTF8.GetBytes(AesKey256);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Convert Base64 strings to byte array
byte[] src = System.Convert.FromBase64String(text);
// decryption
using (ICryptoTransform decrypt = aes.CreateDecryptor())
{
byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
return Encoding.Unicode.GetString(dest);
}
}
Decryption page (I use this page to print out what I the decrypted string from the first page and then compare it what I get in the URL:
protected void Page_Load(object sender, EventArgs e)
{
string urlValue = HttpUtility.UrlDecode(Request.QueryString["p"].Trim());
Decrypt256(Encoding.ASCII.GetString(s2));
Response.Write(urlValue + "<br /><br />");
Response.Write("AUwsHc8j/llULnuwVnspNwolBUAhl5GFqC6iOrUN5euZFrOgFVypqTGfhAaooLxa0Fko+9KGtRh3UcQJtzkfSw==");
}
The end result is the following two lines (the first line is the output from the URL). They almost match except the first URL (the encoded/decoded querystring result) is missing the '+' symbol. Any idea how to avoid this?
AUwsHc8j/llULnuwVnspNwolBUAhl5GFqC6iOrUN5euZFrOgFVypqTGfhAaooLxa0Fko 9KGtRh3UcQJtzkfSw==
AUwsHc8j/llULnuwVnspNwolBUAhl5GFqC6iOrUN5euZFrOgFVypqTGfhAaooLxa0Fko+9KGtRh3UcQJtzkfSw==

ok fixed it, simply removed the urldecode method. The decode appears to happen automatically.

Related

System.Text.Encoding.UTF8.GetString() is not converting complete byte array to string

I have to decrypt a string using AES algorithm. Basically I am converting a php code into C#. The implementation in PHP is working fine. Below is the php code for decryption:
function decrypt($data) //decrypt function
{
$authentication_key="NKu84HvQaPRr";
$iv="abcdef987654";
$payload=base64_decode($data);
$decrypt=openssl_decrypt($payload, 'AES-128-CBC', $authentication_key, 1, $iv);
return $decrypt;
}
I am able to get the $payload in my C# code but in a byte array. The byte array generated in C# matches with the byte array of $payload in PHP(Both are of same length and bytes. Screenshot is attached). Now as per the PHP code $payload is passed as a string in openssl_decrypt function, so I am also converting C# byte array to string using
string payLdBffrString = System.Text.Encoding.UTF8.GetString(payLoadBuffer,0,payLoadBuffer.Length).
Length of payLoadBuffer.Length is 1936 which is same as in PHP().
Length of payLdBffrString is 1843 which is not same as in PHP and should be same.().
Below is my C# code of decryption:
static string DecryptAes(string data, byte[] key, byte[] iv)
{
using (var aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Padding = PaddingMode.Zeros;
aes.Key = key;
aes.IV = iv;
byte[] payLoadBuffer = Convert.FromBase64String(data);
string payLdBffrString =System.Text.Encoding.UTF8.GetString(payLoadBuffer,0,payLoadBuffer.Length);
//string payLdBffrString = BytesToString(payLoadBuffer);
//var s = new StringBuilder();
//foreach (byte b in payLoadBuffer)
//{
// s.Append(b.ToString());
//}
int strLen = payLdBffrString.Length;
//byte[] dataBuffer = Encoding.Unicode.GetBytes(data);
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
byte[] decryptedByteArr = PerformCryptography(payLoadBuffer, decryptor);
}
}
}
static byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
{
using (var ms = new MemoryStream())
using (var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return ms.ToArray();
}
}
Now I am stuck in generating the $payload string in C#. I have used StringBuilder and all the encodings available in .net with no success. It is also mentioned that only UTF8 encoding is giving me the desired string but problem here is that I am not getting complete string as I get in PHP code at $payload variable.
I need help regarding generation of complete payload string in C# as I get in PHP so that I can decrypt it in C#
EDIT: I have updated the DecryptAes() function and added PerformCryptography() function which is giving me again byte array. Then how can I convert this byte array to the string again?

Laravel's Encryption and Decryption Using C#

I'm trying to encrypt and decrypt data from and to Laravel using C#
I've tried using this code. I've modified it to work correctly after Laravel's updates.
however, when I try to decrypt the C# string in Laravel I recieve "The MAC is invalid." Exception.
I have no idea about what's the problem with the computation of the MAC on C# side.
Any ideas on how to solve this issue?
C# Code:
using System;
using System.Text;
using System.Security.Cryptography;
using System.Web.Script.Serialization;
using System.Collections.Generic;
namespace Aes256CbcEncrypterApp
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, world!");
// The sample encryption key.
byte[] Key = Convert.FromBase64String("My44CharKeyBase64");
// The sample text to encrypt and decrypt.
string Text = "Here is some text to encrypt!";
// Encrypt and decrypt the sample text via the Aes256CbcEncrypter class.
string Encrypted = Aes256CbcEncrypter.Encrypt(Text, Key);
string Decrypted = Aes256CbcEncrypter.Decrypt(Encrypted, Key);
// Show the encrypted and decrypted data and the key used.
Console.WriteLine("Original: {0}", Text);
Console.WriteLine("Key: {0}", Convert.ToBase64String(Key));
Console.WriteLine("Encrypted: {0}", Encrypted);
Console.WriteLine("Decrypted: {0}", Decrypted);
Console.ReadKey();
}
}
/**
* A class to encrypt and decrypt strings using the cipher AES-256-CBC.
*/
class Aes256CbcEncrypter
{
private static readonly Encoding encoding = Encoding.UTF8;
public static string Encrypt(string plainText, byte[] key)
{
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.GenerateIV();
ICryptoTransform AESEncrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] buffer = encoding.GetBytes(plainText);
string encryptedText = Convert.ToBase64String(AESEncrypt.TransformFinalBlock(buffer, 0, buffer.Length));
String mac = "";
mac = BitConverter.ToString(HmacSHA256(Convert.ToBase64String(aes.IV) + encryptedText, key)).Replace("-", "").ToLower();
var keyValues = new Dictionary<string, object>
{
{ "iv", Convert.ToBase64String(aes.IV) },
{ "value", encryptedText },
{ "mac", mac },
};
JavaScriptSerializer serializer = new JavaScriptSerializer();
return Convert.ToBase64String(encoding.GetBytes(serializer.Serialize(keyValues)));
}
catch (Exception e)
{
throw new Exception("Error encrypting: " + e.Message);
}
}
public static string Decrypt(string plainText, byte[] key)
{
try
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.Key = key;
// Base 64 decode
byte[] base64Decoded = Convert.FromBase64String(plainText);
string base64DecodedStr = encoding.GetString(base64Decoded);
// JSON Decode base64Str
JavaScriptSerializer serializer = new JavaScriptSerializer();
var payload = serializer.Deserialize<Dictionary<string, string>>(base64DecodedStr);
aes.IV = Convert.FromBase64String(payload["iv"]);
ICryptoTransform AESDecrypt = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] buffer = Convert.FromBase64String(payload["value"]);
return encoding.GetString(AESDecrypt.TransformFinalBlock(buffer, 0, buffer.Length));
}
catch (Exception e)
{
throw new Exception("Error decrypting: " + e.Message);
}
}
static byte[] HmacSHA256(String data, byte[] key)
{
using (HMACSHA256 hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(encoding.GetBytes(data));
}
}
}
}
After digging, debugging, looking for any updates Laravel made and compared it to the C# code above, there was not any major deference, however the problem was at Laravel side and solved by entering these commands.
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear
composer dump-autoload
Remember to disable payload serialization while encrypting and decrypting to get the original payload and not a serialized one since serialization is enabled by default.
echo encrypt("test", false);
echo decrypt("base64EncryptedString", false)
In case more exceptions thrown from encrypt() or decrypt() try to open the browser in incognito mode or delete browser cache.

Why is my encryption method not able to handle Base-64 strings do I need to convert it?

I have this AES alogrithm to encrypt and decrypt a string.
It works just fine if I try to decrypt 1 letter / character
but as soon as it overstrides 1 and goes up to maybe 20+ characters it starts throwing me this error.
Additional information: The input is not a valid Base-64 string as it
contains a non-base 64 character, more than two padding characters, or
an illegal character among the padding characters.
I'm new to AES encryption/descryption so I've never encountered this error before. How and what do I need to convert to a Convert.ToBase64String()?
This all happends when I press the decrypt button.
And the line throwing me the error would be thisbyte[] encryptedBytes = Convert.FromBase64String(encrypted);
Here is an example of a encrypted string zg1Y3w6pPSoYW36q8nR7iQ==gDY7P3Kg6jxWM/bn0N+f6Q==x8FfbeOUwSxqYkokfFtY8Q==4iycn+vyY6UN9Y99k4aljg==N//H6vUxhIboleXlETNj9A==V017IsPvKRmJcn8GH9W+Vg==NZNjLcLJZTkyXGhh3E6mNw==hmnbcfZdVXPU04ywXR3nWQ==SU5RemiwkhpqTAai6/OQOw==p/2GA7NdspZDt6G/RPR9yQ==8l83Pv/2ZQLkHE/4iKYyOA==cSJ+0Um4s160QPDn/pueYw==J+Wxf9fR8MyHTg0FzpurvQ==ufke4Nlwkwb2V6PNdMCPtg==NKeRkuObfma9PdktTiy/AA==tv1JTHMelvuUIzGjZmLEdA==CO3qyFFDu20kMhU8MwE42w==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==N//H6vUxhIboleXlETNj9A==
private const string constKey = "qMvdN091nWru1aUG";
private static string IV = "asdfghjkliyt52g6";
private void encryptButton_Click(object sender, RoutedEventArgs e)
{
string encryptthis = Encrypt(texttoencrypt.Text);
encryptedtext.Text = encryptthis;
}
private static string Encrypt(string text)
{
byte[] plaintextBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(text);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Key = System.Text.ASCIIEncoding.ASCII.GetBytes(constKey);
aes.IV = System.Text.ASCIIEncoding.ASCII.GetBytes(IV);
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
ICryptoTransform crypto = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] encrypted = crypto.TransformFinalBlock(plaintextBytes, 0, plaintextBytes.Length);
return Convert.ToBase64String(encrypted);
}
private static string Decrypt(string encrypted)
{
byte[] encryptedBytes = Convert.FromBase64String(encrypted);
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Key = System.Text.ASCIIEncoding.ASCII.GetBytes(constKey);
aes.IV = System.Text.ASCIIEncoding.ASCII.GetBytes(IV);
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
ICryptoTransform crypto = aes.CreateDecryptor(aes.Key, aes.IV);
byte[] secret = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
crypto.Dispose();
return System.Text.ASCIIEncoding.ASCII.GetString(secret);
}

Arithmetic signs in encrypted value cannot decrypt on live server in c#

I am working in a MVC3 Project. Encryption and decryption method are working fine in locally, but when I put on live some values are not hitting. I found that the arithmetic symbols like +,/ cause the problem. How can I resolve this?
My code is given below:
RijndaelManaged aese = new RijndaelManaged();
public string Encrypt(string stringToEncrypt)
{
try
{
aese.Mode = CipherMode.CBC;
aese.Padding = PaddingMode.PKCS7;
aese.BlockSize = 128;
aese.KeySize = 128;
aese.Key = Encoding.UTF8.GetBytes("bsGCslxDSrPTesVG");
aese.IV = Encoding.UTF8.GetBytes("IkLNSuWfZaQdPQCS");
byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);
ICryptoTransform crypto = aese.CreateEncryptor();
byte[] cipherText = crypto.TransformFinalBlock(inputByteArray, 0, inputByteArray.Length);
return (Convert.ToBase64String(cipherText));
}
catch
{
return "Encryption Error";
}
}
public string Decrypt(string stringToDecrypt)
{
byte[] inputByteArray = new byte[stringToDecrypt.Length + 1];
try
{
aese.Mode = CipherMode.CBC;
aese.Padding = PaddingMode.PKCS7;
aese.BlockSize = 128;
aese.KeySize = 128;
aese.Key = Encoding.UTF8.GetBytes("bsGCslxDSrPTesVG");
aese.IV = Encoding.UTF8.GetBytes("IkLNSuWfZaQdPQCS");
inputByteArray = Convert.FromBase64String(stringToDecrypt);
ICryptoTransform decrypto = aese.CreateDecryptor();
byte[] plainText = aese.CreateDecryptor().TransformFinalBlock(inputByteArray, 0, inputByteArray.Length);
return Encoding.UTF8.GetString(plainText);
}
catch
{
return "Dycription Error";
}
}
Given you're working with a web project, I'm going to guess that you're passing Base64-encoded cipher-text as a URL or similar. Because Base64 data contains characters that are interpreted differently when treated as a URL (e.g. a + in a URL is interpreted as space), your data will be corrupted if you include Base64-encoded data in a URL verbatim.
If you intend to pass Base64 data in this way, you must correctly URL-encode it (e.g. using System.Web.HttpUtility.UrlEncode()) before including it in the URL.
There is a URL-safe version of Base64, which uses - (minus) for value 62 and _ (underline) for value 63. See RFC 4648, section 5 for details.

AES algorithm value differences between .NET and nodejs, CryptoJS

The below AES algorithm in C# returns an encrypted value that is different from what node js and CryptoJS returns. NodeJS Crypto library and CryptoJS return the same values but .NET's AesCryptoServiceProvider returns a different value. Any ideas?
C# example
private const string AesIV = #"!QAZ2WSX#EDC4RFV";
private const string AesKey = #"5TGB&YHN7UJM(IK<";
public static void Main()
{
try
{
string original = "HelloWorld";
Console.WriteLine(Encrypt(original));
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
/// <summary>
/// AES Encryption
/// </summary>
private static string Encrypt(string text)
{
// AesCryptoServiceProvider
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.IV = Encoding.UTF8.GetBytes(AesIV);
aes.Key = Encoding.UTF8.GetBytes(AesKey);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
NodeJS example:
crypto = require "crypto"
algo = 'aes-128-cbc'
keyBuffer = new Buffer("!QAZ2WSX#EDC4RFV")
ivBuffer = new Buffer("5TGB&YHN7UJM(IK<")
cipher = crypto.createCipheriv(algo, keyBuffer, ivBuffer)
textBuffer = new Buffer('HelloWorld')
encrypted = cipher.update(textBuffer)
encryptedFinal = cipher.final()
encryptedText = encrypted.toString('base64') + encryptedFinal.toString('base64')
console.log encryptedText
CryptoJS example:
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Utf8.parse('!QAZ2WSX#EDC4RFV');
var iv = CryptoJS.enc.Utf8.parse('5TGB&YHN7UJM(IK<');
var encrypted = CryptoJS.AES.encrypt("HelloWorld", key, { iv: iv });
alert(encrypted);
</script>
Your C# version uses UTF-16LE to convert HelloWorld to plaintext bytes. The NodeJS one (and presumably hence the CryptoJS one) use UTF-8 bytes. Use Encoding.UTF8.GetBytes() in the C# one.
Default padding in NodeJS is PKCS5 (assuming CryptoJS is the same since you said they produce the same result). Your C# code is setting padding to PKCS7.

Categories

Resources