AES in ASP.NET with VB.NET - asp.net

What is a good link or article on encrypting a URL link with AES to pass username to another web site in ASP.NET using VB.NET 2005?
FYI: The receiving web site will have access to the private KEY to decrypt.

First
Don't do it! Writing your own crypto system can easily lead to making mistakes. It's best to use an existing system, or if not, get someone who knows cryptography to do it. If you have to do it yourself, read Practical Cryptography.
And please, remember: "We already have enough fast, insecure systems." (Bruce Schneier) -- Do things correct and worry about performance later.
That said, if you are stuck on using AES to roll your own, here are a few pointers.
Initialization Vector
AES is a block cipher. Given a key and a block of plaintext, it converts it to a specific ciphertext. The problem with this is that the same blocks of data will generate the same ciphertext with the same key, every time. So suppose you send data like this:
user=Encrypt(Username)&roles=Encrypt(UserRoles)
They're two separate blocks, and the UserRoles encryption will have the same ciphertext each time, regardless of the name. All I need is the ciphertext for an admin, and I can drop it right in with my cipher'd username. Oops.
So, there are cipher operation modes. The main idea is that you'll take the ciphertext of one block, and XOR it into the ciphertext of the next block. That way we'll do Encrypt(UserRoles, Username), and the Username ciphertext is affected by the UserRoles.
The problem is that the first block is still vulnerable - just by seeing someone's ciphertext, I might know their roles. Enter the initialization vector. The IV "starts up" the cipher and ensures it has random data to encrypt the rest of the stream. So now the UserRoles ciphertext has the ciphertext of the random IV XOR'd in. Problem solved.
So, make sure you generate a random IV for each message. The IV is not sensitive and can be sent plaintext with the ciphertext. Use an IV large enough -- the size of the block should be fine for many cases.
Integrity
AES doesn't provide integrity features. Anyone can modify your ciphertext, and the decrypt will still work. It's unlikely it'll be valid data in general, but it might be hard to know what valid data is. For instance, if you're transmitting a GUID encrypted, it'd be easy to modify some bits and generate a completely different one. That could lead to application errors and so on.
The fix there is to run a hash algorithm (use SHA256 or SHA512) on the plaintext, and include that in the data you transmit. So if my message is (UserName, Roles), you'll send (UserName, Roles, Hash(UserName, Roles)). Now if someone tampers with the ciphertext by flipping a bit, the hash will no longer compute and you can reject the message.
Key derivation
If you need to generate a key from a password, use the built-in class: System.Security.Cryptography.PasswordDeriveBytes. This provides salting and iterations, which can improve the strength of derived keys and reduce the chance of discovering the password if the key is compromised.
Timing/replay
Edit: Sorry for not mentioning this earlier :P. You also need to make sure you have an anti-replay system. If you simply encrypt the message and pass it around, anyone who gets the message can just resend it. To avoid this, you should add a timestamp to the message. If the timestamp is different by a certain threshold, reject the message. You may also want to include a one-time ID with it (this could be the IV) and reject time-valid messages that come from other IPs using the same ID.
It's important to make sure you do the hash verification when you include the timing information. Otherwise, someone could tamper with a bit of the ciphertext and potentially generate a valid timestamp if you don't detect such brute force attempts.
Sample code
Since apparently using an IV correctly is controversial for some folks, here's some code that'll generate random IVs and add them to your output for you. It'll also perform the authentication step, making sure the encrypted data wasn't modified.
using System;
using System.Security.Cryptography;
using System.Text;
class AesDemo {
const int HASH_SIZE = 32; //SHA256
/// <summary>Performs encryption with random IV (prepended to output), and includes hash of plaintext for verification.</summary>
public static byte[] Encrypt(string password, byte[] passwordSalt, byte[] plainText) {
// Construct message with hash
var msg = new byte[HASH_SIZE + plainText.Length];
var hash = computeHash(plainText, 0, plainText.Length);
Buffer.BlockCopy(hash, 0, msg, 0, HASH_SIZE);
Buffer.BlockCopy(plainText, 0, msg, HASH_SIZE, plainText.Length);
// Encrypt
using (var aes = createAes(password, passwordSalt)) {
aes.GenerateIV();
using (var enc = aes.CreateEncryptor()) {
var encBytes = enc.TransformFinalBlock(msg, 0, msg.Length);
// Prepend IV to result
var res = new byte[aes.IV.Length + encBytes.Length];
Buffer.BlockCopy(aes.IV, 0, res, 0, aes.IV.Length);
Buffer.BlockCopy(encBytes, 0, res, aes.IV.Length, encBytes.Length);
return res;
}
}
}
public static byte[] Decrypt(string password, byte[] passwordSalt, byte[] cipherText) {
using (var aes = createAes(password, passwordSalt)) {
var iv = new byte[aes.IV.Length];
Buffer.BlockCopy(cipherText, 0, iv, 0, iv.Length);
aes.IV = iv; // Probably could copy right to the byte array, but that's not guaranteed
using (var dec = aes.CreateDecryptor()) {
var decBytes = dec.TransformFinalBlock(cipherText, iv.Length, cipherText.Length - iv.Length);
// Verify hash
var hash = computeHash(decBytes, HASH_SIZE, decBytes.Length - HASH_SIZE);
var existingHash = new byte[HASH_SIZE];
Buffer.BlockCopy(decBytes, 0, existingHash, 0, HASH_SIZE);
if (!compareBytes(existingHash, hash)){
throw new CryptographicException("Message hash incorrect.");
}
// Hash is valid, we're done
var res = new byte[decBytes.Length - HASH_SIZE];
Buffer.BlockCopy(decBytes, HASH_SIZE, res, 0, res.Length);
return res;
}
}
}
static bool compareBytes(byte[] a1, byte[] a2) {
if (a1.Length != a2.Length) return false;
for (int i = 0; i < a1.Length; i++) {
if (a1[i] != a2[i]) return false;
}
return true;
}
static Aes createAes(string password, byte[] salt) {
// Salt may not be needed if password is safe
if (password.Length < 8) throw new ArgumentException("Password must be at least 8 characters.", "password");
if (salt.Length < 8) throw new ArgumentException("Salt must be at least 8 bytes.", "salt");
var pdb = new PasswordDeriveBytes(password, salt, "SHA512", 129);
var key = pdb.GetBytes(16);
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Key = pdb.GetBytes(aes.KeySize / 8);
return aes;
}
static byte[] computeHash(byte[] data, int offset, int count) {
using (var sha = SHA256.Create()) {
return sha.ComputeHash(data, offset, count);
}
}
public static void Main() {
var password = "1234567890!";
var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
var ct1 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct1"));
Console.WriteLine(Convert.ToBase64String(ct1));
var ct2 = Encrypt(password, salt, Encoding.UTF8.GetBytes("Alice; Bob; Eve;: PerformAct2"));
Console.WriteLine(Convert.ToBase64String(ct2));
var pt1 = Decrypt(password, salt, ct1);
Console.WriteLine(Encoding.UTF8.GetString(pt1));
var pt2 = Decrypt(password, salt, ct2);
Console.WriteLine(Encoding.UTF8.GetString(pt2));
// Now check tampering
try {
ct1[30]++;
Decrypt(password, salt, ct1);
Console.WriteLine("Error: tamper detection failed.");
} catch (Exception ex) {
Console.WriteLine("Success: tampering detected.");
Console.WriteLine(ex.ToString());
}
}
}
Output:
JZVaD327sDmCmdzY0PsysnRgHbbC3eHb7YXALb0qxFVlr7Lkj8WaOZWc1ayWCvfhTUz/y0QMz+uv0PwmuG8VBVEQThaNTD02JlhIs1DjJtg=
QQvDujNJ31qTu/foDFUiVMeWTU0jKL/UJJfFAvmFtz361o3KSUlk/zH+4701mlFEU4Ce6VuAAuaiP1EENBJ74Wc8mE/QTofkUMHoa65/5e4=
Alice; Bob; Eve;: PerformAct1 Alice;
Bob; Eve;: PerformAct2 Success:
tampering detected.
System.Security.Cryptography.CryptographicException:
Message hash incorrect. at
AesDemo.Decrypt(String password,
Byte[] passwordSalt, Byte[]
cipherText) in
C:\Program.cs:line
46 at AesDemo.Main() in
C:\Program.cs:line
100
After removing the random IV and the hash, here's the type of output:
tZfHJSFTXYX8V38AqEfYVXU5Dl/meUVAond70yIKGHY=
tZfHJSFTXYX8V38AqEfYVcf9a3U8vIEk1LuqGEyRZXM=
Notice how the first block, corresponding to "Alice; Bob; Eve;" is the same. "Corner case" indeed.
Example without hashing
Here's a simple example of passing a 64-bit integer. Just encrypt and you're open to attack. In fact, the attack is easily done, even with CBC padding.
public static void Main() {
var buff = new byte[8];
new Random().NextBytes(buff);
var v = BitConverter.ToUInt64(buff, 0);
Console.WriteLine("Value: " + v.ToString());
Console.WriteLine("Value (bytes): " + BitConverter.ToString(BitConverter.GetBytes(v)));
var aes = Aes.Create();
aes.GenerateIV();
aes.GenerateKey();
var encBytes = aes.CreateEncryptor().TransformFinalBlock(BitConverter.GetBytes(v), 0, 8);
Console.WriteLine("Encrypted: " + BitConverter.ToString(encBytes));
var dec = aes.CreateDecryptor();
Console.WriteLine("Decrypted: " + BitConverter.ToUInt64(dec.TransformFinalBlock(encBytes, 0, encBytes.Length), 0));
for (int i = 0; i < 8; i++) {
for (int x = 0; x < 250; x++) {
encBytes[i]++;
try {
Console.WriteLine("Attacked: " + BitConverter.ToUInt64(dec.TransformFinalBlock(encBytes, 0, encBytes.Length), 0));
return;
} catch { }
}
}
}
Output:
Value: 6598637501946607785 Value
(bytes): A9-38-19-D1-D8-11-93-5B
Encrypted:
31-59-B0-25-FD-C5-13-D7-81-D8-F5-8A-33-2A-57-DD
Decrypted: 6598637501946607785
Attacked: 14174658352338201502
So, if that's the kind of ID you're sending, it could quite easily be changed to another value. You need to authenticate outside of your message. Sometimes, the message structure is unlikely to fall into place and can sorta act as a safeguard, but why rely on something that could possibly change? You need to be able to rely on your crypto working correctly regardless of the application.

