Encrypt Query String including keys - c#

I have an app that is using query string to pass some values around pages. I found few examples on how to encrypt values in query string, but the problem is that my KEYS tell more about query string then the values which are all integers converted to string.
Is there a way to encrypt the whole query string in ASP.NET including keys and key values?
Something like:
Default.aspx?value1=40&value2=30&value3=20
to
Default.aspx?56sdf78fgh90sdf4564k34klog5646l
Thanks!

There is one issue that many of the references above overlook, and that is just prior to returning the encrypted string, URL Encode (see below right before the string is returned). I am using IIS 7.5, and it will automatically "Decode" the string for you, so the decryption "should" be OK. Both the Encrypt and Decrypt code is shown below.
public string EncryptQueryString(string inputText, string key, string salt)
{
byte[] plainText = Encoding.UTF8.GetBytes(inputText);
using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
{
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(key), Encoding.ASCII.GetBytes(salt));
using (ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainText, 0, plainText.Length);
cryptoStream.FlushFinalBlock();
string base64 = Convert.ToBase64String(memoryStream.ToArray());
// Generate a string that won't get screwed up when passed as a query string.
string urlEncoded = HttpUtility.UrlEncode(base64);
return urlEncoded;
}
}
}
}
}
public string DecryptQueryString(string inputText, string key, string salt)
{
byte[] encryptedData = Convert.FromBase64String(inputText);
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(key), Encoding.ASCII.GetBytes(salt));
using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
{
using (ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
{
using (MemoryStream memoryStream = new MemoryStream(encryptedData))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainText = new byte[encryptedData.Length];
cryptoStream.Read(plainText, 0, plainText.Length);
string utf8 = Encoding.UTF8.GetString(plainText);
return utf8;
}
}
}
}
}

There are many examples on web.
some of them:
How can I encrypt a querystring in asp.net?
how to pass encrypted query string in asp.net
http://www.codeproject.com/Articles/33350/Encrypting-Query-Strings
http://www.keyvan.ms/how-to-encrypt-query-string-parameters-in-asp-net
http://forums.asp.net/t/989552.aspx/1
Now you say that you do like to encrypt the keys also, actually what you have to do is to encrypt them all url line, and then you just read the RawUrl what after the ? and decrypt it.

Related

Getting Error while decrypting string

I am getting an error while decrypting and encrypted string:
5duOH+Tlg5deIrWZiHoNaQ==wVxXSl9pFu6A8h14/nLdyBkDzO4xmec7PQ0cB7MHjCDqhSRum3C7I1OfqL1rEWbNonU/ayCaJS18zV7ivQQU7A==MBJzKMrrrbmc2/vBZSPDkQ==I09Kj25UO+LcmRzgoqTT2g==+Fkm9VCGplEK6eEyHyEtuEodKSbckC07M2FShu2ukCg=
Error is as follows:
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.
My encryption code is:
public string EncryptQueryString(string inputText, string key, string salt)
{
byte[] plainText = Encoding.UTF8.GetBytes(inputText);
using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
{
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(key), Encoding.ASCII.GetBytes(salt));
using (ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainText, 0, plainText.Length);
cryptoStream.FlushFinalBlock();
string base64 = Convert.ToBase64String(memoryStream.ToArray());
// Generate a string that won't get screwed up when passed as a query string.
string urlEncoded = HttpUtility.UrlEncode(base64);
return urlEncoded;
}
}
}
Decryption:
public string DecryptQueryString(string inputText, string key, string salt)
{
byte[] encryptedData = Convert.FromBase64String(inputText);
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(Encoding.ASCII.GetBytes(key), Encoding.ASCII.GetBytes(salt));
using (RijndaelManaged rijndaelCipher = new RijndaelManaged())
using (ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
using (MemoryStream memoryStream = new MemoryStream(encryptedData))
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainText = new byte[encryptedData.Length];
cryptoStream.Read(plainText, 0, plainText.Length);
string utf8 = Encoding.UTF8.GetString(plainText);
return utf8;
}
}
Base64 padding consists of = or ==, and so it looks like multiple Base64 strings where appended together somehow. You'll have to find where they all originally ended, split them there, and try again.
Note that Base64 strings do not always have padding, only when it is needed, so there may even need to be breaks in places that you cannot see here.

double escape sequence and RijndaelManaged cryptography in C#

I want to pass an id to QueryString of my webpage, as I don't want visitors to see the number, I encode it using RijndaelManaged algorithm, My issue is, this encryption sometimes add a character for example '+' which causes double escape sequence error.
I was wondering if there is way to exclude some characters from encryption output.
My encryption code is as below:
public static string Encrypt(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
byte[] cipherTextBytes;
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memoryStream.ToArray();
cryptoStream.Close();
}
memoryStream.Close();
}
return Convert.ToBase64String(cipherTextBytes);
}
You could convert your bytearray to a Hex string instead of a base64 string. Hex will only contain [a-f0-9]
See this question for details.
But as for your original Problem: you should really use URL encoding for your query strings, which will solve the Problem of the + character.

C# CryptographicException length of the data to decrypt is invalid

