Encryption with same parameters need different results - c#

I am trying to encrypt a string passing the same parameters, but each time it produces the same result.
Currently, each time I call Encrypt("testing"), I am getting an output of "pvsLPLnR3fI=". However, I require the output to be different even if the parameters are the same. For example, calling Encrypt("testing") 3 times could produce output of:
pvsLPLnR3fI=
nR3fIasweds=
PHQHasfdevw=
The method I use to encrypt is as follows:
private const string mysecurityKey = "MyTestSampleKey";
public static string Encrypt(string TextToEncrypt)
{
byte[] MyEncryptedArray = UTF8Encoding.UTF8.GetBytes(TextToEncrypt);
MD5CryptoServiceProvider MyMD5CryptoService = new MD5CryptoServiceProvider();
byte[] MysecurityKeyArray = MyMD5CryptoService.ComputeHash(UTF8Encoding.UTF8.GetBytes(mysecurityKey));
MyMD5CryptoService.Clear();
var MyTripleDESCryptoService = new TripleDESCryptoServiceProvider();
MyTripleDESCryptoService.Key = MysecurityKeyArray;
MyTripleDESCryptoService.Mode = CipherMode.ECB;
MyTripleDESCryptoService.Padding = PaddingMode.PKCS7;
var MyCrytpoTransform = MyTripleDESCryptoService.CreateEncryptor();
byte[] MyresultArray = MyCrytpoTransform.TransformFinalBlock(MyEncryptedArray, 0, MyEncryptedArray.Length);
MyTripleDESCryptoService.Clear();
return Convert.ToBase64String(MyresultArray, 0, MyresultArray.Length);
}

You're getting the same result each time because there are no elements of the operation that are changing - you're using the same key and the same plaintext with the same algorithm. This is expected under the ECB mode of operation.
ECB is inherently insecure, so changing the mode to something like GCM (or CBC if you cannot) will both solve your original problem and improve the security immensely.
Be aware that MD5 and TripleDES are both poor choices for new software - consider using AES with a KDF that isn't a message digest, like Argon2 or PBKDF2.
I suggest you review the code examples in this repository for examples of secure, modern encryption.

Related

TripleDES TransformFinalBlock occasionally giving 'Bad Data. ' error

I have the following code:
public static string PerformEncryption(string text, string uniqueKey, bool encrypt = false)
{
byte[] textBytes = encrypt ? Encoding.UTF8.GetBytes(text) : Convert.FromBase64String(text);
byte[] resultArray;
var staticKey = Convert.FromBase64String(ConfigReader.SecretKey);
using (TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider())
{
tDes.Mode = CipherMode.ECB;
tDes.Padding = PaddingMode.PKCS7;
tDes.Key = GenerateTripleDesKey(uniqueKey, staticKey);     
CTransform = encrypt ? tDes.CreateEncryptor() : tDes.CreateDecryptor();
resultArray = CTransform.TransformFinalBlock(textBytes, 0, textBytes.Length);
tDes.Clear();
}
if (encrypt)
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
return Encoding.UTF8.GetString(resultArray);
}
private static byte[] GenerateTripleDesKey(string uniqueKey, byte[] staticKey)
{
byte[] keyArray;
using (SHA512CryptoServiceProvider hash = new SHA512CryptoServiceProvider())
keyArray = hash.ComputeHash(Encoding.UTF8.GetBytes(string.Format("{0}{1}", uniqueKey, staticKey)));
byte[] trimmedBytes = new byte[24];
Buffer.BlockCopy(keyArray, 0, trimmedBytes, 0, 24);
return trimmedBytes;
}
 
PerformEncryption is used as a helper method to perform encryption/decryption of a string. A secret key is also supplied for either operation.
It is used in a web API application that is consumed by a mobile app in Android & iOS devices.
Bad Data error is occurring with large portion of the users that are on Android, with a much smaller occurance of this error on iOS. Any tests I have conducted on similar mobile devices do not produce the issue.
Only way I can reproduce error is if I modify the string value in a unit test after its encrypted.
The web API uses Async/Await so I'm not sure if this has something to do with it?
Is there anything I miss with the code above that I have left out or is bad practice?
I don't have access to the raw request being sent to the server so I can't determine if the encrypted value has its content appended with dodgy characters from Android/iOS in the request??
My other thoughts are:
should I switch from using UTF8 getbytes() to ASCII equivalent helper class if that is causing issues with iOS and android environments
Should I just switch to using a diff algorithm completely like AES.
3DES should not be used for new code, instead use AES.
Do not use ECB mode, it is insecure, see ECB mode, scroll down to the Penguin. Instead use CBC mode with a random IV, just prefix the encrypted data with the IV for use in decryption.
PBKDF2 is more secure than a simple hash or even a salted hash. The main difference is iteration in order to take more time to calculate the key,100,000 iterations is common for PBKDF2.
UTF-8 is to be preferred over ASCII which is too limited.