I wrote a blog post which has a sample project that you can download here (C# though):
http://www.codestrider.com/blog/read/AESFileEncryptorWithRSAEncryptedKeys.aspx
The code basically uses AES for encryption of binary data and then RSA encrypts the Key and the IV using an X509Certificate. So, as long as the private key certificate is available, the Key and IV can be decrypted, and then in turn the AES encrypted data can be decrypted ..
You could set up your certificate stores so that the 'encryptor' only has access to the public key certificate, while the 'decryptor' has access to the private key.
This allows you to encrypt using different Key and IV each time and avoid hardcoding anything.. which I believe is more secure. There should be nothing in your source code that would easily allow someone to decrypt your data - and if your system was ever compromised, you would only need to swap out the certificates with new ones. No need to recompile the application with new hardcoded values.. :)
The sample code may be slightly different from your intended use, but I think the technique and some of the code might be useful to you.

Below you'll find a class that provides AES Encryption/Decryption methods that explicitly provide URL-friendly strings for use in applications like yours. It also has the methods that work with byte arrays.
NOTE: you should use different values in the Key and Vector arrays! You wouldn't want someone to figure out your keys by just assuming that you used this code as-is! All you have to do is change some of the numbers (must be <= 255) in the Key and Vector arrays.
Using it is easy: just instantiate the class and then call (usually) EncryptToString(string StringToEncrypt) and DecryptString(string StringToDecrypt) as methods. It couldn't be any easier (or more secure) once you have this class in place.
using System;
using System.Data;
using System.Security.Cryptography;
using System.IO;
public class SimpleAES
{
// Change these keys
private byte[] Key = { 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 };
private byte[] Vector = { 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 2521, 112, 79, 32, 114, 156 };
private ICryptoTransform EncryptorTransform, DecryptorTransform;
private System.Text.UTF8Encoding UTFEncoder;
public SimpleAES()
{
//This is our encryption method
RijndaelManaged rm = new RijndaelManaged();
//Create an encryptor and a decryptor using our encryption method, key, and vector.
EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);
//Used to translate bytes to text and vice versa
UTFEncoder = new System.Text.UTF8Encoding();
}
/// -------------- Two Utility Methods (not used but may be useful) -----------
/// Generates an encryption key.
static public byte[] GenerateEncryptionKey()
{
//Generate a Key.
RijndaelManaged rm = new RijndaelManaged();
rm.GenerateKey();
return rm.Key;
}
/// Generates a unique encryption vector
static public byte[] GenerateEncryptionVector()
{
//Generate a Vector
RijndaelManaged rm = new RijndaelManaged();
rm.GenerateIV();
return rm.IV;
}
/// ----------- The commonly used methods ------------------------------
/// Encrypt some text and return a string suitable for passing in a URL.
public string EncryptToString(string TextValue)
{
return ByteArrToString(Encrypt(TextValue));
}
/// Encrypt some text and return an encrypted byte array.
public byte[] Encrypt(string TextValue)
{
//Translates our text value into a byte array.
Byte[] bytes = UTFEncoder.GetBytes(TextValue);
//Used to stream the data in and out of the CryptoStream.
MemoryStream memoryStream = new MemoryStream();
/*
* We will have to write the unencrypted bytes to the stream,
* then read the encrypted result back from the stream.
*/
#region Write the decrypted value to the encryption stream
CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
cs.Write(bytes, 0, bytes.Length);
cs.FlushFinalBlock();
#endregion
#region Read encrypted value back out of the stream
memoryStream.Position = 0;
byte[] encrypted = new byte[memoryStream.Length];
memoryStream.Read(encrypted, 0, encrypted.Length);
#endregion
//Clean up.
cs.Close();
memoryStream.Close();
return encrypted;
}
/// The other side: Decryption methods
public string DecryptString(string EncryptedString)
{
return Decrypt(StrToByteArray(EncryptedString));
}
/// Decryption when working with byte arrays.
public string Decrypt(byte[] EncryptedValue)
{
#region Write the encrypted value to the decryption stream
MemoryStream encryptedStream = new MemoryStream();
CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
decryptStream.FlushFinalBlock();
#endregion
#region Read the decrypted value from the stream.
encryptedStream.Position = 0;
Byte[] decryptedBytes = new Byte[encryptedStream.Length];
encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
encryptedStream.Close();
#endregion
return UTFEncoder.GetString(decryptedBytes);
}
/// Convert a string to a byte array. NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
// System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
// return encoding.GetBytes(str);
// However, this results in character values that cannot be passed in a URL. So, instead, I just
// lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
public byte[] StrToByteArray(string str)
{
if (str.Length == 0)
throw new Exception("Invalid string value in StrToByteArray");
byte val;
byte[] byteArr = new byte[str.Length / 3];
int i = 0;
int j = 0;
do
{
val = byte.Parse(str.Substring(i, 3));
byteArr[j++] = val;
i += 3;
}
while (i < str.Length);
return byteArr;
}
// Same comment as above. Normally the conversion would use an ASCII encoding in the other direction:
// System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
// return enc.GetString(byteArr);
public string ByteArrToString(byte[] byteArr)
{
byte val;
string tempStr = "";
for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
{
val = byteArr[i];
if (val < (byte)10)
tempStr += "00" + val.ToString();
else if (val < (byte)100)
tempStr += "0" + val.ToString();
else
tempStr += val.ToString();
}
return tempStr;
}
}