I have this code which is meant to decrypt a file, but if I run it, it throws a CryptographicException (length of the data to decrypt is invalid) at the end of the using statement using (CryptoStream ...) { ... }
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] cipherTextBytes;
using (StreamReader reader = new StreamReader(path)) cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
plainTextBytes = new byte[Encoding.UTF8.GetByteCount((new StreamReader(cryptoStream)).ReadToEnd())];
cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
//plainTextBytes = memoryStream.ToArray();
cryptoStream.FlushFinalBlock();
}
}
string result = Encoding.ASCII.GetString(plainTextBytes, 0, plainTextBytes.Length).TrimEnd("\0".ToCharArray());
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(Encoding.ASCII.GetBytes(result), 0, Encoding.ASCII.GetBytes(result).Length);
MessageBox.Show("Decrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while decrypting the file:\n\n" + ex, "Error");
}
}
}
Does anybody know why this is or how I can fix it? (I don't know if it comes from my encrypting method, but I have another program which uses the exact same thing to encrypt strings and that one does work.)
My encrypting method:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
try
{
byte[] TextBytes;
using (StreamReader reader = new StreamReader(path)) TextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
}
using (FileStream writer = new FileStream(path, FileMode.Create)) writer.Write(CipherTextBytes, 0, CipherTextBytes.Length);
MessageBox.Show("Encrypt succesfull");
}
catch (Exception ex)
{
MessageBox.Show("An error while encrypting the file:\n\n" + ex, "Error");
}
}
There are a few issues with your code:
You use a padding mode of Zeroes in Encrypt and None in Decrypt. These need to match
You load the bytes from your file using Encoding.UTF8, you need to read the raw bytes, you can do this by using the following instead:
byte[] cipherTextBytes = File.ReadAllBytes(path);
You call cryptoStream.FlushFinalBlock(); when only using a single iteration of a stream. You don't need this call in Decrypt if you are only doing a single block iteration.
You read the original text from your file in UTF8 and then write it back as ASCII. You should either change the result assignment in decrypt to use UTF8 or (preferably) change both to use raw bytes.
You use Create to interact with the files when you are overwriting in-place. If you know the file already exists (as you are replacing it) you should use truncate or better yet just call File.WriteAllBytes.
Your decrypt is all kinds of messed up. It looks like you're tying yourself into knots over byte retrieval. You should just use the raw bytes out of the CryptoStream and not try using UTF8
Here's a revised set of methods for you:
public static void DecryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] cipherTextBytes = File.ReadAllBytes(path);
byte[] keyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] plainTextBytes;
const int chunkSize = 64;
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
using (MemoryStream dataOut = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var decryptedData = new BinaryReader(cryptoStream))
{
byte[] buffer = new byte[chunkSize];
int count;
while ((count = decryptedData.Read(buffer, 0, buffer.Length)) != 0)
dataOut.Write(buffer, 0, count);
plainTextBytes = dataOut.ToArray();
}
File.WriteAllBytes(path, plainTextBytes);
}
and:
public static void EncryptFile(string path, string key, string saltkey, string ivkey)
{
byte[] TextBytes = File.ReadAllBytes(path);
byte[] KeyBytes = new Rfc2898DeriveBytes(key, Encoding.ASCII.GetBytes(saltkey)).GetBytes(256 / 8);
RijndaelManaged symmetricKey = new RijndaelManaged() { Mode = CipherMode.CFB, Padding = PaddingMode.PKCS7 };
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(KeyBytes, Encoding.ASCII.GetBytes(ivkey));
byte[] CipherTextBytes;
using (MemoryStream ms = new MemoryStream())
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(TextBytes, 0, TextBytes.Length);
cs.FlushFinalBlock();
CipherTextBytes = ms.ToArray();
}
File.WriteAllBytes(path, CipherTextBytes);
}
Most likely your problem comes from cipherTextBytes = Encoding.UTF8.GetBytes(reader.ReadToEnd());
You can't use UTF8 to encode arbitrary binary data, you will likely need to fix both your encrypting end decrypting end. You either must use cipherTextBytes = File.ReadAllBytes(path) or if you are forced to use strings you must first encode the bytes to a valid string using Convert.ToBase64String()
In my case it happened because I was decrypting a value which was never encrypted.
I had my values saved in the database without encryption. But when I introduced encryption and decryption routine in my code and executed my program first time, it was actually trying to decrypt a value which was never encrypted, hence the problem.
Simply clearing the existing values from the database for the initial run solved the problem. If you don't want to lose data even during the first run then you should write a separate routine to encrypt the existing values.

How is encryption done without encoding the string to byte[]?

