How to decrypt AES CBC? - c#

I used this link to test decrypting AES CBC.
Here my parameters:
This work. Then I implement with C#. My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace HHT.Module.eWallet.Helper
{
public class AESHelper
{
private AesCryptoServiceProvider _aes;
private ICryptoTransform _crypto;
public AESHelper(string key, string IV)
{
_aes = new AesCryptoServiceProvider();
_aes.BlockSize = 128;
_aes.KeySize = 256;
_aes.Key = ASCIIEncoding.ASCII.GetBytes(key);
if (!string.IsNullOrEmpty(IV)) {
_aes.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
_aes.Padding = PaddingMode.PKCS7;
_aes.Mode = CipherMode.CBC;
}
public string encrypt(string message)
{
_crypto = _aes.CreateEncryptor(_aes.Key, _aes.IV);
byte[] encrypted = _crypto.TransformFinalBlock(
ASCIIEncoding.ASCII.GetBytes(message), 0, ASCIIEncoding.ASCII.GetBytes(message).Length);
_crypto.Dispose();
return System.Convert.ToBase64String(encrypted);
}
public string decrypt(string message)
{
_crypto = _aes.CreateDecryptor(_aes.Key, _aes.IV);
byte[] decrypted = _crypto.TransformFinalBlock(
System.Convert.FromBase64String(message), 0, System.Convert.FromBase64String(message).Length);
_crypto.Dispose();
return ASCIIEncoding.ASCII.GetString(decrypted);
}
}
}
The function decrypt not work, it return wrong string. I think some parameters is not same as the picture.
Base64-encoded encrypted data:
5qG6aB4UZ6bnbbPgFhnC+qp/FJ7ZuZ+fg1cYm+OUM1uP/6PyWfLg0w5bJstmBf2W
Key:
745d88b1e8f75d320c2bd9198c08485d

You need to initialize the IV to all zeroes because otherwise it starts with a random value:
if (!string.IsNullOrEmpty(IV))
{
_aes.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
else
{
_aes.IV = new byte[_aes.BlockSize / 8];
}
With this fix in place, it gets the same result as the online site you used.
Also note that your question says ECB but your code and screenshot use CBC (and it's AES, not ACS).

Related

Cannot decrypt data .net 6 with bare bones implementation keep getting The input data is not a complete block

There are lots of topics on this but none of them seem to solve the problem.
So here is the code I am using, ive gone to a bare bones implementation which should work:
public class CustomEncryptor : IEncryptor
{
public string Key { get; } // 32 bytes i.e 6GUNtgba8Rqgm5tc9JGhzxjN6XbzuyNM
public CustomEncryptor(string key)
{
Key = key;
}
public byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(Key);
aes.IV = RandomNumberGenerator.GetBytes(16);
aes.Padding = PaddingMode.PKCS7;
return aes.EncryptCbc(data, aes.IV);
}
public byte[] Decrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(Key);
aes.IV = RandomNumberGenerator.GetBytes(16);
aes.Padding = PaddingMode.PKCS7;
return aes.DecryptCbc(data, aes.IV);
}
}
It encrypts the data fine, but when I come to decrypt it I always get:
System.Security.Cryptography.CryptographicException: The input data is
not a complete block.
Given I am using the aes built in methods and im providing a key of 32 bytes, then generating 16 bytes for the initialisation vector it should all be fine right, ive tried just giving it a blank new byte[16] instead and that doesn't help, I have also tried different padding modes and nothing works.
To give some context the app that uses this has an extension method it uses to simplify string scenarios, the use cases are often user does something, it encrypts it and saves it somewhere, then when the app is reloaded it decrypts it and puts it in a var for usage.
public static string Encrypt(this IEncryptor encryptor, string textToEncrypt)
{
var textAsBytes = Encoding.UTF8.GetBytes(textToEncrypt);
var encryptedData = encryptor.Encrypt(textAsBytes);
return Convert.ToBase64String(encryptedData);
}
public static string Decrypt(this IEncryptor encryptor, string encryptedText)
{
var textAsBytes = Convert.FromBase64String(encryptedText);
var decryptedData = encryptor.Decrypt(textAsBytes);
return Encoding.UTF8.GetString(decryptedData);
}
I am baffled as to why it doesnt work as lots of examples online do same sort of thing.
== Update 1 ==
It was mentioned in comments that the key & iv need to be consistent so I have tried setting that to explicitly be the same thing like so:
public class CustomEncryptor : IEncryptor
{
public byte[] Key { get; }
public byte[] IV { get; }
public CustomEncryptor(byte[] key, byte[] iv)
{
IV = iv;
Key = key;
}
public byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Key;
return aes.EncryptCbc(data, IV);
}
public byte[] Decrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Key;
return aes.DecryptCbc(data, IV);
}
}
// Example usage
var key = Encoding.UTF8.GetBytes("UxRBN8hfjzTG86d6SkSSNzyUhERGu5Zj"); // 32 bytes
var iv = Encoding.UTF8.GetBytes("7cA8jkRMJGZ8iMeJ"); // 16 bytes
services.AddSingleton<IEncryptor>(new CustomEncryptor(key, iv));
Still get same error