how to pad data before encryption with Bouncy Castle

I'm trying to encrypt my data with AES algorithm with CBC mode. For this reason I use .Net Library 'Bouncy Castle'. I have not a background in crypto, so I'm trying to use it in a straightforward way. Here is my encrypt code
public byte[] encrypt(byte[] key, byte[] iv,byte[] data)
{
IBlockCipher engine=new AesFastEngine();
KeyParameter keyParam = new KeyParameter(key);
CbcBlockCipher cipher = new CbcBlockCipher(engine);
ICipherParameters parameters = new ParametersWithIV(keyParam, iv);
byte[] output=new byte[16+data.Length];
cipher.Init(true, parameters);
cipher.ProcessBlock(data, 0, output, data.Length);
//process output
byte[] cipherArray = new byte[data.Length];
/*
int k=0;
for (int i = 0; i < output.Length; i++)
{
if (output[i]!= 0)
{
cipherArray[k++] = output[i];
}
}
*/
return cipherArray;
}
When I try an input that is not a multiply of 16, I get an exception. When I pad the array to the right with a number of (16-length%16) with zeros on the left, I can get a result. But the result is a problem for me as well. It gives me a result like this:
[0][0][0][0[111][22][33][44][66][77][33][12][32][23][0][0][0][0][0]
zeros on the both left and right.
I thought it may be about my use of ProcessBlock(data, 0, output, data.Length) function. I use it with the assumption that output will be my ciphered text, but it seems that output should be longer than the input length. since I don't have a documentation about this function, I may be using it in a wrong way. Any help would be appreciated
Bouncy Castle will do the padding for you, to start you need to set up your cihper as:
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(engine), new Pkcs7Padding());
For the rest of your code to work would need to use cipher.GetOutputSize(data.Length) ProcessBytes,and DoFinal so the padding is added correctly.
byte[] output = new byte[cipher.GetOutputSize(data.Length)];
int len = cipher.ProcessBytes(data, 0, data.Length, output, 0);
cipher.DoFinal(output, len);
I have a simple example on of using AES-GCM in Bouncy Castle on CodeReview
AES-GCM adds authenticated encryption, but the basic principle of using the api is the same.
I also have a C# port of high level encryption framework, Kecyzar, that I used Bouncy Castle as the backend, although it's a harder example, the abstracted encryption code SymmetricStream is setup for using AES-CBC in BouncyAesKey
Typically one would use a standard padding algorithm to ensure that plaintext data is aligned with the block-size for a cipher.
You are currently hand-coding zero padding. This is not a great choice as it forbids the original data ending in a zero byte - how can you distinguish that from the padding?
I would recommend you use a standard padding, such as PKCS #7 padding. Note that this is often referred to as "PKCS #5 padding", as they are very similar.
You may wish to refer to this other SO question - Encrypt/Decrypt using Bouncy Castle in C# - for an example of using standard padding.

AES _Encryption in Mysql , Decryption in C#.Net