Markt pointed out that Rijndael uses the AES encryption algorithm. Since a managed implementation ships with the .net framework (and has since at least 1.1), using it should satisfy the OP.
The API docs have a pretty straightforward example of using Rijndael as an encryption and decryption stream.
If you've got a way to get the shared secret (e.g., the private key) to the other website then you might be able to get away with using plain old symmetric encryption (no public key, both sides know the IV and private key). This is especially the case if your brain is the "insecure channel" across which the key is shared (e.g., you administer both websites). :)
Have a look at "Keep Your Data Secure
with the New Advanced Encryption
Standard". An AES implementation
doesn't ship with the .NET framework
but it links to a custom
implementation (AES.exe).
1:
http://msdn.microsoft.com/en-us/magazine/cc164055.aspx

Related

Decrypt AES changing the last letter

I need to encrypt some data in a client application and verify it later in a server application.
I'm assuming that if the message was decrypted then it was from a valid client, since the key is necessary to create a valid crypted string.
I'using AES implementation from MSDN https://learn.microsoft.com/pt-br/dotnet/api/system.security.cryptography.aes?view=netframework-4.8
I choose AES because in my tests it generated a short string. It is an important issue for me.
public static void Main()
{
string original = "message to secure";
using (Aes myAes = Aes.Create())
{
myAes.Key = Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODw==");
byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);
var encryptedString = Convert.ToBase64String(encrypted);
string roundtrip = DecryptStringFromBytes_Aes(Convert.FromBase64String(encryptedString), myAes.Key, myAes.IV);
Console.WriteLine("Encrypted: " + encryptedString);
Console.WriteLine("Decrypted: " + roundtrip);
}
Console.ReadKey();
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.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_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
string plaintext = null;
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.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;
}
But I noticed that if there is a change in the last caracter (before equals sign) the string is decrypted as nothing was changed.
For example:
HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR14= was generated and I changed it to HdPAmfHTxkMmj8D3VelWjH2A8iGm6gnzzPYGNT5NR15= and a got the same result.
Could someone give me some guidance in how I guarantee that if the generated string was change it cannot be decrypted?
Solomon has more or less hit the nail on the head with all their comments.
I'm assuming that if the message was decrypted then it was from a valid client, since the key is necessary to create a valid crypted string.
This base assumption is actually false. There are a number of scenarios (in unauthenticated modes of operation) where decryption can succeed even if the ciphertext has been modified - resulting in a plaintext different from the one that was originally encrypted.
Recall that AES is a block cipher. It transforms one block of 128 bits to another block of 128 bits. The only other variables are the key used, and the operation (e.g. encrypt or decrypt). There is no mechanism to detect if the block of 128 bits passed in has been modified since some prior operation - AES isn't aware of that. It is simply a keyed transformation function.
To avoid this problem, use an authenticated mode of operation like GCM, or use an HMAC. See the examples in this repository for an example of use GCM in C#.
As to the second issue:
But I noticed that if there is a change in the last caracter (before equals sign) the string is decrypted as nothing was changed.
Technically nothing was changed - this is a "feature". Each base64 character represents 6 bits of the original data. This means that, unless your ciphertext length is divisible by both 8 and 6, there are bits "left over". See the example below where we encode 16-bits:
Raw : { 0x00, 0x01 }
Binary : 00000000 00000001
Base64 : AAE=
Binary (6 Digit Grouping): 000000 000000 000100
Binary (8 Digit Grouping): 00000000 00000001 00
^^ these bits are irrelevant
In essence, nothing to worry about.