Decrypting AES string not working as expected

I am trying to decrypt a simple AES string BqvGk+lyQ+pyhSqwV3MfRg== (which translates as Hello World) upon user input with a hardcoded key but I get an error. Could the be an issue when it's trying to read the base64 string from terminal? Not sure how to fix that.
at EncryptionDecryptionUsingSymmetricKey.AesOperation.DecryptString(String key) in \\visualstudio\AES_Decryptor\AES_Decryptor\Program.cs:line 32
at EncryptionDecryptionUsingSymmetricKey.AesOperation.Main(String[] args) in \\visualstudio\AES_Decryptor\AES_Decryptor\Program.cs:line 21
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace EncryptionDecryptionUsingSymmetricKey
{
public class AesOperation
{
static void Main(string[] args)
{
var key = "b14ca5898a4e4133bbce2ea2315a1916";
Console.WriteLine("[+] Decrypt: ");
var str = Console.ReadLine();
var decryptedString = AesOperation.DecryptString(key);
Console.WriteLine($"[+] Original payload: {decryptedString}");
}
private static object DecryptString(string key)
{
throw new NotImplementedException();
}
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
}
This method is working. When I run:
void Main()
{
var key = "b14ca5898a4e4133bbce2ea2315a1916";
var decryptedString = DecryptString(key, "BqvGk+lyQ+pyhSqwV3MfRg==");
Console.WriteLine($"[+] Original payload: {decryptedString}");
}
The result is:
[+] Original payload: Hello World!
I am using your own method as well:
public static string DecryptString(string key, string cipherText)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
SO I think you need to either provide more of the error message if there is any, or we need more of the code. Cause your key decrypts the AES string just fine with your own provided method.
If I had to guess the issue is 1 of 2 things.
var str = Console.ReadLine();
I don't see you reference str anywhere. also it is going to read the entire console line, which might give you more than the aes string. Also you might just be calling your throw exception method. I am not sure why that is there. Perhaps try getting rid of that and running code. I am referring to this method.
private static object DecryptString(string key)
{
throw new NotImplementedException();
}

PHP Des Encryption from CSharp

I'm porting the Following C# Encryption logic to php, i tried using OPENSSL to port the encryption but its giving me a weird error, i have no idea what im doing wrong maybe OPENSSL fault, padding? not sure
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.IO;
namespace Rextester
{
public class Program
{
public static ICryptoTransform DES;
public static DESCryptoServiceProvider Crypto = new DESCryptoServiceProvider();
public static MemoryStream memStream = new MemoryStream();
public static string encyrptedMessage;
public static void Main(string[] args)
{
Crypto.BlockSize = 64;
Crypto.FeedbackSize = 8;
Crypto.Mode = CipherMode.ECB;
Crypto.Padding = PaddingMode.PKCS7;
//Your code goes here
DES = Crypto.CreateEncryptor(Encoding.ASCII.GetBytes("L)#!&8#*"), Encoding.UTF8.GetBytes("CodeFast"));
CryptoStreamMode mode = CryptoStreamMode.Write;
// Set up streams and encrypt
byte[] messageBytes = Encoding.ASCII.GetBytes("yes");
CryptoStream cryptoStream = new CryptoStream(memStream, DES, mode);
cryptoStream.Write(messageBytes, 0, messageBytes.Length);
cryptoStream.FlushFinalBlock();
// Read the encrypted message from the memory stream
byte[] encryptedMessageBytes = new byte[memStream.Length];
memStream.Position = 0;
memStream.Read(encryptedMessageBytes, 0, encryptedMessageBytes.Length);
// Encode the encrypted message as base64 string
string encryptedMessage = Convert.ToBase64String(encryptedMessageBytes);
Console.WriteLine(encryptedMessage);
}
}
}
<?php
namespace encryption;
class DESCrypto{
private $key;
private $iv;
public function __construct(string $key, ?string $iv){
$this->key = $key;
$this->iv = utf8_encode($iv);
}
public function encrypt(string $text): string {
return openssl_encrypt($text,"DES-ECB",$this->key,OPENSSL_RAW_DATA,$this->iv);
}
public function decrypt(string $text): string {
return openssl_decrypt($text,"DES-ECB",$this->key,OPENSSL_RAW_DATA,$this->iv);
}
}
the C# Code is working fine meanwhile the php one returning the following error
Warning: openssl_decrypt(): IV passed is 4 bytes long which is longer than the 0 expected by selected cipher
Warning: openssl_encrypt(): IV passed is 4 bytes long which is longer than the 0 expected by selected cipher

Encode and Decode Passwords in WPF Applications

I have a WPF applications that contains a user form. I have a password field I need to decript it, I just have an encrypt method for this:
public static string Encode(string value)
{
var hash = System.Security.Cryptography.SHA1.Create();
var encoder = new System.Text.ASCIIEncoding();
var combined = encoder.GetBytes(value ?? "");
return BitConverter.ToString(hash.ComputeHash(combined)).ToLower().Replace("-", "");
}
How to create a Decode method for this?
The key in this situation is that you don't want to decrypt the password. What you want to do is to encrypt the password entered by the user using the same function and comparing the result to what is stored in the database for the user's account.
Hashing is the right thing to do, but you should also hash the value that you are storing in the database and compare the two hashes, you should not, if at all possible, be storing passwords, even if they are encrypted.
You should store the one-way hash in the database instead of a reversible encryption. What the user's actual password happens to be is not your concern, only that the hash of the user supplied password when salted matches the hash in your database. You might, however, encrypt it for transport to be decrypted at the server and then hashed for storage in the database.
Retrieve salt and hashed password from database.
Salt candidate password and hash it.
Compare values.
SHA1 is a hash function which means it is basically one-way function, you cannot get the original content back.
you should use cryptographic functions if you need to get the original value back.
there basically two classes of encryption algorithms - symmetric and asymmetric cryptographic functions.
symetric is for example AES
asymetric would be Public/private key encrytion such as RSA
take a look at this sample: http://www.codeproject.com/Articles/10877/Public-Key-RSA-Encryption-in-C-NET
You can't decrypt SHA1 hash because it's a one way hash. Hashing is not a reversible operation.
Below a helper class that both encrypt and decrypte a string
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace EncryptStringSample
{
public static class StringCipher
{
// This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
// This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be
// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes("tu89geji340t89u2");
// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
public static string Encrypt(string plainText, string passPhrase)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
using(PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using(RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using(ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes))
{
using(MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using(CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
}
}
The above class can used quite simply with code similar to the following:
using System;
using System.Linq;
namespace EncryptStringSample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter a password to use:");
string password = Console.ReadLine();
Console.WriteLine("Please enter a string to encrypt:");
string plaintext = Console.ReadLine();
Console.WriteLine("");
Console.WriteLine("Your encrypted string is:");
string encryptedstring = StringCipher.Encrypt(plaintext, password);
Console.WriteLine(encryptedstring);
Console.WriteLine("");
Console.WriteLine("Your decrypted string is:");
string decryptedstring = StringCipher.Decrypt(encryptedstring, password);
Console.WriteLine(decryptedstring);
Console.WriteLine("");
Console.ReadLine();
}
}
}
Here's another way to decrypt and encrypt using RSA
Replace your_rsa_key with your RSA key.
var provider = new System.Security.Cryptography.RSACryptoServiceProvider();
provider.ImportParameters(your_rsa_key);
var encryptedBytes = provider.Encrypt(
System.Text.Encoding.UTF8.GetBytes("Hello World!"), true);
string decryptedTest = System.Text.Encoding.UTF8.GetString(
provider.Decrypt(encryptedBytes, true));
Here's a good link on how RSA works with a clear exmaple

query string parameter obfuscation

I want to obfuscate one query string parameter in ASP.NET. The site will have a high volume of request, so the algorithm shouldn't be too slow.
My problem is that all the algorithms I found result in unwanted characters (like +/=)
Here is an example of what i want to achieve:
www.domain.com/?id=1844
to
www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ
The obfuscated param should only include a-z and A-Z and 0-9 characters.
I know I can encrypt using base64, but this will generate unwanted characters such as / or = or +.
Any idea what algorithm can be used?
Update:
I'm aware of UrlEncoding , i want to avoid encoding the string.
because that will generate charaters like %F2 or %B2 in the url.
You can use triple DES to encode the value using a narow block cipher.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApplication1 {
class Program {
static string ToHex(byte[] value) {
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
sb.AppendFormat("{0:x2}", b);
return sb.ToString();
}
static string Encode(long value, byte[] key) {
byte[] InputBuffer = new byte[8];
byte[] OutputBuffer;
unsafe {
fixed (byte* pInputBuffer = InputBuffer) {
((long*)pInputBuffer)[0] = value;
}
}
TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
TDes.Mode = CipherMode.ECB;
TDes.Padding = PaddingMode.None;
TDes.Key = key;
using (ICryptoTransform Encryptor = TDes.CreateEncryptor()) {
OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 8);
}
TDes.Clear();
return ToHex(OutputBuffer);
}
static long Decode(string value, byte[] key) {
byte[] InputBuffer = new byte[8];
byte[] OutputBuffer;
for (int i = 0; i < 8; i++) {
InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
}
TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
TDes.Mode = CipherMode.ECB;
TDes.Padding = PaddingMode.None;
TDes.Key = key;
using (ICryptoTransform Decryptor = TDes.CreateDecryptor()) {
OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 8);
}
TDes.Clear();
unsafe {
fixed (byte* pOutputBuffer = OutputBuffer) {
return ((long*)pOutputBuffer)[0];
}
}
}
static void Main(string[] args) {
long NumberToEncode = (new Random()).Next();
Console.WriteLine("Number to encode = {0}.", NumberToEncode);
byte[] Key = new byte[24];
(new RNGCryptoServiceProvider()).GetBytes(Key);
Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
string EncodedValue = Encode(NumberToEncode, Key);
Console.WriteLine("The encoded value is {0}.", EncodedValue);
long DecodedValue = Decode(EncodedValue, Key);
Console.WriteLine("The decoded result is {0}.", DecodedValue);
}
}
}
The output should be something like this:
Number to encode = 873435734.
Key to encode with is 38137b6a7aa49cc6040c4297064fdb4461c79a895f40b4d1.
The encoded value is 43ba3fb809a47b2f.
The decoded result is 873435734.
Note that the encoded value is only 16 characters wide.
If you're really conserned about abuse, then AES can be used in a similar manner. In the next example I switch in AES and write the 64 bit id number into both sides of the block. If it doesn't decode with the same value on both sides then it is rejected. This can prevent people from writing in random numbers.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace ConsoleApplication1 {
class Program {
static string ToHex(byte[] value) {
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
sb.AppendFormat("{0:x2}", b);
return sb.ToString();
}
static string Encode(long value, byte[] key) {
byte[] InputBuffer = new byte[16];
byte[] OutputBuffer;
unsafe {
fixed (byte* pInputBuffer = InputBuffer) {
((long*)pInputBuffer)[0] = value;
((long*)pInputBuffer)[1] = value;
}
}
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Aes.Key = key;
using (ICryptoTransform Encryptor = Aes.CreateEncryptor()) {
OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 16);
}
Aes.Clear();
return ToHex(OutputBuffer);
}
static bool TryDecode(string value, byte[] key, out long result) {
byte[] InputBuffer = new byte[16];
byte[] OutputBuffer;
for (int i = 0; i < 16; i++) {
InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
}
AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
Aes.Mode = CipherMode.ECB;
Aes.Padding = PaddingMode.None;
Aes.Key = key;
using (ICryptoTransform Decryptor = Aes.CreateDecryptor()) {
OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 16);
}
Aes.Clear();
unsafe {
fixed (byte* pOutputBuffer = OutputBuffer) {
//return ((long*)pOutputBuffer)[0];
if (((long*)pOutputBuffer)[0] == ((long*)pOutputBuffer)[1]) {
result = ((long*)pOutputBuffer)[0];
return true;
}
else {
result = 0;
return false;
}
}
}
}
static void Main(string[] args) {
long NumberToEncode = (new Random()).Next();
Console.WriteLine("Number to encode = {0}.", NumberToEncode);
byte[] Key = new byte[24];
(new RNGCryptoServiceProvider()).GetBytes(Key);
Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
string EncodedValue = Encode(NumberToEncode, Key);
Console.WriteLine("The encoded value is {0}.", EncodedValue);
long DecodedValue;
bool Success = TryDecode(EncodedValue, Key, out DecodedValue);
if (Success) {
Console.WriteLine("Successfully decoded the encoded value.");
Console.WriteLine("The decoded result is {0}.", DecodedValue);
}
else
Console.WriteLine("Failed to decode encoded value. Invalid result.");
}
}
}
The result should now look something like this:
Number to encode = 1795789891.
Key to encode with is 6c90323644c841a00d40d4407e23dbb2ab56530e1a4bae43.
The encoded value is 731fceec2af2fcc2790883f2b79e9a01.
Successfully decoded the encoded value.
The decoded result is 1795789891.
Also note that since we have now used a wider block cipher the encoded value is now 32 characters wide.
You can use HttpServerUtility.UrlTokenEncode and HttpServerUtility.UrlTokenDecode
Encode uses base64 encoding, but replaces URL unfriendly characters.
There's a similar answer in a previous SO question. See the accepted answer.
So here's a working example that I put together from a few different examples that takes an integer ID and converts it to a hexidecimal formatted encrypted string. This encrypted string should not include URL-unfriendly characters and will not include escaped characters either.
Here's the entire working console app. Please note that it's a prototype and definitely not for production -- this just illustrates a solution and definitely needs to be refactored.
When you run the code, your output should be this:
1234 get encrypted as ZaB5GE/bWMJcNaeY/xJ6PQ==
ZaB5GE/bWMJcNaeY/xJ6PQ== encrypted is this in hex 5a61423547452f62574d4a634e6165592f784a3650513d3d
5a61423547452f62574d4a634e6165592f784a3650513d3d gets dehexed as ZaB5GE/bWMJcNaeY/xJ6PQ==
ZaB5GE/bWMJcNaeY/xJ6PQ== got decrypted as 1234
Sources:
byte to hex article on SO: Encryption to alphanumeric in System.Security.Cryptography
Crypto helper class: Encrypt and decrypt a string (4th answer)
Program2.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace ConsoleApplication1
{
class Program2
{
static void Main(string[] args)
{
int theId = 1234; //the ID that's being manipulated
byte[] byteArray; //the byte array that stores
//convert the ID to an encrypted string using a Crypto helper class
string encryptedString = Crypto.EncryptStringAES(theId.ToString(), "mysecret");
Console.WriteLine("{0} get encrypted as {1}", theId.ToString(), encryptedString);
//convert the encrypted string to byte array
byteArray = ASCIIEncoding.Default.GetBytes(encryptedString);
StringBuilder result = new StringBuilder();
//convert each byte to hex and append to a stringbuilder
foreach (byte outputByte in byteArray)
{
result.Append(outputByte.ToString("x2"));
}
Console.WriteLine("{0} encrypted is this in hex {1}", encryptedString, result.ToString());
//now reverse the process, and start with converting each char in string to byte
int stringLength = result.Length;
byte[] bytes = new byte[stringLength / 2];
for (int i = 0; i < stringLength; i += 2)
{
bytes[i / 2] = System.Convert.ToByte(result.ToString().Substring(i, 2), 16);
}
//convert the byte array to de-"hexed" string
string dehexedString = ASCIIEncoding.Default.GetString(bytes);
Console.WriteLine("{0} gets dehexed as {1}", result, dehexedString);
//decrypt the de-"hexed" string using Crypto helper class
string decryptedString = Crypto.DecryptStringAES(dehexedString, "mysecret");
Console.WriteLine("{0} got decrypted as {1}", dehexedString, decryptedString);
Console.ReadLine();
}
}
public class Crypto
{
private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5");
/// <summary>
/// Encrypt the given string using AES. The string can be decrypted using
/// DecryptStringAES(). The sharedSecret parameters must match.
/// </summary>
/// <param name="plainText">The text to encrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for encryption.</param>
public static string EncryptStringAES(string plainText, string sharedSecret)
{
if (string.IsNullOrEmpty(plainText))
throw new ArgumentNullException("plainText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
string outStr = null; // Encrypted string to return
RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data.
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
outStr = Convert.ToBase64String(msEncrypt.ToArray());
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return outStr;
}
/// <summary>
/// Decrypt the given string. Assumes the string was encrypted using
/// EncryptStringAES(), using an identical sharedSecret.
/// </summary>
/// <param name="cipherText">The text to decrypt.</param>
/// <param name="sharedSecret">A password used to generate a key for decryption.</param>
public static string DecryptStringAES(string cipherText, string sharedSecret)
{
if (string.IsNullOrEmpty(cipherText))
throw new ArgumentNullException("cipherText");
if (string.IsNullOrEmpty(sharedSecret))
throw new ArgumentNullException("sharedSecret");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// generate the key from the shared secret and the salt
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
byte[] bytes = Convert.FromBase64String(cipherText);
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
}
}
The problem with obfuscating the id, is that you need a way to de-obfuscicate. This requires either:
Fullblown encryption, which if it's any good will require a pretty large value.
Storing the value along with the id number, so it becomes an alternative identifier.
Something that depends on security-by-obscurity.
Alternatively, keep the id clear, but use a check as well.
public static String ChkSumStr(int id, int reduce)
{
return string.Concat(ReduceStrength(ChkSum(id), reduce).Select(b => b.ToString("X2")).ToArray());
}
public static byte[] ChkSum(int id)
{
byte[] idBytes = Encoding.UTF8.GetBytes("This is an arbitrary salt" + id);
return SHA256.Create().ComputeHash(idBytes);
}
private static byte[] ReduceStrength(byte[] src, int reduce)
{
byte[] ret = null;
for(int i = 0; i != reduce; ++i)
{
ret = new byte[src.Length / 2];
for(int j = 0; j != ret.Length; ++j)
{
ret[j] = (byte)(src[j * 2] ^ src[j * 2 + 1]);
}
src = ret;
}
return src;
}
The higher the value given for reduce, the smaller the result (until at 6 it keeps producing the empty string). A low value (or 0) gives better security, at the cost of a longer URI.
The string "This is an arbitrary salt" needs to be secret for best security. It can be hardcoded in some uses, but would want to be obtained from a secure source for others.
With the above, an id of 15 and a reduce of 3 produces a result of 05469B1E. We can then use this as:
www.domain.com/?id=15&chk=05469B1E
In the handler that would look up whatever 15 is, we do the same thing again, and if the result is different to 05469B1E we can either return a 403 Forbidden or arguably more reasonable 404 Not Found (on the basis that we've received a URI that as a whole doesn't identify anything).
Have you tried URL encoding your query string text? It's part of the HttpUtility class which:
Provides methods for encoding and
decoding URLs when processing Web
requests.
and should allow you to pass your base64 encoded text in the query string.
Do your encryption and then use HttpServerUtility.UrlTokenEncode() to encode the byte array.

Categories