We had a small desktop app that needs to be provided as a web feature now (.Net). This app contains some code for encryption and uses Rijndael classes from .Net framework. The code accepts an input string, encrypts it and writes it out the results to a file. Since all the code is contained in one class, I just copied the class to my web service application. When I encrypt the same string, using the same key, in the original app and the new app, the results are different.
The result string given by the original app is a subset of the result string given by my web service. The latter has additional characters at the end of the encrypted string.
Below is the code I am using. Please note that I did not develop this code nor do I understand it fully. Any thoughts on the difference in behaviour? Please help!!
Here is the code that gets the user input and calls the encryptor.
public void EncryptDomain(string EncryptValue, string outputDomainFile)
{
if (EncryptValue.Length > 0)
{
if ((outputDomainFile != null) && (outputDomainFile.Length > 0))
{
_outputDomainFile = outputDomainFile;
}
byte[] input = Encoding.UTF8.GetBytes(EncryptValue);
Transform(input, TransformType.ENCRYPT);
}
This is the encryptor code:
private byte[] Transform(byte[] input, TransformType transformType)
{
CryptoStream cryptoStream = null; // Stream used to encrypt
RijndaelManaged rijndael = null; // Rijndael provider
ICryptoTransform rijndaelTransform = null;// Encrypting object
FileStream fsIn = null; //input file
FileStream fsOut = null; //output file
MemoryStream memStream = null; // Stream to contain data
try
{
// Create the crypto objects
rijndael = new RijndaelManaged();
rijndael.Key = this._Key;
rijndael.IV = this._IV;
rijndael.Padding = PaddingMode.Zeros;
if (transformType == TransformType.ENCRYPT)
{
rijndaelTransform = rijndael.CreateEncryptor();
}
else
{
rijndaelTransform = rijndael.CreateDecryptor();
}
if ((input != null) && (input.Length > 0))
{
//memStream = new MemoryStream();
//string outputDomainFile =
FileStream fsOutDomain = new FileStream(_outputDomainFile,
FileMode.OpenOrCreate, FileAccess.Write);
cryptoStream = new CryptoStream(
fsOutDomain, rijndaelTransform, CryptoStreamMode.Write);
cryptoStream.Write(input, 0, input.Length);
cryptoStream.FlushFinalBlock();
//return memStream.ToArray();
return null;
}
return null;
}
catch (CryptographicException)
{
throw new CryptographicException("Password is invalid. Please verify once again.");
}
finally
{
if (rijndael != null) rijndael.Clear();
if (rijndaelTransform != null) rijndaelTransform.Dispose();
if (cryptoStream != null) cryptoStream.Close();
if (memStream != null) memStream.Close();
if (fsOut != null) fsOut.Close();
if (fsIn != null) fsIn.Close();
}
}
Code that sets up the IV values:
private void GenerateKey(string SecretPhrase)
{
// Initialize internal values
this._Key = new byte[24];
this._IV = new byte[16];
// Perform a hash operation using the phrase. This will
// generate a unique 32 character value to be used as the key.
byte[] bytePhrase = Encoding.ASCII.GetBytes(SecretPhrase);
SHA384Managed sha384 = new SHA384Managed();
sha384.ComputeHash(bytePhrase);
byte[] result = sha384.Hash;
// Transfer the first 24 characters of the hashed value to the key
// and the remaining 8 characters to the intialization vector.
for (int loop = 0; loop < 24; loop++) this._Key[loop] = result[loop];
for (int loop = 24; loop < 40; loop++) this._IV[loop - 24] = result[loop];
}
I would guess that's because of the IV (Initialisation Vector)
This is a classic mistake. Whether you generate an IV yourself or not Rijndael (AES) will provide one for you. The trick is to always save the IV (there's a getter on RijndaelManaged).
When you decrypt, you need to pass both the Key and IV.
If you're saving data to a file or database you can store the IV as a plain text. You can even pass the IV on wire (network, internet) as a plain text. The attacker wont be able(as far as I know) break your cipher based just on an IV. Passing or storing the IV is usually done by prefixing it in front of ciphertext or appending it at the end. (concatenating the two strings)
e.g.
CiphertextIV or IVCiphertext. (remember IV is in plaintext is it should be of a fixed length - making it easy to separate upon receiving for decryption or for database insertion)
So, if your Key is ABCDEFABCDEFABCD and your IV is ABCDEF0123456789
and this plaintext:
'this is some secrect text' (let's say) produces a cipher such as: abcd1234abcd00
You would transmit(or store) like it this: ABCDEF0123456789abcd1234abcd00
Related
I am trying to convert the following php code to C#:
$m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$key, json_encode($arParams), MCRYPT_MODE_ECB)));
What the documentation says:
m_params : A JSON array of data of
additional parameters
encrypted using the
Rijindael-256 algorithm and
encoded using a base64
algorithm.
What I've assumed?
Step 1: Create an array of params i.e. $arParams
For php its declared like:
$arParams = array(
'success_url' => 'http://google.com/new_success_url',
'fail_url' => 'http://google.com/new_fail_url',
'status_url' => 'http://google.com/new_status_url',
);
For C# I've declared it like this:
var additional_params = new object[]
{
new {"http://google.com/new_success_url"},
new {"http://google.com/new_fail_url"},
new {"http://google.com/new_status_url"},
};
Step 2: Encode to JSON string, I've used JsonConvert.SerializeObject(additional_params);
Step 3: Encrypt the result using RIJNDAEL-256 Algorithm using ECB (I've used CBC as well)
Step 4: Encode the result using base64. I've used Convert.ToBase64String(encrypted);
Step 5: Url encode the result. I've used HttpUtility.UrlEncode(base64String, Encoding.UTF8);
Step 6: Save the result in m_params
My current code looks like this:
var additional_params = new object[]
{
new {"http://google.com/new_success_url"},
new {"http://google.com/new_fail_url"},
new {"http://google.com/new_status_url"},
};
string m_params ="";
//converting to Json object additional params
var jsonEncoded = JsonConvert.SerializeObject(additional_params);
try
{
string original = jsonEncoded;
// Create a new instance of the RijndaelManaged
// class. This generates a new key and initialization
// vector (IV).
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
var final_Key = CreateMD5(payeer.m_key + payeer.m_orderid);
var rfc = CreateKey(final_Key);
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes(original, rfc[0], rfc[1]);
var base64String = Convert.ToBase64String(encrypted);
m_params = HttpUtility.UrlEncode(base64String, Encoding.UTF8);
// Decrypt the bytes to a string.
string roundtrip = DecryptStringFromBytes(encrypted, rfc[0], rfc[1]);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", original);
Console.WriteLine("Round Trip: {0}", roundtrip);
}
static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
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;
// Create an RijndaelManaged object
// with the specified key and IV.
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
rijAlg.Mode = CipherMode.ECB;
// rijAlg.KeySize = 256;
rijAlg.BlockSize = 256;
rijAlg.Padding = PaddingMode.PKCS7;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
public static string CreateMD5(string input)
{
// Use input string to calculate MD5 hash
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
// Convert the byte array to hexadecimal string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
sb.Append(hashBytes[i].ToString("X2"));
}
return sb.ToString();
}
}
public static dynamic 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))
{
var key = rfc2898DeriveBytes.GetBytes(32);
var IV = rfc2898DeriveBytes.GetBytes(16);
dynamic[] arr = new dynamic[2];
arr[0] = key;
arr[1] = IV;
return arr;
}
}
Its not giving the same output. Am I missing something??
As mentioned in President James K. Polk's comment, Rijndael with a block size of 256 bits is only supported in the .NET Framework, not in .NET Core. You did not specify the version you are running, but since you use a block size of 256 bits in the posted code (rijAlg.BlockSize = 256;), I assume you are running .NET Framework (otherwise, you need to apply a third party library that supports Rijndael with a block size of 256 bits, such as BouncyCastle/C#).
Both codes use a different padding. mcrypt applies Zero padding by default, the C# code explicitly uses PKCS7 padding (which is also the C# default). So that the C# code provides the same result as the PHP code, it is necessary to switch to Zero padding in the C# code (it should be noted that Zero padding is unreliable, unlike PKCS7 padding).
When additional_params is instantiated (which, by the way, does not compile on my machine), the variable names are missing, so they are also missing in the serialization. An anonymous type could be used instead. Also, note that json_encode() escapes the slash (/) by default, i.e. converts it to a \/, which has to be done manually in the C# code, e.g. with Replace("/", "\\/"). One possible implementation of the JSON serialization is:
using Newtonsoft.Json;
...
var additionalParams = new
{
success_url = "http://google.com/new_success_url",
fail_url = "http://google.com/new_fail_url",
status_url = "http://google.com/new_status_url"
};
string jsonEncoded = JsonConvert.SerializeObject(additionalParams).Replace("/", "\\/");
In the PHP code, the key is derived from a password using the MD5 digest. By default, md5() returns the result as a hexadecimal string, which converts the 16 bytes hash into a 32 bytes value that is applied as the key, so that AES-256 is used. PHP represents the hexadecimal digits with lowercase letters, which must also be implemented accordingly in the C# code, e.g.:
using System;
using System.Text;
using System.Security.Cryptography;
...
MD5 md5 = MD5.Create();
string password = "My password"; // test password
byte[] passwordHash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
string passwordHashHex = BitConverter.ToString(passwordHash).Replace("-", "").ToLower(); // convert byte array to lowercase hex string as in PHP
byte[] key = Encoding.UTF8.GetBytes(passwordHashHex);
where the conversion of the byte array to the hexadecimal string is done with BitConverter, see here.
A possible implementation for the encryption is:
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Security.Cryptography;
...
byte[] encrypted = null;
using (RijndaelManaged rijndael = new RijndaelManaged())
{
rijndael.Key = key;
rijndael.Mode = CipherMode.ECB; // default: CBC
rijndael.BlockSize = 256; // default: 128
rijndael.Padding = PaddingMode.Zeros; // default: PKCS7
ICryptoTransform encryptor = rijndael.CreateEncryptor(rijndael.Key, null);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(jsonEncoded);
}
encrypted = msEncrypt.ToArray();
}
}
}
string base64String = Convert.ToBase64String(encrypted);
string m_params = HttpUtility.UrlEncode(base64String, Encoding.UTF8);
Console.WriteLine(m_params);
where this code with the used test password gives the following result:
C3pldgsLDSqfG28cbt%2fv0uiBNQT6cWn86iRwg%2bv2blTzR7Lsnra%2b2Ok35Ex9f9UbG%2bjhKgITUQ8kO3DrIrWUQWirzYzwGBucHNRThADf60rGUIBDdjZ2kOIhDVXUzlMsZtBvYIgFoIqFJXCbhZq9GGnKtABUOa5pcmIYeUn%2b%2fqG1mdtJenP5vt8n0eTxsAd6CFc1%2bguR0wZx%2fEZAMsBBRw%3d%3d
in accordance with the result of the following PHP code:
$key = md5('My password'); // test password
$arParams = array(
'success_url' => 'http://google.com/new_success_url',
'fail_url' => 'http://google.com/new_fail_url',
'status_url' => 'http://google.com/new_status_url',
);
$m_params = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$key, json_encode($arParams), MCRYPT_MODE_ECB)));
print($m_params . "\n");
Note that C# uses lowercase letters for the url encoding, while PHP uses uppercase letters, which represents the same url encoding, see RFC 3986, sec. 2.1. If the C# code should nevertheless also apply uppercase letters for the url encoding, this can easily be achieved using regular expressions, see e.g. here.
A few remarks regarding security:
The PHP code applies the insecure ECB mode. For security reasons, a mode with an IV should be used, e.g. CBC or GCM. The latter provides implicit authenticated encryption. The IV is randomly generated for each encryption, is not secret and is sent to the recipient along with the ciphertext (usually prepended).
MD5 as a key derivation function (KDF) is also insecure. Here, a reliable KDF should be used, e.g. PBKDF2.
In addition, using the hexadecimal string as the key weakens the same, since each byte is reduced to the 16 values of the hexadecimal number system. More secure is the use of the binary data generated by the KDF, so that each byte can take 256 different values.
mcrypt is deprecated. A possible alternative is openssl.
I'm trying to create Aes 256bit Encryption with key in login screen. I need a large encrypted string as i'm using 256bit But it result in small encrypted string.I have checked many samples But all are for Windows desktop application not for windows Phone application. Please help regarding this.
This is my code
namespace SampleEncription
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
byte[] encryptedPassword;
// Create a new instance of the RijndaelManaged
// class. This generates a new key and initialization
// vector (IV).
using (var algorithm = new AesManaged())
{
algorithm.KeySize = 256;
algorithm.BlockSize = 128;
// Encrypt the string to an array of bytes.
encryptedPassword = Cryptology.EncryptStringToBytes("Password", algorithm.Key, algorithm.IV);
//string chars = encryptedPassword.Aggregate(string.Empty, (current, b) => current + b.ToString());
string chars = System.Convert.ToBase64String(encryptedPassword);
Debug.WriteLine(chars);
}
}
}
}
one another class named cryptology:
namespace SampleEncription
{
class Cryptology
{
private const string Salt = "603deb1015ca71be2b73aef0857d7781";
private const int SizeOfBuffer = 1024 * 8;
internal static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iv)
{
// Check arguments.
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("key");
}
byte[] encrypted;
// Create an RijndaelManaged object
// with the specified key and IV.
using (var rijAlg = new AesManaged())
{
rijAlg.Key = key;
rijAlg.IV = iv;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for encryption.
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
}
}
instead of
string chars = System.Convert.ToBase64String(encryptedPassword);
do this
Encoding.UTF8.GetString(encryptedPassword, 0, encryptedPassword.Length);
I think wp8 doesn't allow you to use System.Text.Encoding.Default.GetString, you can try to default it to UTF8, which i assume that the cipher text are all in latin characters..
You forgot to flush :)
You are calling encrypted = msEncrypt.ToArray(); before closing and therefore flushing the CryptoStream. As the final block needs to be padded, not all bytes will have been written. If you use a block cipher mode of encryption or an authenticated cipher, it is always required to flush. Only stream cipher modes of encryption may not require you to flush the stream as each bit can be encryption separately.
In your implementation, you should be able to just move msEncrypt.ToArray() below the using scope of the CryptoStream, if I'm not mistaken.
I'm writing a Windows app in C# which has to interact with a Mac app (written in Cocoa). This app encrypts files in AES with CBC (IV, a key, salt, HMAC). I don't know a lot about encryption but I think that's what it does. The Cocoa library we use is RNCryptor. They have a C# version which is what I'm using on the Windows side (with a few modifications, mainly to use byte[] instead of Strings).
Now text files are decrypted correctly, but other files (for example, a PNG file), end up corrupted (the correct file on the right, and the corrupted on the left, using UTF8 encoding, you can see there's a lot of diamonds with ?s):
I believe this is due to the encoding of the file, but I tried UTF8, Default, Unicode, ASCII... and the output files are always corrupted with different file sizes, being ASCII and the default encoding (UTF16 I believe) the closest in size.
This is the RNCryptor modified code I used:
public byte[] Decrypt (byte[] encryptedBase64, string password)
{
PayloadComponents components = this.unpackEncryptedBase64Data (encryptedBase64);
if (!this.hmacIsValid (components, password)) {
return null;
}
byte[] key = this.generateKey (components.salt, password);
byte[] plaintextBytes = new byte[0];
switch (this.aesMode) {
case AesMode.CTR:
// Yes, we are "encrypting" here. CTR uses the same code in both directions.
plaintextBytes = this.encryptAesCtrLittleEndianNoPadding(components.ciphertext, key, components.iv);
break;
case AesMode.CBC:
plaintextBytes = this.decryptAesCbcPkcs7(components.ciphertext, key, components.iv);
break;
}
return plaintextBytes;
}
private byte[] decryptAesCbcPkcs7 (byte[] encrypted, byte[] key, byte[] iv)
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var decryptor = aes.CreateDecryptor(key, iv);
string plaintext;
using (MemoryStream msDecrypt = new MemoryStream(encrypted))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
return Encoding.UTF8.GetBytes(plaintext);
}
private PayloadComponents unpackEncryptedBase64Data (byte[] encryptedBase64)
{
List<byte> binaryBytes = new List<byte>();
binaryBytes.AddRange (encryptedBase64);
PayloadComponents components;
int offset = 0;
components.schema = binaryBytes.GetRange(0, 1).ToArray();
offset++;
this.configureSettings ((Schema)binaryBytes [0]);
components.options = binaryBytes.GetRange (1, 1).ToArray();
offset++;
components.salt = binaryBytes.GetRange (offset, Cryptor.saltLength).ToArray();
offset += components.salt.Length;
components.hmacSalt = binaryBytes.GetRange(offset, Cryptor.saltLength).ToArray();
offset += components.hmacSalt.Length;
components.iv = binaryBytes.GetRange(offset, Cryptor.ivLength).ToArray();
offset += components.iv.Length;
components.headerLength = offset;
components.ciphertext = binaryBytes.GetRange (offset, binaryBytes.Count - Cryptor.hmac_length - components.headerLength).ToArray();
offset += components.ciphertext.Length;
components.hmac = binaryBytes.GetRange (offset, Cryptor.hmac_length).ToArray();
return components;
}
private bool hmacIsValid (PayloadComponents components, string password)
{
byte[] generatedHmac = this.generateHmac (components, password);
if (generatedHmac.Length != components.hmac.Length) {
return false;
}
for (int i = 0; i < components.hmac.Length; i++) {
if (generatedHmac[i] != components.hmac[i]) {
return false;
}
}
return true;
}
And this is my code decrypting and writing the file:
byte[] decryptedFile = this.decryptor.Decrypt(File.ReadAllBytes(filePath), password);
File.WriteAllBytes(filePath, decryptedFile);
What can be wrong here? Thanks in advance.
The problem is in your use of StreamReader when decrypting. StreamReader reads text (UTF-8 here), not arbitrary binary data. One solution would be to read the data into a MemoryStream, and use its ToArray() method to get the resulting byte[].
I have following code that uses AesCryptoServiceProvider for encrypting and decrypting. The iv and key used are same for both encryption and decryption. Still the decrypted value differ from the source string.
What need to be corrected to get the original value after decrypt?
This code is working when inputValue = valid128BitString. But when the inputString = “Test” I am getting the following exception Padding is invalid and cannot be removed.. How can we correct it?
UPDATED QUESTION
The following will do the trick based on #jbtule answer.
encyptedValue.IV = result.IV;
The IV value from encryption result changes. Suppose encryption is done in a separate process, how can we know the IV for decryption? Is there a way to make it constant or known?
Answer: Your other option is pass a IV in to Encrypt and assign it before you begin your crypto transform, instead of letting aesProvider generate a random one for you. – #Scott Chamberlain
aesProvider.IV = Convert.FromBase64String("4uy34C9sqOC9rbV4GD8jrA==");
Update: Refer How to apply padding for Base64. We can use UTF8 for encoding the source input and result output. The key and IV may remain in Base64.
Using Base64 for source input will cause issues with some values, for example, "MyTest" where length of string is not a multiple of 4
Relevant points:
To decrypt data that was encrypted using one of the SymmetricAlgorithm classes, you must set the Key property and IV property to the same values that were used for encryption.
SymmetricAlgorithm.IV Property: Information from the previous block is mixed into the process of encrypting the next block. Thus, the output of two identical plain text blocks is different. Because this technique uses the previous block to encrypt the next block, an initialization vector is needed to encrypt the first block of data. (As per SymmetricAlgorithm.IV Property MSDN article)
The valid Key sizes are: 128, 192, 256 bits (as per How many characters to create a byte array for my AES method?)
Main Program
class Program
{
static void Main(string[] args)
{
string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
string inputValue = valid128BitString;
string keyValue = valid128BitString;
string iv = valid128BitString;
byte[] byteValForString = Convert.FromBase64String(inputValue);
EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
EncryptResult encyptedValue = new EncryptResult();
encyptedValue.IV = iv;
encyptedValue.EncryptedMsg = result.EncryptedMsg;
string finalResult = Convert.ToBase64String(Aes128Utility.DecryptData(encyptedValue, keyValue));
Console.WriteLine(finalResult);
if (String.Equals(inputValue, finalResult))
{
Console.WriteLine("Match");
}
else
{
Console.WriteLine("Differ");
}
Console.ReadLine();
}
}
AES Utility
public static class Aes128Utility
{
private static byte[] key;
public static EncryptResult EncryptData(byte[] rawData, string strKey)
{
EncryptResult result = null;
if (key == null)
{
if (!String.IsNullOrEmpty(strKey))
{
key = Convert.FromBase64String((strKey));
result = Encrypt(rawData);
}
}
else
{
result = Encrypt(rawData);
}
return result;
}
public static byte[] DecryptData(EncryptResult encryptResult, string strKey)
{
byte[] origData = null;
if (key == null)
{
if (!String.IsNullOrEmpty(strKey))
{
key = Convert.FromBase64String(strKey);
origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
}
}
else
{
origData = Decrypt(Convert.FromBase64String(encryptResult.EncryptedMsg), Convert.FromBase64String(encryptResult.IV));
}
return origData;
}
private static EncryptResult Encrypt(byte[] rawData)
{
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = key;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream encStream = new CryptoStream(memStream, aesProvider.CreateEncryptor(), CryptoStreamMode.Write);
encStream.Write(rawData, 0, rawData.Length);
encStream.FlushFinalBlock();
EncryptResult encResult = new EncryptResult();
encResult.EncryptedMsg = Convert.ToBase64String(memStream.ToArray());
encResult.IV = Convert.ToBase64String(aesProvider.IV);
return encResult;
}
}
}
private static byte[] Decrypt(byte[] encryptedMsg, byte[] iv)
{
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = key;
aesProvider.IV = iv;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
using (MemoryStream memStream = new MemoryStream())
{
CryptoStream decStream = new CryptoStream(memStream, aesProvider.CreateDecryptor(), CryptoStreamMode.Write);
decStream.Write(encryptedMsg, 0, encryptedMsg.Length);
decStream.FlushFinalBlock();
return memStream.ToArray();
}
}
}
}
DTO Class
public class EncryptResult
{
public string EncryptedMsg { get; set; }
public string IV { get; set; }
}
References
How many characters to create a byte array for my AES method?
Specified key is not a valid size for this algorithm
Encryption with AES-256 and the Initialization Vector
Invalid length for a Base-64 char array
What's the difference between UTF8/UTF16 and Base64 in terms of encoding
It is easy to make implementation mistakes with cryptographic primitives, people do it all the time, it's best to use a high level library if you can.
I have a snippet that I try to keep reviewed and up to date, that works pretty close to what you're doing. It also does authentication on the cipher text, which I would recommend if there is anyway an adversary could send chosen ciphertext to your decryption implementation, there are a lot of side channel attacks related to modifying the ciphertext.
However, the problem you're having does not have any thing to do with padding, if your ciphertext doesn't matchup to your key and iv, and you didn't authenticate your iv and ciphertext, you'll typically get a padding error (if this is bubbled up a client it's called a padding oracle). You need to change your main statement to:
string valid128BitString = "AAECAwQFBgcICQoLDA0ODw==";
string inputValue = "Test";
string keyValue = valid128BitString;
byte[] byteValForString = Encoding.UTF8.GetBytes(inputValue);
EncryptResult result = Aes128Utility.EncryptData(byteValForString, keyValue);
EncryptResult encyptedValue = new EncryptResult();
encyptedValue.IV = result.IV; //<--Very Important
encyptedValue.EncryptedMsg = result.EncryptedMsg;
string finalResult =Encoding.UTF8.GetString(Aes128Utility.DecryptData(encyptedValue, keyValue));
So you use the same IV to decrypt as you did to encrypt.
Okay i'm a little bit stuck on how to solve this problem.
When a user registers. I want to send him a link so that he can verify hes email address.
But i have troubles generating the link.
I've already written the controller to accept the links with the correct keys. i only have no idea on how to generate the activation keys.
So when the user registers i'll send him a link by mail like this:
Your activation link is : http://site.com/user/verify?key=keyhere
Now i have created this method (called by the controller/action) to handle the key in the link:
public string Verify(string value)
{
String email = Decrypt(value);
user u = gebRep.GetUsers().WithEmail(email).SingleOrDefault();
if (u != null)
{
u.emailValid = true;
userReppository.Save();
}
return "Invallid validation value!";
}
Now my problem is I have no idea on how to encrypt and decrypt the email into some sort of key (url friendly) So i can mail it with the link and can use it to verify the email.
I need some kind of (not to complicated but secure) way to encrypt the email into a urlfriendly key.
Tyvm
You could use something like Rijndael encryption to encrypt the user's e-mail address as the verification key, then when they click the link you simply decrypt the verification code with your private encryption key to retrieve the e-mail address and activate the appropriate user. With this technique you would not need to store any extra data about the user for account activation and you'd still have an encrypted, very hard to guess value as the verification code.
Here's an example of Rijndael encryption with C#.
In case you aren't familiar with it, with Rijndael encryption you have a private key that only you know, which is used as the encryption key for your data. So long as you know the encryption key you can always decrypt whatever you have encrypted.
The additional beauty of this technique is that it also makes it easy to have an expiring link. Say for example you want a 24 hour password reset link, so you send the user a link with a verification code in it, but rather than storing data about the verification code and its expiration date in your system, encrypt the user's e-mail and an expiration date and send that as the verification code. So basically, you could encrypt a value like, 1272746267|14 where first part is the link expiration timestamp and the second value is the user's ID in the system. Upon decrypting and parsing out the timestamp you can make the decision right there whether or not the link is still valid. The only drawback I see to this approach is possibly the length of the encrypted string, but seeing as you're sending it in an e-mail that shouldn't matter too much.
Here's a quick example:
Key: a1b2c3d4e5f6h7i8
Value: 1272746267|14
Encrypted: wxtVC1u5Q7N9+FymCln3qA==
Decrypted: 1272746267|14
Be sure to throw a Url.Encode() on the encrypted value before linking it, as it can contain some unfriendly characters like slashes.
It may easier to not use any encryption and make things a bit more simple.
Create a new Guid for the link (and save this with the user) then just verify the user when the link is called
The way I would do this is simply generate a largish-random number for the "key" and then store a mapping in your database between activation key and email.
This is what I use. Short and easy.
private string GetNewValidationCode()
{
long i = 1;
foreach (byte b in Guid.NewGuid().ToByteArray())
{
i *= ((int)b + 1);
}
return string.Format("{0:x}", i - DateTime.Now.Ticks);
}
Result looks like this: 8e85a8a078884bbc
Encrypt, Decrypt using AES. Please find example below:
class AesExample
{
public static void Main()
{
try
{
string original = "Here is some data to encrypt!";
// Create a new instance of the AesManaged
// class. This generates a new key and initialization
// vector (IV).
using (AesManaged myAes = new AesManaged())
{
// Encrypt the string to an array of bytes.
string encrypted = EncryptPasswordAes(original, myAes.Key, myAes.IV);
// Decrypt the bytes to a string.
string roundtrip = DecryptPasswordAes(encrypted, myAes.Key, myAes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", original);
Console.WriteLine("Encrypted: {0}", encrypted);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
static string EncryptPasswordAes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
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("Key");
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return Convert.ToBase64String(encrypted);
}
static string DecryptPasswordAes(string encryptedString, byte[] Key, byte[] IV)
{
//Convert cipher text back to byte array
byte[] cipherText = Convert.FromBase64String(encryptedString);
// Byte[] cipherText = System.Text.Encoding.UTF8.GetBytes(encryptedString);
// Check arguments.
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("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}