Decrypt AES changing the last letter - encryption

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.

Related

decrypt AES input on Flutter, when on web use cryptoJS AES

On web, I'm using CryptoJS for decrypto JS:
CryptoJS.AES.decrypt(inputBase64, key).toString(CryptoJS.enc.Utf8);
Example:
input: "tzfwnxVwE/qNoaWRRfqLp11ZyhB4UtKO+0/Lvv5B7eE="
key: "20190225165436_15230006321670000_15510884759030000"
On flutter I can't find any library to decrypt with a key of any length.
I know "For AES, NIST selected three members of the Rijndael family, each with a block size of 128 bits, but three different key lengths: 128, 192 and 256 bits.
"
But I don't know how to convert any length key to 128 bit format?
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:tuple/tuple.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
String encryptAESCryptoJS(String plainText, String passphrase) {
try {
final salt = genRandomWithNonZero(8);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final encrypted = encrypter.encrypt(plainText, iv: iv);
Uint8List encryptedBytesWithSalt = Uint8List.fromList(
createUint8ListFromString("Salted__") + salt + encrypted.bytes);
return base64.encode(encryptedBytesWithSalt);
} catch (error) {
throw error;
}
}
String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
Uint8List encryptedBytes =
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
final salt = encryptedBytesWithSalt.sublist(8, 16);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final decrypted =
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
return decrypted;
} catch (error) {
throw error;
}
}
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
var password = createUint8ListFromString(passphrase);
Uint8List concatenatedHashes = Uint8List(0);
Uint8List currentHash = Uint8List(0);
bool enoughBytesForKey = false;
Uint8List preHash = Uint8List(0);
while (!enoughBytesForKey) {
int preHashLength = currentHash.length + password.length + salt.length;
if (currentHash.length > 0)
preHash = Uint8List.fromList(
currentHash + password + salt);
else
preHash = Uint8List.fromList(
password + salt);
currentHash = md5.convert(preHash).bytes;
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
}
var keyBtyes = concatenatedHashes.sublist(0, 32);
var ivBtyes = concatenatedHashes.sublist(32, 48);
return new Tuple2(keyBtyes, ivBtyes);
}
Uint8List createUint8ListFromString(String s) {
var ret = new Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
Uint8List genRandomWithNonZero(int seedLength) {
final random = Random.secure();
const int randomMax = 245;
final Uint8List uint8list = Uint8List(seedLength);
for (int i=0; i < seedLength; i++) {
uint8list[i] = random.nextInt(randomMax)+1;
}
return uint8list;
}
Usage
import 'package:app/utils/cryptojs_aes_encryption_helper.dart';
String plainText = 'PlainText is Me';
var encrypted = encryptAESCryptoJS(plainText, "password");
var decrypted = decryptAESCryptoJS(encrypted, "password");
When you pass CryptoJS a string as the key it treats it as a passphrase and generates the key from it using a key derivation function - in this case PBKDF2. It generates a 256 bit key and a 128 bit initialization vector (IV). It then uses those for the encryption/decryption. You also need to find out what chaining method CryptoJS uses (probably cipher block chaining (CBC)) and what padding method it uses (to make sure that the plain text is a round number of 128 bit blocks - probably PKCS#7).
CryptoJS has this "works out of the box" mode, but it isn't particularly clear what it's doing under the hood - you'd need to read the source code or scour the documentation.
When trying to inter-operate between two systems/languages it's best if you remain in charge of things, rather than letting one end make arbitrary decisions. That way you can make sure that you have the settings the same at each end.
So, you might choose to:
Use PBKDF2 to generate a 128 bit key and 128 bit IV from the string
passphrase - with, say, 9999 rounds
Use PKCS#7 padding
Use AES in CBC mode
The pointycastle package supports all the above in Dart. It looks like CryptoJS supports all of those too.
Start with a passphrase and make sure you can generate the same key and IV in JS and Dart. Then move onto creating the ciphers.
Remember, too, never to encrypt two messages with the same key/IV pair. Use a message sequence number, for example, to slightly change the IV for each message.
Sample encrypt using nodejs script:
var key = CryptoJS.PBKDF2("123456", "123456", {
keySize: 256 / 32
});
var iv = CryptoJS.PBKDF2("123456", "123456", {
keySize: 128 / 32
});
var encrypted = CryptoJS.AES.encrypt('my message', key, { iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
decript using dart
import 'package:encrypt/encrypt.dart' as aes;
import 'package:crypto/crypto.dart';
import 'package:hex/hex.dart';
import 'package:password_hash/pbkdf2.dart';
void main(List<String> arguments) {
String encrypted = 'HbsmGAigiIWmU3MNZAf8+w==';
final generator = PBKDF2(hashAlgorithm: sha1);
final key = aes.Key.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 32)));
final iv = aes.IV.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 16)));
final encrypter = aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));
final decrypted = encrypter.decrypt64(encrypted, iv:iv);
print(decrypted);
}

