I need to implement custom chaining mode for encryption. I need to use symmetric block cipher (AES, 3DES, DES, IDEA). The problem I encounter is, the libraries/wrappers I found do not allow it.
BouncyCastle has those as Enum: Mode = CipherMode.CBC, so I do not see - how I can use my own. System.Security.Cryptography seem to do same way.
Is there any .NET library or wrapper, that allows custom made chaining modes?
Right now only idea I have got is to encrypt every block using CBC with IV full of zero bits and implement my chaining mode on top of it, but it does not seems as good idea.
I don't know of any library that supports a chaining callback, it sort of goes against the black-box-ness of most cryptographic API.
The way to accomplish your goal is to encrypt using ECB, since that is "just apply the encryption algorithm to this data". For example, to do CBC:
private byte[] _iv;
private ICryptoTransform _encryptor;
private void EncryptBlock(byte[] input, byte[] output)
{
byte[] buf = (byte[])input.Clone();
for (int i = 0; i < buf.Length; i++)
{
buf[i] ^= _iv[i];
}
_encryptor.TransformBlock(buf, 0, buf.Length, output, 0);
Buffer.BlockCopy(out, 0, _iv, 0, output.Length);
}
(all sorts of error checking omitted)
Given somewhere you initialized things as
using (Aes aes = Aes.Create())
{
aes.Mode = CipherMode.ECB;
aes.Key = key;
_encryptor = aes.CreateEncryptor();
_decryptor = aes.CreateDecryptor();
}
(etc).
Related
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.
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.
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
Is there any way to encrypt a bytearray without using a stream??
In fact you don't need to use any streams at all to operate with bytes.
The only thing you need is to call TransformFinalBlock() method of ICryptoTransform which can be encryptor or decryptor of any algorithm derived from SymmetricAlgorithm class
public class CryptoProvider
{
private SymmetricAlgorithm _algorithm = new RijndaelManaged();
public byte[] EncryptData(byte[] data, string password)
{
GetKey(password);
ICryptoTransform encryptor = _algorithm.CreateEncryptor();
byte[] cryptoData = encryptor.TransformFinalBlock(data, 0, data.Length);
return cryptoData;
}
public byte[] DecryptData(byte[] cryptoData, string password)
{
GetKey(password);
ICryptoTransform decryptor = _algorithm.CreateDecryptor();
byte[] data = decryptor.TransformFinalBlock(cryptoData, 0, cryptoData.Length);
return data;
}
private void GetKey(string password)
{
byte[] salt = new byte[8];
byte[] passwordBytes = Encoding.ASCII.GetBytes(password);
int length = Math.Min(passwordBytes.Length, salt.Length);
for (int i = 0; i < length; i++)
salt[i] = passwordBytes[i];
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt);
_algorithm.Key = key.GetBytes(_algorithm.KeySize / 8);
_algorithm.IV = key.GetBytes(_algorithm.BlockSize / 8);
}
}
If you are concerned about disk I/O you can use a MemoryStream.
However, the RSACryptoServiceProvider class will operate on byte arrays. This class performs asymmetric encryption and decryption using an implementation of the RSA algorithm.
The examples here show how you can do this with byte arrays
Well, you could write your own encryption algorithms - but it's much easier just to use the built-in streaming API writing to a MemoryStream which you then convert to a byte array using ToArray.
Using the Cryptography Application Block of Microsofts Enterprise Library you can do this, but I agree with the others that you don't really get any benefits from not using a stream.
http://www.obviex.com/samples/Code.aspx?Source=EncryptionCS&Title=Symmetric%20Key%20Encryption&Lang=C%23
Use a block cypher and implement it yourself.
But this is almost certainly pointless since using a MemoryStream on the byte array should work just fine and will use a well tested implementation.
Implementing something yourself when you're talking about crypto is normally a bad idea.
Ok I'm trying to use the Win32 Crypto API in C++ to decrypt a string encrypted in C# (.NET 2) with the RijndaelManaged Class. But I'm having no luck at all i get jibberish or a bad data Win32 error code. All my keys, IV and salt match, I've looked in the watch for both test apps. I've spent all say looking at it and I'm officialy stuck.
Anyway here is the C#
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(GetPassPhrase(), salt, 1000);
RijndaelManaged rijndael = new RijndaelManaged();
rijndael.BlockSize = 128;
rijndael.KeySize = 256;
rijndael.Mode = CipherMode.CBC;
rijndael.Key = pdb.GetBytes(m_KeySize);
rijndael.IV = GetIV(iv);
ICryptoTransform encryptor = rijndael.CreateEncryptor();
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
Byte[] encryptedBytes = null;
Byte[] toBeEncrypted = UnicodeEncoding.Unicode.GetBytes(value);
csEncrypt.Write(toBeEncrypted, 0, toBeEncrypted.Length);
csEncrypt.FlushFinalBlock();
encryptedBytes = msEncrypt.ToArray();
The C++ to decrypt it is:
keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
keyBlob.hdr.reserved = 0;
keyBlob.hdr.aiKeyAlg = CALG_AES_256;
keyBlob.cbKeySize = KEY_SIZE;
keyBlob.rgbKeyData = &byKey[0];
if ( CryptImportKey( hProv, (const LPBYTE) &keyBlob, sizeof(BLOBHEADER) + sizeof(DWORD) + KEY_SIZE, 0, CRYPT_EXPORTABLE, &hKey ) )
{
if ( CryptSetKeyParam( hKey, KP_IV, (const BYTE *) &byIV, 0))
{
DWORD dwLen = iDestLen;
if ( CryptDecrypt( hKey, 0, TRUE, 0, pbyData, &dwLen))
{
if ( dwLen < (DWORD) *plOutSize)
{
memcpy_s(pbyOutput, *plOutSize, pbyData, dwLen);
*plOutSize = dwLen;
bRet = TRUE;
}
}
else
{
// Log
DWORD dwErr = ::GetLastError();
int y =0;
}
}
}
I'm calling CryptAcquireContext successfully and my C++ is executing fine. Can anyone spot the error in my ways. It's starting to depress me know :(
Ok my fault, I didn't include the Struct def for the keyblob in the C++ and it turns out you need a contigous block of data for the key with the header but I was using the MSDN example that had a pointer to the key data. Which is wrong!
I see that you are using CBC chaining mode to encrypt the plain text.
Are you sure you are using the same chaining mode to decrypt the cypher text?
(I am sorry. I am not able to understand that from the code)
There are a handful of things you should check, since some of the code (declarations etc) are missing:
Block size - this usually should be the same as key size, I think it might even be the default since you dont specify it on C++ side. Set it to 256 on C# side, I guess it best that you explicitly specify it in C++ too.
Padding - the managed classes have PKCS7 as their default padding, I think its the default for cryptoAPI functions too, but I'm not sure.
I assume that GetPassPhrase, GetIV etc give you he same keys you're using on the C++ side?
It's not clear how the encrypted data is passed between the programs, is it possible there is some kind of translation error? E.g. base64, URL encode, etc.