Mysql :
SELECT AES_ENCRYPT('Test','pass')
AES_ENCRYPT() and AES_DECRYPT() enable encryption and decryption of data using the official AES (Advanced Encryption Standard) algorithm, previously known as “Rijndael.” Encoding with a 128-bit key length is used, but you can extend it up to 256 bits by modifying the source. We chose 128 bits because it is much faster and it is secure enough for most purposes.
http://dev.mysql.com/doc/refman/5.5/en/encryption-functions.html#function_aes-encrypt
I was trying to convert that Encrypted string into Decryped Strig in C#.net but i don't get the results as i expect.
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx#Y0
C#
static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
In this method I pass ciphertext,Key value which i usedfrom Mysql and
Rijndael.Create().IV for byte[] IV
I use the code but i don't get expected result.
Review the code and comment Idk where made a mistake
What you are doing is following a road of pain. Either decrypt/encrypt on MySQL and use an encrypted connection to the database (if that matters) or encrypt/decrypt on your .NET application, storing the encrypted data in a suitable column.
Mixing AES implementations is prone to mistakes and things can break more easily if you change versions of .NET or MySQL.
Now, to know what exactly is wrong we need to know if the IV is compatible between MySQL and .NET, or else find out what is MySQL's implementation IV and supply that.
And the other potential source of problems is how you have generated the byte arrays (we are not seeing that in your example). You have to consider character encoding issues in generating the arrays if the key is textual.
In the comments of this MySQL docs link there is information about the missing parameters.
After a long hours, I found a solution to this issue.
Couple of FYI's:
MySQL as a default for AES_Encrypt uses 128 bit, with ECB mode, which does not require an IV.
What padding mode they use is not specified, but they do say they pad it. For padding I use PaddingMode.Zeros.
In C#, use AesManaged, not RijndaelManaged since that is not recommended anymore.
If your Key is longer than 128 bits (16 bytes), then use a function below to create the correct key size, since the default MySQL AES algorithm uses 128 bit keys.
Make sure you play around with the correct Encoding and know exactly what type of character encoding you will receive back when translating the bytes to characters.
For more info go here: https://forums.mysql.com/read.php?38,193084,195959#msg-195959
Code:
public static string DecryptAESStringFromBytes(byte[] encryptedText, byte[] key)
{
// Check arguments.
if ((encryptedText == null || encryptedText.Length <= 0) || (key == null || key.Length <= 0))
{
throw new ArgumentNullException("Missing arguments");
}
string decryptedText = null;
// Create an AES object with the specified key and IV.
using (AesManaged aesFactory = new AesManaged())
{
aesFactory.KeySize = 128;
aesFactory.Key = AESCreateKey(key, aesFactory.KeySize / 8);
aesFactory.IV = new byte[16];
aesFactory.BlockSize = 128;
aesFactory.Mode = CipherMode.ECB;
aesFactory.Padding = PaddingMode.Zeros;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesFactory.CreateDecryptor();
// Create the streams used for decryption.
using (MemoryStream stream = new MemoryStream())
{
using (CryptoStream decryptStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
decryptStream.Write(encryptedText, 0, encryptedText.Length);
}
decryptedText = Encoding.Default.GetString(stream.ToArray());
}
}
return decryptedText.Trim();
}
public static byte[] AESCreateKey(byte[] key, int keyLength)
{
// Create the real key with the given key length.
byte[] realkey = new byte[keyLength];
// XOR each byte of the Key given with the real key until there's nothing left.
// This allows for keys longer than our Key Length and pads short keys to the required length.
for (int i = 0; i < key.Length; i++)
{
realkey[i % keyLength] ^= key[i];
}
return realkey;
}
Here is some working code for achieving the same encryption via C# as MySQL:
public byte[] AESEncrypt(byte[] plaintext, byte[] key) {
/*
* Block Length: 128bit
* Block Mode: ECB
* Data Padding: Padded by bytes which Asc() equal for number of padded bytes (done automagically)
* Key Padding: 0x00 padded to multiple of 16 bytes
* IV: None
*/
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Key = key;
ICryptoTransform encryptor = aes.CreateEncryptor();
MemoryStream mem = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(mem, encryptor,
CryptoStreamMode.Write);
cryptStream.Write(plaintext, 0, plaintext.Length);
cryptStream.FlushFinalBlock();
byte[] cypher = mem.ToArray();
cryptStream.Close();
cryptStream = null;
encryptor.Dispose();
aes = null;
return cypher;
}
For details see MySQL Bug # 16713
EDIT:
Since the above is relying on officially non-documented information (though it is working) I would recommend to avoid it and use one of the options described in the answer from Vinko Vrsalovic .
If you run SELECT AES_ENCRYPT('Test','pass')
your are sending the pass over the network unencrypted so any one can unencrypted the data.
The AES_ENCRYPT is used to store data so if the database gets hacked your data is safe, not to transmit data.
if you want data encryption over the net work connect to your mysql server using the ssl socket

Signing a byte array of 128 bytes with RSA in C sharp

