I'm using this function to Encrypt/Decrypt data using AES because it looked simple and clean (googl'ed code)
public static string Encrypt(string toEncrypt)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73"); // 256-AES key
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
public static string Decrypt(string toDecrypt)
{
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73"); // AES-256 key
byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.CBC;
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return UTF8Encoding.UTF8.GetString(resultArray);
}
I'm trying to encrypt the data "test garbage" and thats what i receive back:
YfhyS3GE/liPCaXR0cMHfQ==
However, I tried the same key/phrase on a lot of online-aes encrypt/decrypt and all of them are returning
U2FsdGVkX184u0/vPgA/B0rxofp5Iuqm7hfn4+QZAhg=
Can anyone actually tell me whats wrong?
"3a8114db34d5623d4fd1ee0fb0ga7a73" is hex encoded 128 bit key not a utf8 encoded 256 bit key.
That said simple and clean doesn't necessarily mean correct. For example, the code your using does use a random IV, but doesn't include it in the wire format, you'll never be able to decrypt what you encrypt.
I have a cut and paste style simple code sample that I try to keep up to date and reviewed that uses authenticated encryption using AES:
Modern Examples of Symmetric Authenticated Encryption of a string. C#
First a few issues with your code. Apparently Google doesn't always return the best code on top.
You are getting a key through the UTF8 encoding, which is silly. This produces a very weak key:
// 256-AES key
byte[] keyArray = UTF8Encoding.UTF8.GetBytes("3a8114db34d5623d4fd1ee0fb0ga7a73");
You are using CBC mode but the IV is not (explicitly) set.
Then you compare to some online-aes encrypt/decrypt services and you see a difference. That's because they probably (hopefully) work different.
The main thing here is that your 2 methods are a match and you can round-trip your data. But a good encryption would use a different way to get Key and IV.
I'm not exactly sure why you see a different (smaller) length encrypted data but that's up to a whole list of settings : Key length, Padding mode etc.
Related
I'm attempting to replace PasswordDerivedBytes with Rfc2898DerivedBytes but I'm having a problem with the latter when getting back a unicode encoded result.
Take this code for example:
[TestMethod]
public void DerivedBytesTest()
{
string encrypted = "y4Ijqo9Ga/mHlFbLHDdDUkYZlyu7CHF4PVXGLnb8by7FAVtCgPLhFSiA9Et6hDac";
string key = "{00B3403A-3C29-4f26-A9CC-14C411EA8547}";
string salt = "gT5M07XB9hHl3l1s";
string expected = "4552065703414505";
string decrypted;
decrypted = Decrypt(encrypted, key, salt, true);
Assert.IsTrue(decrypted == expected); // Works
decrypted = Decrypt(encrypted, key, salt, false);
Assert.IsTrue(decrypted == expected); // Doesn't work, get wrong unicode characters in 24 character string
}
private string Decrypt(string encrypted, string key, string salt, bool legacy = false)
{
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] encryptedDataBytes = Convert.FromBase64String(encrypted);
byte[] saltBytes = encoding.GetBytes(salt);
RijndaelManaged encryption = new RijndaelManaged();
DeriveBytes secretKey;
if (legacy)
{
secretKey = new PasswordDeriveBytes(key, saltBytes) {IterationCount = 100};
encryption.Padding = PaddingMode.PKCS7;
}
else
{
secretKey = new Rfc2898DeriveBytes(key, saltBytes, 100);
encryption.Padding = PaddingMode.Zeros; // This is the only one that doesn't throw the "Padding is invalid and cannot be removed" exception, but gives me a non-ASCII result
}
ICryptoTransform decryptor = encryption.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
string decryptedText = "";
using (MemoryStream memoryStream = new MemoryStream(encryptedDataBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] bytes = new byte[encryptedDataBytes.Length];
int decryptedCount = cryptoStream.Read(bytes, 0, bytes.Length);
decryptedText = encoding.GetString(bytes, 0, decryptedCount);
if (!legacy)
{
// Something more to do with result?
}
}
}
return decryptedText;
}
I wonder if anyone can advise where I'm going wrong?
PasswordDeriveBytes is a badly implemented extension of PBKDF1, while Rfc2898DeriveBytes is the implementation of PBKDF2. Both derive a key from a password, but they are two different algorithms and therefore they derive two different results. As they are using cryptographically secure hashes underneath, there is no way to convert one to another.
If you can spare a few bytes of storage you could still derive the key using PKBDF1 and then encrypt that key using the result of PBKDF2. If the output size is identical you could even use XOR encryption for that (a one-time-pad) but AES would of course also work. So then the decryption becomes: calculate PBKDF2 result, decrypt data key, use data key to decrypt ciphertext.
Otherwise you will have to decrypt and then re-encrypt the result.
If you want to compare the decryption result then compare the resulting bytes; do not first convert it into a string. Using authenticated encryption or a MAC is highly advised so that a authentication tag can be validated instead. Just ignoring padding exceptions by using Zero Padding is not the way to go. These padding errors occur because the key is wrong.
Generic notes:
PasswordDeriveBytes should not be used for any amount of bytes > 20 bytes as the Mickeysoft extension of PBKDF1 is horribly insecure, even repeating bytes in the output (!). If you do the same for PBKDF2 then any adversary will have to do half the work that you have to do so that's not a good idea either.
The iteration count in the question is very low, but as you seem to use a highly random UID instead of a password that should be OK.
Why does this code return the weak key error?
static public byte[] TDESDecrypt(byte[] toDecrypt, byte[] key, CipherMode mode = CipherMode.ECB, PaddingMode padding = PaddingMode.None)
{
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = key;
tdes.Mode = mode;
tdes.Padding = padding;
ICryptoTransform cTransform = tdes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toDecrypt, 0, toDecrypt.Length);
tdes.Clear();
return resultArray;
}
When I try to execute the line "tdes.Key = key", I get the error
deriveSessionKeyIS System.Security.Cryptography.CryptographicException:
Specified key is a known weak key for TripleDES and cannot be used
Why? The key I'm trying is random, but one of the tested keys, for example, is FB13347FE570DC4FFB13347FE570DC4F. Where is the problem?
You can read in wikipedia for example about what is the weak key in cryptography. For triple DES there is a method (TripleDES.IsWeakKey) which checks triple DES key for weakness. In you case, key FB13347FE570DC4FFB13347FE570DC4F is symmetric in a sense that first 8 bytes of it are exactly equal last 8 bytes. That means if you encrypt something with that key, and then encrypt that encrypted info one more time - you will restore original text (because of how this concrete encryption algorithm works), which is obviously dangerous.
So in short .NET protects you from doing dangerous things resulting in cryptographic weakness. If you will use standard GenerateKey() function to generate key (or just don't set Key explicitly) - weak keys won't be generated.
A bit more information about why that key is weak for triple DES. 3DES is named like this because it essentially uses 3 keys and applies pure DES encryption\decryption with those keys 3 times. Each key is 8 bytes long, so 3DES key size is 8*3 = 24 bytes. However, algorithm also allows for first and third keys to be the same, and as such allows to use 16-byte keys (like in your example). In that case first half of those 16-bytes are used as a third key. This option provides less security but is still viable.
Now, when in your case first half and second half of your 16-bytes key are the same, so all three keys which will be used by 3DES are the same. Given that 3DES works like this:
DES encrypt with 3rd(DES Decrypt with 2nd(DES Encrypt with 1st(plaintext)))
You see that in your case you fall back to using simple DES, which defeats whole purpose of using 3DES in the first place.
It is a weak 3DES key because the additional 8-byte will again be repeated as the last 8-bits. Thus the 3DES encryption has reverted to DES and that is weak.
3DES does three operations, in the most common form of ede the data is first encrypted with the first 8-bytes of the key, then decrypted with the second 8-bytes and finally encrypted with the final 8-bytes (which in this case are the first 8-bytes). Note that after the first two operations the data is back to the original data thus the only encryption that is actually performed is the last encryption and that is 8-bytes which is a 56-bit key. That is a weak 3DES key.
I found this solution on MSDN Forum. This solution works perfectly with weak keys.
With the code from the forum I made this:
using System.Security.Cryptography;
using System.IO;
using System.Reflection;
static class MyDES
{
public static byte[] Encrypt(byte[] data, byte[] key, byte[] IV)
{
MemoryStream mStream = new MemoryStream();
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
CryptoStream cStream = new CryptoStream(mStream,
des.CreateWeakEncryptor(key, IV),
CryptoStreamMode.Write);
cStream.Write(data, 0, data.Length);
cStream.FlushFinalBlock();
byte[] ret = mStream.ToArray();
cStream.Close();
mStream.Close();
return ret;
}
public static byte[] Decrypt(byte[] data, byte[] key, byte[] IV)
{
MemoryStream msDecrypt = new MemoryStream(data);
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.None;
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
des.CreateWeakDecryptor(key, IV),
CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[data.Length];
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return fromEncrypt;
}
#region DESCryptoExtensions
public static ICryptoTransform CreateWeakEncryptor(this DESCryptoServiceProvider cryptoProvider, byte[] key, byte[] iv)
{
MethodInfo mi = cryptoProvider.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
object[] Par = { key, cryptoProvider.Mode, iv, cryptoProvider.FeedbackSize, 0 };
ICryptoTransform trans = mi.Invoke(cryptoProvider, Par) as ICryptoTransform;
return trans;
}
public static ICryptoTransform CreateWeakEncryptor(this DESCryptoServiceProvider cryptoProvider)
{
return CreateWeakEncryptor(cryptoProvider, cryptoProvider.Key, cryptoProvider.IV);
}
public static ICryptoTransform CreateWeakDecryptor(this DESCryptoServiceProvider cryptoProvider, byte[] key, byte[] iv)
{
return CreateWeakEncryptor(cryptoProvider, key, iv);
}
public static ICryptoTransform CreateWeakDecryptor(this DESCryptoServiceProvider cryptoProvider)
{
return CreateWeakDecryptor(cryptoProvider, cryptoProvider.Key, cryptoProvider.IV);
}
#endregion
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I try to convert this code to php, but i can't and always i get different result in C# and PHP
Here is my C# code for encrypt and decrypt :
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes("1234567812345678");
private const int keysize = 256;
private string pass = "sample";
public static string Encrypt(string plainText, string passPhrase)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string cipherText, string passPhrase)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
and this is my php code :
$iv = "1234567812345678";
$out = null;
$key = "sample";
foreach ($iv as $i) { $out .= chr(ord(substr($i,0,1))); }
$res = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_CBC, implode($out));
The keys are not the same. C# is extending the key with PasswordDeriveBytes which is a good method. PHP mcrypt is extending they key with nulls. You need the extended (256-bit) keys to be the same.
The padding is not the same. Unencrypted data needs to be a multiple of the block size (128-bits for AES) and if it isn't always that padding must to be added. C# is not specifying any padding and will expect the data to be a multiple of the block size (128-bits). PHP will by default add null padding which is non-standard and will not work for binary data. You need to add common padding, the standard is PKCS#7 (aka PKCS#5), See PKCS#7 padding. C# supports PKCS#5 but for mcrypt you will have to do it in your code (the mcrypt developers were Bozos and did not provide standard padding).
Rijndael supports multiple bock sizes, it is not clear what the C# default block size is. If what you want is AES (it should be) the block size needs to be 128-bits.
Given that the MSDN documentation does not specify defaults it is best to explicitly set the block size, key size, mode and padding.
Your PHP code will not run on PHP 5.6 as the key size is wrong, it must be 32 bytes.
Said that, on previous versions PHP was padding the key with \0's to reach the correct key length, but in C# you're creating derived bytes (what indeed is correct) to get enough bytes for your key, which ends in different keys used on C# and PHP.
As a proof, create a key with 32 bytes (32 chars) and use directly those 32 bytes as key, both in PHP and C#, in that way it should work.
But at the end you will need a common way to derive the bytes both on PHP and C# to finally have a consistent keying code, an example can be to use a SHA-256 hash to generate the key.
I am using a Java based configuration management tool called Zuul which supports encrypting sensitive configuration information using various encryption schemes.
I have configured it to use below scheme for my data
AES (Bouncy Castle)
Name: PBEWITHSHA256AND128BITAES-CBC-BC
Requirements: Bouncy Castle API and JCE Unlimited Strength Policy Files
Hashing Algorithm: SHA256
Hashing Iterations: 1000
Now when reading my configuration data back, I need to decrypt the information before I can use it and the documentation provides below information around this topic.
The encrypted values produced by Jasypt (and thus Zuul) are are prefixed with the salt (usually 8 or 16 bytes depending on the algorithm requirements). They are then Base64 encoded. Decrypting the results goes something like this:
Convert the Base64 string to bytes
Strip off the first 8 or 16 bytes as the salt
Keep the remaining bytes for the encrypted payload
Invoke the KDF function with the salt, iteration count and the password to create the secret key.
Use the secret key to decrypt the encrypted payload
More details here: Zull Encryption wiki
Based on above details, I have written below code (and my knowledge around security is very limited)
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, saltBytes, iterations);
byte[] keyBytes = key.GetBytes(16);
AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider();
aesAlg.KeySize = 256;
aesAlg.BlockSize = 128;
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
MemoryStream msDecrypt = new MemoryStream(encryptedBytes);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
StreamReader srDecrypt = new StreamReader(csDecrypt);
return srDecrypt.ReadToEnd();
}
I configured Zuul to use below password for the encryption
SimplePassword
And now I have an encrypted string given to me by Zuul and I need to decrypt it
p8C9hAHaoo0F25rMueT0+u0O6xYVpGIkjHmWqFJmTOvpV8+cipoDFIUnaOFF5ElQ
When I try to decrypt this string using above code, I get below exception
System.Security.Cryptography.CryptographicException : Padding is invalid and cannot be removed.
As I mentioned earlier, my knowledge around this topic is limited and I am not able to figure out if the information provided in the documentation is not enough, if I am doing something wrong while writing the decryption routine or should I be using bouncy castle for decryption as well.
Any help with this will be much appreciated.
According to Zuul documentation they are deriving both key and iv from the password/salt.
So you should derive 256+128 bits (i.e. 48 bytes), and use first 32 bytes as the key, and next 16 bytes as IV.
And this should be done in one operation, not as consequent calls to key.DeriveBytes.
I resorted to Bouncy Castle for decryption instead since that is used by Zuul as well.
Here is the code that works
public static string Decrypt(string cipher, string password)
{
const int saltLength = 16;
const int iterations = 1000;
const string algSpec = "AES/CBC/NoPadding";
const string algName = "PBEWITHSHA256AND128BITAES-CBC-BC";
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = cipherBytes.Take(saltLength).ToArray();
byte[] encryptedBytes = cipherBytes.Skip(saltLength).ToArray();
char[] passwordChars = password.ToCharArray();
Asn1Encodable defParams = PbeUtilities.GenerateAlgorithmParameters(algName, saltBytes, iterations);
IWrapper wrapper = WrapperUtilities.GetWrapper(algSpec);
ICipherParameters parameters = PbeUtilities.GenerateCipherParameters(algName, passwordChars, defParams);
wrapper.Init(false, parameters);
byte[] keyText = wrapper.Unwrap(encryptedBytes, 0, encryptedBytes.Length);
return Encoding.Default.GetString(keyText);
}
I have my pre generated AES key what i would like to use in C#. Can anybody point me to the right direction how to use pre generated AES key with RijndaelManaged object.
EDIT: I have the key in byte[] array and i need to encrypt a Stream.
I found these code samples online:
private static byte[] Decrypt(byte[] key, byte[] PGPkey)
{
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = key;
//rDel.Mode = CipherMode.ECB; // http://msdn.microsoft.com/en-us/library/system.security.cryptography.ciphermode.aspx
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(PGPkey, 0, PGPkey.Length);
return resultArray;
}
private static byte[] Encrypt(byte[] key, byte[] PGPkey)
{
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = key;
//rDel.Mode = CipherMode.ECB; // http://msdn.microsoft.com/en-us/library/system.security.cryptography.ciphermode.aspx
rDel.Padding = PaddingMode.PKCS7; // better lang support
ICryptoTransform cTransform = rDel.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(PGPkey, 0, PGPkey.Length);
return resultArray;
}
Im not getting any errors but after decryption the byte array is not the same as it was before going to the ecryption.
EDIT: I think i got it working, had to set the rDel.Mode = CipherMode.ECB;
If it worked when setting it to ECB mode, that is becuase it was using CBC mode before, which uses a randomly generated Initialization Vector when encrypting. Initialization Vectors randomize the cipher text so two identical peiced of data, encrypted with the same key, dont produce the same cipher text. You can grab the byte[] RijndaelManagedInstance.IV property and store that with your cipher text. Then when decrypting, set the same property to the Initialization Vector used to encrypt, and then you should recieve the same plain text after decryption.
You need the key as a byte[]. Then you assign it to the Key property.
For encrypting using the autogenerated IV is fine. But you need to store it with your encrypted data and put it into the decryptor to decrypt your data again.
Don't use ECB, CBC is a much better fit for your use.