encrypt using ase256 gives different output in python and nodejs

I am trying to encrypt a string "1" using key = "secret_key" and text "11869021012". Earlier I had written this in nodejs. now I want to port this to python. but here surprisingly both are giving different outputs.
var crypto = require('crypto');
function getBytes (str) {
let bytes = [], char;
str = encodeURI(str);
while (str.length) {
char = str.slice(0, 1);
str = str.slice(1);
if ('%' !== char) {
bytes.push(char.charCodeAt(0));
} else {
char = str.slice(0, 2);
str = str.slice(2);
bytes.push(parseInt(char, 16));
}
}
return bytes;
};
function getIV (str, bytes){
iv = getBytes(str);
if(!bytes) bytes = 16;
for(let i=iv.length;i<bytes;i++) {
iv.push(0);
}
return Buffer.from(iv);
};
function getKey (pwd){
pwd = Buffer.from(getBytes(pwd), 'utf-8');
let hash = crypto.createHash('sha256');
pwd = hash.update(pwd).digest();
return pwd;
};
function createCipherIV (algorithm, input_key, iv_input, text){
let iv = getIV(iv_input);
let key = getKey(input_key);
let cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text)
encrypted += cipher.final('base64');
return encrypted;
}
output = createCipherIV('aes256', 'secret_key', '11869021012', '1')
console.log(output)
This produces the output:
s6LMaE/YRT6y8vr2SehLKw==
python code:
# AES 256 encryption/decryption using pycrypto library
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
password = "secret_key"
def encrypt(raw, password):
private_key = hashlib.sha256(bytearray(password, "utf-8")).digest()
raw = pad(raw)
iv = b'11869021012\x00\x00\x00\x00\x00'
cleartext = bytearray(raw, 'utf-8')
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(cleartext))
# First let us encrypt secret message
encrypted = encrypt("1", password)
print(encrypted)
This produces the output:
MTE4NjkwMjEwMTIAAAAAALOizGhP2EU+svL69knoSys=
I have used aes256 algorithm here for encrypting message.
Clearly they are very close, but node seems to be padding the output with some extra bytes. Any ideas how I can get the two to interoperate?
First, in a secure crypto system, you should expect the output to be different every time you encrypt, even using the same code. That fact that yours doesn't indicates it's an insecure cipher. Typically this is done by adding a random IV.
Your IV is "11869021012", which is horrible (because it's not random, and not even 16 bytes), but it does seem you're using it the same way in both, so that's fine.
Your password is the SHA-256 of a string, which is a horrible way to create a key, but still, you seem to be doing it the same way in both cases, so that's fine.
Your problem is that the Python code emits the IV followed by the cipher text. Your JS code does not emit the IV; it only emits the cipher text. So you probably meant this in the Python:
return base64.b64encode(cipher.encrypt(cleartext))
Or you need to rework the JavaScript to glue together the IV and the cipher text before Base64 encoding.

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 in ASP.NET with VB.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

Resources