I have a document that is saying to Encrypted string using AES256. According to my document with two value 10002:1486703720424 AND HashKey: hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF it will generate the result 1ltQFLRGNif73uCNzi0YEvBqLKiRgx6fWsk5e/GcTQc= but when i try to generate the result it is generating 6SKbqJAxbBrg4eU7r/B8gJoJEPg+KjMvGL5L7bfykUU= from my code. Can you please tell what i am doing mistakes. This is the first time when i doing encryption so i am little bit confuse to find the my mistakes.
string getHashKey1 = EncryptText("10002:1486703720424", "hpIw4SgN)TxJdoQj=GKo)p83$uHePgoF");
public string EncryptText(string input, string password)
{
string result = "";
try
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
result = Convert.ToBase64String(bytesEncrypted);
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return result;
}
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
try
{
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
}
catch (Exception ex)
{
ErrorLog errLog = new ErrorLog();
errLog.LogsWrite(ex, Path.GetDirectoryName(Application.ExecutablePath));
}
return encryptedBytes;
}
Found... They are using ECB as the cypher mode, so no IV. I won't comment on the "security" of this. The padding seems to be PKCS7 (the default of AES). The password is used "as is", simply encoded in UTF8 (or perhaps even ASCII) (so it must be 32 bytes long).
public static string EncryptText(string input, string password)
{
// Get the bytes of the string
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
using (MemoryStream ms = new MemoryStream())
{
using (Aes aes = Aes.Create())
{
aes.Key = passwordBytes;
aes.Mode = CipherMode.ECB;
// "zero" IV
aes.IV = new byte[16];
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
}
byte[] encryptedBytes = ms.ToArray();
return encryptedBytes;
}
}
#LukePark correctly made a tirade about this answer that is useful for the OP but useless in the greater world. I'll bold the correctly word. For this reason I'll explain what is "wrong" on the specification given to A. Goutam and what a "correct" specification must always contain.
A specification for encryption should always contain: the algorithm used (AES for example), the key size (if you say AES256 then clearly it is 256 bits), the block mode (CBC, ECB etc). Many block modes (CBC for example) require an IV vector. ECB has a lower security than other block modes (see for example https://crypto.stackexchange.com/questions/225/should-i-use-ecb-or-cbc-encryption-mode-for-my-block-cipher). If the IV is necessary, then the specification must contain it (or explain how it should be generated). The specification must contain the padding that should be used. The Padding.None should be used only if the data to be encrypted can be exactly subdivided in encryption blocks (so for example, with AES, PaddingMode.None is good only if the data is 16, 32, 64, 96, ... bytes). PaddingMode.Zeros is good only for text (and I wouldn't use it, because it will add '\0' at the end of the text). The other padding modes are good.
Often the key isn't used "as is", because for example it is a string. The specification should contain how the encryption key must be derived from the string key. SHA256 on the key is a weak solution. Normally a good solution is using a strong key derivation function, like Rfc2898DeriveBytes. If this function is used, the specification must contain the number of iteration and other informations about using Rfc2898DeriveBytes or similar functions. Clearly what encoding should be used for the key (and for the data to be encrypted, if it is a text) must be included (UTF8 is always a good idea).
I'll add that a good specification should contain some test cases. At least one test case should be of length smaller than the encryption block and at least one test case must be of length greater than the encryption block but smaller than two encryption blocks (or > 2 and < 3... some complete blocks and one non-complete block). In this way you are testing both the PaddingMode and the CipherMode (note the should/must: by testing something bigger than an encryption block plus an incomplete block you are already testing everything)
Related
I wrote code that decrypts the input string completely. But since this is an ECB mode, I wanted to somehow decrypt not the entire input text, but only a separate block of it.
As far as I understand, ECB AES encrypts in blocks of 8 bytes. How can I add the AES_Decrypt function to it so that it decrypts only the last 8 bytes of the input string, for example.
byte[] bytesToBeDecrypted = new byte[32];
byte[] 8_bytesToBeDecrypted = new byte[8]; // Only 8 bytes of bytesToBeDecrypted
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
string salt = "12345678";
Encoding unicode = Encoding.Unicode;
byte[] saltBytes = unicode.GetBytes(salt);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.Zeros;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 65535);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.ECB;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
You can likely just Seek the MemoryStream with a multiple of 16 bytes from the beginning (or apparently even 16 bytes from the last part of the stream) and then connect the CryptoStream to decrypt whatever is left. With CBC that would be a bit more tricky as you would have to set the IV to the previous ciphertext block, but with ECB this should be a breeze.
Notes:
Of course, you don't need to set the IV for the ECB mode, that should not be required.
Rijndael with a block size of 128 is actually AES. C#/.NET only supports a subset of Rijndael by the way: only 64 increments of the block size and key size are supported, if I remember correctly.
I'd still use aes = Aes.Create() rather than aes = new RijndaelManaged() as that leaves the choice of the AES implementation to the system, and it will probably choose the hardware accelerated one over the managed one.
I have been trying to implement proper IV practice in methods to encrypt and decrypt a UTF-8 string with AES which is then returned as a Base64 string. Using this question as a reference, I have prepended the generated IV to the byte array before the Base64 conversion. I'm having an issue where the decrypt method returns the UTF-8 string with exactly fifty characters of random junk (encryption artifacts?). I don't believe the issue is with the encryption because the decrypt method does consistently return the encrypted string. I think the problem is with one of the other conversion steps but I'm having trouble seeing where this might be coming from. Any help would be wildly appreciated.
Encrypt method
public static string EncryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = System.Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Encoding.UTF8.GetBytes(input);
// IV is the 16 byte AES Initialization Vector
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
{
using (var ms = new MemoryStream())
{
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(rawData, 0, rawData.Length);
cs.FlushFinalBlock();
}
byte[] encryptedData = ms.ToArray();
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
// now encode the whole thing as base 64
return System.Convert.ToBase64String(output);
}
}
}
}
Decrypt method
public static string DecryptString(string input, string key)
{
using (var aes = new AesCryptoServiceProvider())
{
aes.Key = Convert.FromBase64String(key);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] rawData = Convert.FromBase64String(input);
byte[] IV = new byte[16]; // aes.IV.Length should be 16
Array.Copy(rawData, IV, IV.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, IV), CryptoStreamMode.Write))
{
using (var binaryWriter = new BinaryWriter(cs))
{
binaryWriter.Write(rawData,IV.Length ,rawData.Length - IV.Length);
}
}
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
My test
static void Main(string[] args)
{
string payload = "My super secret string";
string key = "tR4mPn7mBQ8G6HWusyFnGk/gqdd/enWiUTr7YbhNrJg=";
Console.WriteLine(payload);
Console.WriteLine(key);
Console.WriteLine("");
string encrypted = EncryptString(payload, key);
Console.WriteLine(encrypted);
Console.WriteLine("");
string decrypted = DecryptString(encrypted, key);
Console.WriteLine(decrypted);
Console.WriteLine(decrypted.Length.ToString() + " " + encrypted.Length.ToString());
Console.ReadKey();
}
Edit to add - this is an example of the output:
�XQ��=F�]�D�?�My super secret string
You are writing the IV to the output twice in EncryptString. First you have:
ms.Write(aes.IV, 0, aes.IV.Length); // aes.IV.Length should be 16
which is the start of encryptedData. You then copy the IV and encryptedData (which already includes the IV) into a new byte array:
// this will hold the IV prepended to the encrypted data
byte[] output = new byte[aes.IV.Length + encryptedData.Length];
Array.Copy(aes.IV, output, aes.IV.Length); // save the iv
Array.Copy(encryptedData, 0, output, aes.IV.Length, encryptedData.Length); // save the data
This doubling of the IV is what is causing the extra bytes.
You don’t need to do the second copying. Just convert encryptedData to base 64 directly and return that:
return System.Convert.ToBase64String(encryptedData);
I have been requested to encrypt some data while talking to my partner's JAVA API, and he sent me the following details about encryption algorithm:
Algorithm : AES256
Key Size : 256 bits
Encryption Mode: CBC (16 bits blocks, PKCS5Padding with 0)
Output Type : Base-64
Password: 0xA8703827AE586460105696504327B7BB0806FEAE96BD664F89E36868FBB48E3D
IV: is a byte[16] with 0 values
I used the below code, but I didn't get a matched result with him:
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[16] { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.KeySize = 256;
aes.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public string EncryptText(string input, string password)
{
byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
string result = Convert.ToBase64String(bytesEncrypted);
return result;
}
So, when we tried to encrypt Hello, How are you?, I got a different result and I should get the same result he had as he will decrypt my sent data on his side and will process it. The given example should have this result: TJTojNoVgoqnhCj4uTv1jLBiZU7r+s/0Bm234bHU+S0=
I did some testing and now able to match your expected result.
2 changes to be done.
IV
IV is the easiest, as you said IV = 0, so set IV as follows:
aes.IV = new byte[16];
In AES, IV is 16 bytes. The above would create a byte array of 16 bytes with each value initialized to zero.
Key
The password you have given starts with "0x" - this essentially means that this is hexadecimal representation of the password string. I converted this password to byte array using this
string password = "A8703827AE586460105696504327B7BB0806FEAE96BD664F89E36868FBB48E3D";
Please note I removed the starting "0x" from the above
byte[] passwordBytes = StringToByteArray(password);
The above converts the hexadecimal password representation to a byte array.
In your AES_Encrypt method, directly assign this byte[] as the Key
aes.Key = passwordBytes;
Now, my result is TJTojNoVgoqnhCj4uTv1jLBiZU7r+s/0Bm234bHU+S0= which exactly matches with your expected output.
I found this article online and implemented a modified version of it.
public static byte[] Encrypt(byte[] input, byte[] iv)
{
var aes = new AesManaged();
aes.Key = StringToByteArray("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
aes.IV = StringToByteArray("00010001000000000000000000000000");
aes.KeySize = 128;
var encryptor = aes.CreateEncryptor();
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(input, 0, input.Length);
cs.Close();
}
return ms.ToArray();
}
}
public static byte[] StringToByteArray(string hex)
{
var NumberChars = hex.Length;
var bytes = new byte[NumberChars / 2];
for (var i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
Now the question I have is, here I am providing same IV and Key (obviously just for testing, in production I'm changing the IV each time I encrypt), but it's returning different encrypted bytes each time I encrypt the same input.
I looked up some posts and they said the output is supposed to be same for specific key/iv combination. Am I missing something here?
EDIT:
[TestMethod]
public void Encryption_returns_same_value_for_same_key_and_iv()
{
const string input = "my input";
var bytes = Encoding.UTF32.GetBytes(input);
var result = EncryptionManager.Encrypt(bytes, bytes);
var result2 = EncryptionManager.Encrypt(bytes, bytes);
Assert.AreEqual(result, result2);
}
This is how i'm calling the encrypt method
So after some discussion, the problem was actually part of the code which was not shown here. Indeed, the original code above always gave the same results and the Unit test should have passed (with additionally using SequenceEqual on the assertion). However, the aes.KeySize was changed in the code (by some colleagues) after setting the key, like this:
aes.Key = StringToByteArray("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
aes.IV = StringToByteArray("00010001000000000000000000000000");
aes.KeySize = 128;
However, as we found out by outputting the used key after setting the KeySize property using
Console.WriteLine("Used Key for Encryption: " + BitConverter.ToString(aes.Key));
the key changes to a random key after you modify the KeySize. That's why we kept getting different results. Sample outputs for calling the function with the same input vector:
Used Key for Encryption: C7-35-58-42-3A-2A-79-DE-0D-09-78-20-34-90-1F-EC
Ciphertext: E4-AA-A3-3B-01-CF-F0-C1-07-9A-0B-73-3E-70-C9-8A
Used Key for Encryption: 8A-95-E7-26-60-F9-CE-66-BA-A4-DE-D2-FA-70-AC-DE
Ciphertext: C5-E7-D3-32-38-21-54-25-86-61-70-CB-94-46-A6-37
Used Key for Encryption: A4-D7-01-8F-35-2B-7F-2D-E6-0A-A9-7F-95-42-71-D6
Ciphertext: F1-B2-75-64-D1-90-75-32-0D-CB-D9-AE-11-AE-DB-DD
The problem is solved by first setting the KeySize and then setting the Key property itself.
I'm new to encryption/decryption. I'm trying to decrypt an input string that is encrypted and comes out to 44 characters.
This is what I have so far but I keep getting "bad data" when it attempts to execute the "TransformFinalBlock" function.
public static String Decrypt(String input)
{
try{
byte[] inputArray = Convert.FromBase64String(input);
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
tripleDES.KeySize = 128;
tripleDES.Key = UTF8Encoding.UTF8.GetBytes("0123456789ABCDEF");
tripleDES.IV = UTF8Encoding.UTF8.GetBytes("ABCDEFGH");
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
ICryptoTransform transform = tripleDES.CreateDecryptor();
byte[] resultArray = transform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
return UTF8Encoding.UTF8.GetString(resultArray);
}
catch(Exception except){
Debug.WriteLine(except + "\n\n" + except.StackTrace);
return null;
}
}
If you use an IV, then you should use CipherMode.CBC. ECB does not use any IV.
In addition, your data is not padded at all, it contains exactly 32 bytes. To test decryption, it is common to try without padding first. That way you can determine by eye which padding is used by looking at the resulting plaintext.
The plain data is too corny to print here, so I won't.
I had a very similar issue and i fixed it by changing the PaddingMode to None
My CipherMode is ECB (Electronic code book).