I am completely new to cryptography and I need to sign a byte array of 128 bytes with an RSA key i have generated with C sharp. The key must be 1024 bits.
I have found a few examples of how to use RSA with C sharp and the code I'm currently trying to use is:
public static void AssignParameter()
{
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "SpiderContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
rsa.KeySize = 1024;
}
public static string EncryptData(string data2Encrypt)
{
AssignParameter();
StreamReader reader = new StreamReader(path + "publickey.xml");
string publicOnlyKeyXML = reader.ReadToEnd();
rsa.FromXmlString(publicOnlyKeyXML);
reader.Close();
//read plaintext, encrypt it to ciphertext
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
return Convert.ToBase64String(cipherbytes);
}
This code works fine with small strings (and thus short byte arrays) but when I try this with a string of 128 characters I get an error saying:
CryptographicException was unhandled: Wrong length
(OK, it might not precisely say 'Wrong length', I get the error in danish, and that is 'Forkert længde' which directly translates to 'Wrong length').
Can anyone tell me how I can encrypt a byte array of 128 bytes with a RSA key of 1024 bits in C sharp?
Thanks in advance,
LordJesus
EDIT:
Ok, just to clarify things a bit: I have a message, from which i make a hash using SHA-256. This gives a 32 byte array. This array is padded using a custom padding, so it ends up being a 128 byte array. This padded hash should then be signed with my private key, so the receiver can use my public key to verify that the message received is the same as the message sent. Can this be done with a key of 1024 bits?
If you want to sign you do not want to encrypt. Signatures and encryption are distinct algorithms. It does not help that there is a well-known signature algorithm called RSA, and a well-known asymmetric encryption algorithm also called RSA, and that the signature algorithm was first presented (and still is in many places) as "you encrypt with the private key". This is just plain confusing.
In RSA encryption, the data to encrypt (with the public key) must be padded with what PKCS#1 (the RSA standard) describes as "Type 2 padding", and the result (which has the same length than the modulus) is then processed through the modular exponentiation which is at the core of RSA (at the core, but RSA is not only a modular exponentiation; the padding is very important for security).
When signing, the data to sign must be hashed, then the hash value is embedded in a structure which describes the hash function which was just used, and the encoded structure is itself padded with a "Type 1 padding" -- not the same padding than the padding for encryption, and that's important, too.
Either way, a normal RSA engine will perform the type 1 or type 2 padding itself, and most RSA signature engines will also handle themselves the structure which identifies the used hash function. A RSA signature engine such as RSACryptoServiceProvider can work either with SignHash(), which expects the hash value (the 32 bytes obtained from SHA-256, without any kind of encapsulating structure or type 1 padding -- RSACryptoServiceProvider handles that itself), or SignData(), which expects the data to be signed (the engine then does the hash computation too).
To sum up, if you do any kind of padding yourself, then you are doing it wrong. If you used Encrypt() to compute a signature, then you are doing it wrong, too.
The minimum key size for encrypting 128 bytes would be 1112 bits, when you are calling Encrypt with OAEP off. Note that setting the key size like this rsa.KeySize = 1024 won't help, you need to actually generate they key of the right size and use them.
This is what worked for me:
using System;
using System.IO;
using System.Security.Cryptography;
namespace SO6299460
{
class Program
{
static void Main()
{
GenerateKey();
string data2Encrypt = string.Empty.PadLeft(128,'$');
string encrypted = EncryptData(data2Encrypt);
string decrypted = DecryptData(encrypted);
Console.WriteLine(data2Encrypt);
Console.WriteLine(encrypted);
Console.WriteLine(decrypted);
}
private const string path = #"c:\";
public static void GenerateKey()
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1112);
string publickKey = rsa.ToXmlString(false);
string privateKey = rsa.ToXmlString(true);
WriteStringToFile(publickKey, path + "publickey.xml");
WriteStringToFile(privateKey, path + "privatekey.xml");
}
public static void WriteStringToFile(string value, string filename)
{
using (FileStream stream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(value);
writer.Flush();
stream.Flush();
}
}
public static string EncryptData(string data2Encrypt)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
StreamReader reader = new StreamReader(path + "publickey.xml");
string publicOnlyKeyXML = reader.ReadToEnd();
rsa.FromXmlString(publicOnlyKeyXML);
reader.Close();
//read plaintext, encrypt it to ciphertext
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
byte[] cipherbytes = rsa.Encrypt(plainbytes,false);
return Convert.ToBase64String(cipherbytes);
}
public static string DecryptData(string data2Decrypt)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
StreamReader reader = new StreamReader(path + "privatekey.xml");
string key = reader.ReadToEnd();
rsa.FromXmlString(key);
reader.Close();
byte[] plainbytes = rsa.Decrypt(Convert.FromBase64String(data2Decrypt), false);
return System.Text.Encoding.UTF8.GetString(plainbytes);
}
}
}
Note however, that I'm not using a crypto container, and thus, I don't need your AssignParameter, but if you need to use it, modifying the code should be easy enough.
If you ever need to encrypt large quantities of data (much larger than 128 bytes) this article has sample code on how to do this.
Apparently, according to this question — how to use RSA to encrypt files (huge data) in C# — RSA can only encrypt data shorter than its key length.
Bizarre. The MSDN docs for`RSACryptoServiceProvider.Encrypt() say that a CryptographicException may be thrown if the length of the rgb parameter is greater than the maximum allowed length.
Well. That seems odd, especially since there doesn't seem to be much in the way of documentation regarding said maximum.
A little further digging, under Remarks has this:
The following table describes the padding supported by different versions
of Microsoft Windows and the maximum length of rgb allowed by the different
combinations of operating systems and padding.
If you are running XP or later and you're using OAEP padding, then the limit is stated to be
Modulus size -2 -2*hLen, where hLen is the size of the hash
No idea what the "size of the hash" might be, since the docs, AFAICS, don't mention "hash" anywhere except in regards to digital signatures.
If you are running Windows 2000 or later with the "high encryption pack" installed (again, no idea how you find that out), then the limit is stated to be
Modulus size - 11. (11 bytes is the minimum padding possible.)
Otherwise (Windows 98, Millenium or Windows 2000 or later without the aforementioned "high encryption pack" then you get "Direct Encryption and OAEP padding not supported", where the limitation is
The maximum size allowed for a symmetric key.
Say...wait a second... RSA is an asymmetric algorithm, right?
Worthless documentation. Sheesh.
See http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.encrypt.aspx. The exception thrown is probably "The length of the rgb parameter is greater than the maximum allowed length."
Usually RSA encryption has padding, and since your encrypted data size goes to the key size, there is no space for padding. Try to use longer key or less data size to encrypt.
Do you real need the custom padding? If not you could just use RSACryptoServiceProvider.SignData Method

Generate authenticated CMSEnvelopedData Messages with bouncycastle

I am trying to encrypt data with a password and store it inside a ASN.1 encoded CMS message (using C# and BouncyCastle 1.4)
The code I have seems to have two problems:
the data does not seem to be signed with a HMAC, so when I tamper with the encodedData (by enabling the commented out line), the decryption still succeeds.
when I decrypt the data I have tampered with, I get beck corrupted plain text. However only a two blocks of plaintext data are corrupted. This seems to suggest that the encryption does not actually use CBC mode.
(edit: disregard the second point, this is exactly how CBC is supposed to work)
This is what I am testing with:
public void TestMethod1()
{
byte[] data = new byte[1024]; // plaintext: a list of zeroes
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
CmsPbeKey encryptionKey = new Pkcs5Scheme2PbeKey("foo", new byte[] { 1, 2, 3 }, 2048);
generator.AddPasswordRecipient(encryptionKey, CmsEnvelopedDataGenerator.Aes256Cbc);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(data);
CmsEnvelopedData envelopeData = generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
byte[] encodedData = envelopeData.GetEncoded();
// encodedData[500] = 10; // tamper with the data
RecipientID recipientID = new RecipientID();
CmsEnvelopedData decodedEnvelopeData = new CmsEnvelopedData(encodedData);
RecipientInformation recipient = decodedEnvelopeData.GetRecipientInfos().GetFirstRecipient(recipientID);
byte[] data2 = recipient.GetContent(encryptionKey);
CollectionAssert.AreEqual(data, data2);
}
What am I doing wrong? What would be the correct way to write this?
To add an HMAC to a CMS message, you would have to use a AuthenticatedData-structure.
I am not especially familiar with Bouncy Castle, but from a cursory look at the API, I would say that it does not support AuthenticatedData. In fact, it looks like it only supports SignedData for authentication.
So your options seems to be:
Use another library (or write your own code) to handle the AuthenticatedData-structure.
Calculate the HMAC and provide it in a non-standard way (in a proprietary Attribute or out-of-band).
Use SignedData with an RSA key pair instead.

Categories