Encryption with DES with urlENcode(encrypt)

I am attempting to use DES to encrypt / decrypt. However, the decrypt is not working. I keep getting the error: Cannot read property 'charCodeAt' of null
The decryption is from http://www.tero.co.uk/des/
I am using this to encode:
var decodedKey = decodeBase64(key);
var em = urlEncode(encrypt(decodedKey, value));
and then I am attempting to use this function to decrypt:
var emD = urlDecode(decrypt(decodedKey, em))
with
function encrypt(key, message) {
var ciphertext = des(key, message, 1, 0, null, 1);
var encodedCiphertext = encodeBase64(ciphertext);
return encodedCiphertext;
}
function decrypt(key,message) {
var ciphertext = des(key, message, 0, 1, null,1);
var plainText = decodeBase64(ciphertext);
return plainText;
};
With:
urlEncode(encrypt(decodedKey, value));
you first encrypt then url-encode. When you need to decrypt, you need to first url-decode then decrypt - something like:
decrypt(decodedKey, urlDecode(em));
as opposed to decrypt then url-decode as you try with this statement:
urlDecode(decrypt(decodedKey, em));
Also, please note that DES is considered insecure and should be avoided. Use a secure cipher like AES.

converting byte array to CryptographicKey

Background:
I am building my Unity app for Windows Store Universal 10. It's already live for Android and iOS as well as WP8.
My app sends encrypted session data back to the server for processing. Currently it's using the AesManaged() library like so:
using (var memoryStream = new MemoryStream())
{
Aes aes = new AesManaged();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.Key = keyBytes;
...
using (CryptoStream cs = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cs.Write(plainTextBytes, 0, plainTextBytes.Length);
cs.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
cs.Close();
return cipherTextBytes;
}
}
The key I am using is using for Aes.Key is a custom method which returns a byte[]:
public static byte[] getKeyBytes(string pApiKey, string pSecret)
According to several articles,
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/52080335-0016-4370-889b-3afe0c0dcb7a/how-to-do-simple-aes-encryptiondecryption-in-metro
http://forum.unity3d.com/threads/aesmanaged-wp8-1-the-type-or-namespace-name-aesmanaged-could-not-be-found.376732/
And my Unity Build errors, the AesManaged libraries do not work when targeting Windows Store and I need to instead be using the updated encryption libraries (Windows.Security.Cryptography.Core):
https://msdn.microsoft.com/en-us/library/windows/apps/windows.security.cryptography.core.symmetrickeyalgorithmprovider.aspx
This seems straightforward, except that the new encryption method:
CryptographicEngine.Encrypt(key, buffMsg, iv);
Takes a CryptographicKey object as an argument instead of a byte[] (which my keys currently are)
Question:
How does one go about casting a byte[] to a CryptographicKey object so I can use the new required encryption methods with my existing keys?
When I try:
CryptographicKey key = new CryptographicKey(pKeyByteArray);
I get:
error CS1729: 'CryptographicKey' does not contain a constructor that takes 1 arguments
Note: there appears to be a CryptographicKey in System.Web.Security.Cryptography which does this:
http://referencesource.microsoft.com/#System.Web/Security/Cryptography/CryptographicKey.cs,58f5cc896110a437,references
But this appears to not available to Windows Store builds.
I wasn't able to test the code below but I modified it to work with your byte array.
byte[] pKeyByteArray;
uint keyLength = 512; //512, 1024, 2048, or 4096 bits
pKeyByteArray = new byte[50];
string strAsymmetricAlgName = System.Text.Encoding.UTF8.GetString(pKeyByteArray);
AsymmetricKeyAlgorithmProvider objAlgProv = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(strAsymmetricAlgName);
CryptographicKey keyPair = objAlgProv.CreateKeyPair(keyLength);
// Export the public key to a buffer for use by others.
IBuffer buffPublicKey = keyPair.ExportPublicKey();
CryptographicKeyApp.buffKeyPair = keyPair.Export();
// Retrieve the size of the key pair.
UInt32 lengthKeyPair = keyPair.KeySize;
// Do something with your the public key.
buffPublicKey........