I've been trying to understand encryption/decryption code of TripleDES for some days. And I have seen many codes in the google, and the code shown below is one of them.
static void Main(string[] args)
{
string original = "Here is some data to encrypt!";
TripleDESCryptoServiceProvider myTripleDES = new TripleDESCryptoServiceProvider();
byte[] encrypted = EncryptStringToBytes(original, myTripleDES.Key, myTripleDES.IV);
string encrypt = Convert.ToBase64String(encrypted);
string roundtrip = DecryptStringFromBytes(encrypted, myTripleDES.Key, myTripleDES.IV);
Console.WriteLine("encryted: {0}", encrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (TripleDESCryptoServiceProvider tdsAlg = new TripleDESCryptoServiceProvider())
{
tdsAlg.Key = Key;
tdsAlg.IV = IV;
ICryptoTransform encryptor = tdsAlg.CreateEncryptor(tdsAlg.Key, tdsAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
string plaintext = null;
using (TripleDESCryptoServiceProvider tdsAlg = new TripleDESCryptoServiceProvider())
{
tdsAlg.Key = Key;
tdsAlg.IV = IV;
ICryptoTransform decryptor = tdsAlg.CreateDecryptor(tdsAlg.Key, tdsAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
There is no error in the code. I works fine. But strangely I noticed that the plainText is never been encoded. There is no line like Encoding.Unicode.GetBytes(plainText); or Encoding.UTF8.GetBytes(plainText); or similar like that. So, my question is , how does (in the code) the plainText which is a string gets converted to the encrypted byte? Is there any work done inside the streams? If thats so then where and how? As far as I understood there is no such line in between the streams that converts the string to byte. So , How does the overall code is working without this basic transformation?
Update:
Is this code really a valid code?
You are sending the plaintext to the encryption stream in the line swEncrypt.Write(plaintext). This does the byte conversion.
The StreamWriter is doing the encoding. The constructor being used specifies UTF-8 encoding:
This constructor creates a StreamWriter with UTF-8 encoding without a
Byte-Order Mark (BOM)

The input data is not a complete block

Scenario: One symmetric key, each user has his own IV, the documents are stored in a NVARCHAR(MAX) field. When I try to to decrypt the file, I get:
The input data is not a complete block.
// Create symmetric key
public static byte[] CreateKey()
{
AesCryptoServiceProvider aesCrypto = (AesCryptoServiceProvider)AesCryptoServiceProvider.Create();
byte[] key = aesCrypto.Key;
return key;
}
//Get key (stored in a database)
public static Byte[] GetAppKey()
{
return db.Encryptors.Where(x => x.EncryptorID == 1).Single().EncryptionKey.ToArray();
}
// Get application IV (stored in database)
public static Byte[] GetAppIV()
{
return db.Encryptors.Where(x => x.EncryptorID == 1).Single().IV.ToArray();
}
// Encrypt document (this will be stored in a VARBINARY(MAX) field
public static byte[] EncryptBinaryToBytes(Binary document, byte[] iv)
{
byte[] key = GetAppKey();
byte[] encrypted;
using (AesCryptoServiceProvider aesCsp = new AesCryptoServiceProvider())
{
aesCsp.Key = key;
aesCsp.IV = iv;
ICryptoTransform encryptor = aesCsp.CreateEncryptor(aesCsp.Key, aesCsp.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(document);
}
encrypted = msEncrypt.ToArray();
}
}
}
// return the encrypted document
return encrypted;
}
// Decrypt document
public static byte[] DecryptBytesToBytes(byte[] document, byte[] iv)
{
byte[] key = GetAppKey();
using (AesCryptoServiceProvider aesCsp = new AesCryptoServiceProvider())
{
aesCsp.Key = key;
aesCsp.IV = iv;
ICryptoTransform decryptor = aesCsp.CreateDecryptor(aesCsp.Key, aesCsp.IV);
using (MemoryStream msDecrypt = new MemoryStream())
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
{
using (StreamWriter swDecrypt = new StreamWriter(csDecrypt))
{
swDecrypt.Write(document);
}
byte[] decrypted = msDecrypt.ToArray();
// return the unencrypted document
return decrypted;
}
}
}
}
Thanks in advance.
To store the document
byte[] fileByte = fluUploadFile.FileBytes;
Binary document = new Binary(fileByte);
byte[] appIv = AES.GetAppIV();
byte[] encryptedDocument = AES.EncryptBinaryToBytes(document, appIv);
byte[] decryptedDocument = AES.DecryptBytesToBytes(encryptedDocument, appIv);
Document d = new Document()
{
OriginalName = originalName,
DocSize = fileSize,
BinaryDocument = encryptedDocument,
UploadedName = uploadedFileName,
MimeType = MIMEType,
DocExtension = extension
};
db.Documents.InsertOnSubmit(d);
db.SubmitChanges();
It's really important that you change the data type of the database field to VARBINARY(MAX), that way you avoid issues with character encodings and byte combinations that cannot be interpreted as legal characters.
Also, I think the problem is that you are not closing the streams before calling ToArray() method on the MemoryStream in both encrypt and decrypt routines. It's very important to call Close() in the CryptoStream so that FlushFinalBlock() is called and the encryption process writes the final block to the stream.
Try moving the call to MemoryStream.ToArray() to the outer using block, that is, outside the using block of CryptoStream, so that Dispose() is called on the CryptoStream and call MemoryStream.Close() before that.
Another problem with your code is that you are wrapping the CryptoStream with a StreamWriter, which writes the text representation of the object you pass into the Write method. You should instead write directly to the CryptoStream to avoid any byte to string conversions.

Categories