Encryption Failing with error Invalid length for a Base-64 char array or string

I am trying to encrypt a PRIVATE KEY with a passphrase so I can save the file to disk. But the encryption method is throwing the exception: "Invalid length for a Base-64 char array or string".
The Encryption method is (it's a 2048bit key length):
public static string Encrypt(string plainString, string key, int keySize = Keysize.SymmetricKeyLength)
{
var aesEncryption = new RijndaelManaged
{
KeySize = keySize,
BlockSize = 128,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
IV = Convert.FromBase64String(Encoding.UTF8.GetString(Convert.FromBase64String(key)).Split(',')[0]),
Key = Convert.FromBase64String(Encoding.UTF8.GetString(Convert.FromBase64String(key)).Split(',')[1])
};
byte[] plainText = Encoding.UTF8.GetBytes(plainString);
ICryptoTransform crypto = aesEncryption.CreateEncryptor();
// The result of the encryption and decryption
byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
I am converting the Private Key to a Base64 string before passing it down to the encryption method using this method:
public string EncodeTo64(string plainString)
{
var bytes = Encoding.UTF8.GetBytes(plainString);
return Convert.ToBase64String(bytes);
}
The Private Key is:
<RSAKeyValue><Modulus>rhtMjLTg17sYlns4ktTow9eeuwRNra0+AO2HqESGmA8zkxe/uOv0msXzzLWUWzdPaTxi4OV+PNVPBAHW1C0CTT/33NlvipkJ1Qr5BJK1TiVZCMInshe4OL/7GNnPUPhsS6DZ/c/fnWLoxtRMUmkKgpWmtXGs7ZSoIztdJ1bgiygJWCDvrHTokVIzDaNzRonZIFk41Qt4rPofCEawjkR639OcOfazNlmU9JjvRs3ysoYghDzvVuLvJvPK7zCMzpJMQFQE7cipezXbumTqSdp20mQXJduDbD9qLKXOvcTw+2KPoNlUp+IRQrOmSf+Dl4Vxi+8+UuOH7KDLz7yL9IOPeQ==</Modulus><Exponent>AQAB</Exponent><P>2AHey3Tgg/K8M16kv6bWk1BsFGhg9xXZw2ruVhS620gyvPBtWBuOU+tzPRnEplw+Kp9jua7Nu4JkKwpQdZvRqeW42d/UCergkdNRheM3DXYj/xQNs8a1diTNe72elCsCfSHr1z/vgN+Cp+v8O4BzX07TrHeGOOP/7HWhE6setxM=</P><Q>zld05TyC/vVI2sBgaR/iYyXdUO3iIIwkGSyOmfDr1dbCKFR7btGLEsW9EpCGibyGPbAk4jA9BLU1bviBM8iH6mxWn1s4UAiIha0QSM2K9NWUPi67FELl6Fs2eLHl9qRniBhAOBCGArklail+YadKCtUsrWhfJgvO3uxkp+fg9MM=</Q><DP>sziaCmVnAxObY2PbfciHsKLBig0wptHSZHmMVo/MmbRFpM43aysx5B8u9jszFnTif6rPq3iF6lY9lhhwuaQXScf4n40++RuQSG307gmf2+Nx6mpRFCCC3wuaElk6AeXNotVKQMYjieHpHjqGhTgGgcV9i1OAYiOKbD8M7qzER1E=</DP><DQ>FEazzfLsTHF9/0D4OFxRurx1ywYVOm2K/o5KVQY/pnu8CIqEtpcQu3+C3Ngm4FIOPvGYLkHfPR8xaP4ydAw4juimenJUTkkIYVpoRz8rcHOsZY/iAlOwk+yipamVl28AXXdEmD3HbW0UKCJ7sMznkbjw8vlWoD54zZ8dJQK8MFE=</DQ><InverseQ>FUFC9v5B1mXxbbiD4WZm/KGIa3XO5+K9FwSRroj8wNMt+JY5aMS8SfUcrZMvUXfHS9+3BYXBIlxPBUm6HnfB7yPE9S0LFzRpB7APbJ0HVIlSjMS9ZdkqxShGAEufYx/FKQXomJlEXXkpgAiDnUnCR2H+ekQf1YpQUzol2KedwfA=</InverseQ><D>gEhc/s/HWyzf0QC5jnaRirs0mVdyZKVhKg3aBoF3KlMJDThSa05vzBpOqGaiCROXz1JPCKYPfYMt1SYFxA/lwkV/u5n6NYTNWcvb7yKptAqQr4Ne/Dm94xKRUJ4rwt1H7fF2rSyc9roKCXYjRhVfSRg63TYE1IjT2iHDYVkB2YVPK67O2O7YmQXeUHMRMVwXpnZCvweleRKlYbVFx2N7ZEC1TZoUn2RKsiBEem1eNSwnLa4wUf1Xl8Q8h+ziY0GnREf9JpTZhJW7f4MKsqLyOMgmoskKiIOWlnwq/b01ivB2CXFhxiiVuNUPPiMuJu6bhljeulvKl32kEzLAFxm2gQ==</D></RSAKeyValue>
and the resulting Base64 string from the above conversion method is:
PFJTQUtleVZhbHVlPjxNb2R1bHVzPnJodE1qTFRnMTdzWWxuczRrdFRvdzllZXV3Uk5yYTArQU8ySHFFU0dtQTh6a3hlL3VPdjBtc1h6ekxXVVd6ZFBhVHhpNE9WK1BOVlBCQUhXMUMwQ1RULzMzTmx2aXBrSjFRcjVCSksxVGlWWkNNSW5zaGU0T0wvN0dOblBVUGhzUzZEWi9jL2ZuV0xveHRSTVVta0tncFdtdFhHczdaU29JenRkSjFiZ2l5Z0pXQ0R2ckhUb2tWSXpEYU56Um9uWklGazQxUXQ0clBvZkNFYXdqa1I2MzlPY09mYXpObG1VOUpqdlJzM3lzb1lnaER6dlZ1THZKdlBLN3pDTXpwSk1RRlFFN2NpcGV6WGJ1bVRxU2RwMjBtUVhKZHVEYkQ5cUxLWE92Y1R3KzJLUG9ObFVwK0lSUXJPbVNmK0RsNFZ4aSs4K1V1T0g3S0RMejd5TDlJT1BlUT09PC9Nb2R1bHVzPjxFeHBvbmVudD5BUUFCPC9FeHBvbmVudD48UD4yQUhleTNUZ2cvSzhNMTZrdjZiV2sxQnNGR2hnOXhYWncycnVWaFM2MjBneXZQQnRXQnVPVSt0elBSbkVwbHcrS3A5anVhN051NEprS3dwUWRadlJxZVc0MmQvVUNlcmdrZE5SaGVNM0RYWWoveFFOczhhMWRpVE5lNzJlbENzQ2ZTSHIxei92Z04rQ3ArdjhPNEJ6WDA3VHJIZUdPT1AvN0hXaEU2c2V0eE09PC9QPjxRPnpsZDA1VHlDL3ZWSTJzQmdhUi9pWXlYZFVPM2lJSXdrR1N5T21mRHIxZGJDS0ZSN2J0R0xFc1c5RXBDR2lieUdQYkFrNGpBOUJMVTFidmlCTThpSDZteFduMXM0VUFpSWhhMFFTTTJLOU5XVVBpNjdGRUxsNkZzMmVMSGw5cVJuaUJoQU9CQ0dBcmtsYWlsK1lhZEtDdFVzcldoZkpndk8zdXhrcCtmZzlNTT08L1E+PERQPnN6aWFDbVZuQXhPYlkyUGJmY2lIc0tMQmlnMHdwdEhTWkhtTVZvL01tYlJGcE00M2F5c3g1Qjh1OWpzekZuVGlmNnJQcTNpRjZsWTlsaGh3dWFRWFNjZjRuNDArK1J1UVNHMzA3Z21mMitOeDZtcFJGQ0NDM3d1YUVsazZBZVhOb3RWS1FNWWppZUhwSGpxR2hUZ0dnY1Y5aTFPQVlpT0tiRDhNN3F6RVIxRT08L0RQPjxEUT5GRWF6emZMc1RIRjkvMEQ0T0Z4UnVyeDF5d1lWT20ySy9vNUtWUVkvcG51OENJcUV0cGNRdTMrQzNOZ200RklPUHZHWUxrSGZQUjh4YVA0eWRBdzRqdWltZW5KVVRra0lZVnBvUno4cmNIT3NaWS9pQWxPd2sreWlwYW1WbDI4QVhYZEVtRDNIYlcwVUtDSjdzTXpua2Jqdzh2bFdvRDU0elo4ZEpRSzhNRkU9PC9EUT48SW52ZXJzZVE+RlVGQzl2NUIxbVh4YmJpRDRXWm0vS0dJYTNYTzUrSzlGd1NScm9qOHdOTXQrSlk1YU1TOFNmVWNyWk12VVhmSFM5KzNCWVhCSWx4UEJVbTZIbmZCN3lQRTlTMExGelJwQjdBUGJKMEhWSWxTak1TOVpka3F4U2hHQUV1Zll4L0ZLUVhvbUpsRVhYa3BnQWlEblVuQ1IySCtla1FmMVlwUVV6b2wyS2Vkd2ZBPTwvSW52ZXJzZVE+PEQ+Z0VoYy9zL0hXeXpmMFFDNWpuYVJpcnMwbVZkeVpLVmhLZzNhQm9GM0tsTUpEVGhTYTA1dnpCcE9xR2FpQ1JPWHoxSlBDS1lQZllNdDFTWUZ4QS9sd2tWL3U1bjZOWVROV2N2Yjd5S3B0QXFRcjROZS9EbTk0eEtSVUo0cnd0MUg3ZkYyclN5Yzlyb0tDWFlqUmhWZlNSZzYzVFlFMUlqVDJpSERZVmtCMllWUEs2N08yTzdZbVFYZVVITVJNVndYcG5aQ3Z3ZWxlUktsWWJWRngyTjdaRUMxVFpvVW4yUktzaUJFZW0xZU5Td25MYTR3VWYxWGw4UThoK3ppWTBHblJFZjlKcFRaaEpXN2Y0TUtzcUx5T01nbW9za0tpSU9XbG53cS9iMDFpdkIyQ1hGaHhpaVZ1TlVQUGlNdUp1NmJobGpldWx2S2wzMmtFekxBRnhtMmdRPT08L0Q+PC9SU0FLZXlWYWx1ZT4=
Apparently this Base64 string is invalid and the encryption fails.
Can anybody see where I am going wrong?
Your Base64 string appears to be valid. When I decode it I get some recognisable XML for RSA:
<RSAKeyValue>
<Modulus>rht ... PeQ==</Modulus>
<Exponent>AQAB</Exponent>
<P>2AH ... txM=</P>
<Q>zld ... 9MM=</Q>
<DP>szi ... R1E=</DP>
<DQ>FEa ... 8MFE=</DQ>
<InverseQ>FUF ... wfA=</InverseQ>
<D>gEh ... m2gQ==</D>
</RSAKeyValue>
That has added newlines and is abbreviated for clarity.
Was that what you were expecting? If it was then I suggest that you check each of the included pieces of Base64 for errors. Alternatively, it may just be that your initial piece of Base64 is too long.
Fixed! It appears the error was a little misleading (or likely I was being stupid!) and it looks like it was NOT the plain text for encryption that was the cause of the exception but the password/passphrase.
I found another app I was using this method in and found the KEY was the problem. I was passing a plain text password in initially, then I changed this to a SHA-256 hash of the password when this was in fact not a valid key.
I added these two methods (the later of which I found on here):
public static string AesKeyFromPassword(string password, int keySize = Keysize.SymmetricKeyLength)
{
byte[] passwordByteArray = CreateKey(password);
var aesEncryption = new RijndaelManaged
{
KeySize = keySize,
BlockSize = 128,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
Key = passwordByteArray
};
aesEncryption.GenerateIV();
string ivStr = Convert.ToBase64String(aesEncryption.IV);
string keyStr = Convert.ToBase64String(aesEncryption.Key);
string completeKey = ivStr + "," + keyStr;
return Convert.ToBase64String(Encoding.UTF8.GetBytes(completeKey));
}
private static byte[] CreateKey(string password)
{
var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
const int Iterations = 9872;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
return rfc2898DeriveBytes.GetBytes(32);
}
This now takes the plain text password/phrase and generates a valid encryption key which I then use in the Encrypt method.
Thank for your help guys! I can now securely store the Private Key! :)

AES Salting iterations, How many required?

I am trying to execute a simple Encrypt and Decrypt AES and I was trying to use salting. These values will be stored in the DB and retrieved from there. (I cannot use hashing) I am using static key and static salt for the time being.
My question is how many iterations of salting should I do? I mean I have to store the value in the DB and I see that at 2 iterations (128bit key, 42bit salt) I get a encrypted string of 152 charaters for 40 characters. For 4 iterations, it's 364 characters for 40 characters, for 8 iterations 1536 characters for 40 characters and for 16 iterations, a ridiculous 19968 characters for 40 characters.
So what should be my optimal number of iterations?
public static String AESencrypt(String value) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
String valueToEnc = null;
String eValue = value;
for (int i = 0; i < ITERATIONS; i++) {
valueToEnc = salt + eValue;
byte[] encValue = c.doFinal(valueToEnc.getBytes());
eValue = new BASE64Encoder().encode(encValue);
}
return eValue;
}
public static String AESdecrypt(String value) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
String dValue = null;
String valueToDecrypt = value;
for (int i = 0; i < ITERATIONS; i++) {
byte[] decordedValue = new BASE64Decoder().decodeBuffer(valueToDecrypt);
byte[] decValue = c.doFinal(decordedValue);
dValue = new String(decValue).substring(salt.length());
valueToDecrypt = dValue;
}
return dValue;
}
Salting is used on passwords to derive keys. It is not used on ciphertext, not even if it is base 64 encoded. Salting is used to make it harder for an attacker to find the password using brute force or rainbow tables. It is typically used by a PBKDF such as PBKDF2, bcrypt or scrypt.
In your case it doesn't matter how much you encrypt; if the attacker tries the key, decrypts the ciphertext and then finds base 64, the attacker will know he has found the key, and can simply do the number of iterations again.
In general, it does not make sense to encrypt things multiple times; block ciphers themselves already should contain a sufficient number of "rounds".

Resources