C# AES Rijndael - detecting invalid passwords - c#

I'm using Rijndael to encrypt some sensitive data in my program.
When the user enters an incorrect password, most of the time a CryptographicException is thrown with the message "Padding is invalid and cannot be removed.".
However, with very small probability, the CryptStream does not throw an exception with the wrong password, but instead gives back an incorrectly decrypted stream. In other words, it decrypts to garbage.
Any idea how to detect/prevent this? The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
But if there's an easier way, I'd love to hear it!

HMAC is what you need. It is exactly made for this purpose. It combines the key and the message (which in this case, will be your password) and hashes them in a way that it will ensure the authenticity and integrity of the content, as long as the hash function used is secure. You can attach the HMAC to the encrypted data, and it can be used later to validate if the decryption was made correctly.
HMAC, Wikipedia
System.Security.Cryptography.HMAC

Checksums are exactly for this purpose. Get a hash of your data before encrypting. Encrypt the data and put it along with the hash into storage. After decrypting, get the hash of the decrypted data and compare it with the former. If you use a crypto grade hash (i.e. SHA512) your data will be safe. After all, this is exactly what encrypted compression software does.
For ultimate security, you can encrypt both the hashes and data separately then decrypt and compare. If both data and hash decrypts to corrupted data, there is very minuscule chances that they will match.

To check if the password you are using is correct, you can use this code
Dim decryptedByteCount As Integer
Try
decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
Catch exp As System.Exception
Return "Password Not Correct"
End Try
in essence, check if an error message is generated during decryption.
I report all the decoding code below
Public Shared Function Decrypt(ByVal cipherText As String) As String
If System.Web.HttpContext.Current.Session("Crypto") = "" Then
HttpContext.Current.Response.Redirect("http://yoursite.com")
Else
If cipherText <> "" Then
'Setto la password per criptare il testo
Dim passPhrase As String = System.Web.HttpContext.Current.Session("Crypto")
'Ottieni lo stream completo di byte che rappresentano: [32 byte di Salt] + [32 byte di IV] + [n byte di testo cifrato]
Dim cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText)
'Ottieni i Salt bytes estraendo i primi 32 byte dai byte di testo cifrato forniti
Dim saltStringBytes = cipherTextBytesWithSaltAndIv.Take((Keysize)).ToArray
'Ottieni i IV byte estraendo i successivi 32 byte dai byte testo cifrato forniti.
Dim ivStringBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize)).Take((Keysize)).ToArray
'Ottieni i byte del testo cifrato effettivo rimuovendo i primi 64 byte dal testo cifrato.
Dim cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(((Keysize) * 2)).Take((cipherTextBytesWithSaltAndIv.Length - ((Keysize) * 2))).ToArray
Dim password = New Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)
Dim keyBytes = password.GetBytes((Keysize))
Dim symmetricKey = New RijndaelManaged
symmetricKey.BlockSize = 256
symmetricKey.Mode = CipherMode.CBC
symmetricKey.Padding = PaddingMode.PKCS7
Dim decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)
Dim memoryStream = New MemoryStream(cipherTextBytes)
Dim cryptoStream = New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
Dim plainTextBytes = New Byte((cipherTextBytes.Length) - 1) {}
Dim decryptedByteCount As Integer
Try
decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
Catch exp As System.Exception
Return "La password di Cryptazione non รจ corretta"
End Try
memoryStream.Close()
cryptoStream.Close()
Return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount)
Else
Decrypt = ""
End If
End If
End Function

Though I can agree somewhat with Teoman Soygul post about CRC/Hash there is one very important thing to note. Never encrypt the hash as this can make it easier to find the resulting key. Even without encrypting the hash you still gave them an easy way to test if they have successfully gained the correct password; however, let's assume that is already possible. Since I know what kind of data you encrypted, be it text, or serialized objects, or whatever, it's likely I can write code to recognize it.
That said, I've used derivations of the following code to encrypt/decrypt data:
static void Main()
{
byte[] test = Encrypt(Encoding.UTF8.GetBytes("Hello World!"), "My Product Name and/or whatever constant", "password");
Console.WriteLine(Convert.ToBase64String(test));
string plain = Encoding.UTF8.GetString(Decrypt(test, "My Product Name and/or whatever constant", "passwords"));
Console.WriteLine(plain);
}
public static byte[] Encrypt(byte[] data, string iv, string password)
{
using (RijndaelManaged m = new RijndaelManaged())
using (SHA256Managed h = new SHA256Managed())
{
m.KeySize = 256;
m.BlockSize = 256;
byte[] hash = h.ComputeHash(data);
byte[] salt = new byte[32];
new RNGCryptoServiceProvider().GetBytes(salt);
m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(hash, 0, hash.Length);
ms.Write(salt, 0, salt.Length);
using (CryptoStream cs = new CryptoStream(ms, m.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
}
public static byte[] Decrypt(byte[] data, string iv, string password)
{
using (MemoryStream ms = new MemoryStream(data, false))
using (RijndaelManaged m = new RijndaelManaged())
using (SHA256Managed h = new SHA256Managed())
{
try
{
m.KeySize = 256;
m.BlockSize = 256;
byte[] hash = new byte[32];
ms.Read(hash, 0, 32);
byte[] salt = new byte[32];
ms.Read(salt, 0, 32);
m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);
using (MemoryStream result = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, m.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] buffer = new byte[1024];
int len;
while ((len = cs.Read(buffer, 0, buffer.Length)) > 0)
result.Write(buffer, 0, len);
}
byte[] final = result.ToArray();
if (Convert.ToBase64String(hash) != Convert.ToBase64String(h.ComputeHash(final)))
throw new UnauthorizedAccessException();
return final;
}
}
catch
{
//never leak the exception type...
throw new UnauthorizedAccessException();
}
}
}

I like Can Gencer's answer; you cannot really verify a decryption without the HMAC.
But, if you have a very a very large plaintext, then the decrypting can be very expensive. You might do a ton of work just to find out that the password was invalid. It would be nice to be able to do a quick rejection of wrong passwords, without going through all that work. There is a way using the PKCS#5 PBKDF2. (standardized in RFC2898, which is accessible to your c# program in Rfc2898DeriveBytes).
Normally the data protocol calls for generation of the key from a password and salt using PBKDF2, at 1000 cycles or some specified number. Then maybe also (optionally) the initialization vector, via a contniuation of the same algorithm.
To implement the quick password check, generate two more bytes via the PBKDF2. If you don't generate and use an IV, then just generate 32 bytes and keep the last 2. Store or transmit this pair of bytes adjacent to your cryptotext. On the decrypting side, get the password, generate the key and (maybe throwaway) IV, then generate the 2 additional bytes, and check them against the stored data. If the pairs don't match you know you have a wrong password, without any decryption.
If they match, it is not a guarantee that the password is correct. You still need the HMAC of the full plaintext for that. But you can save yourself a ton of work, and maybe wall clock time, in most cases of "wrong password", and without compromising the security of the overall system.
ps: you wrote:
The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
Avoid putting plaintext into the cryptotext. It only exposes another attack vector, makes it easier for an attacker to eliminate wrong turns. The password verification thing I mentioned above is a different animal, does not expose this risk.

Public Sub decryptFile(ByVal input As String, ByVal output As String)
inputFile = New FileStream(input, FileMode.Open, FileAccess.Read)
outputFile = New FileStream(output, FileMode.OpenOrCreate, FileAccess.Write)
outputFile.SetLength(0)
Dim buffer(4096) As Byte
Dim bytesProcessed As Long = 0
Dim fileLength As Long = inputFile.Length
Dim bytesInCurrentBlock As Integer
Dim rijandael As New RijndaelManaged
Dim cryptoStream As CryptoStream = New CryptoStream(outputFile, rijandael.CreateDecryptor(encryptionKey, encryptionIV), CryptoStreamMode.Write)
While bytesProcessed < fileLength
bytesInCurrentBlock = inputFile.Read(buffer, 0, 4096)
cryptoStream.Write(buffer, 0, bytesInCurrentBlock)
bytesProcessed = bytesProcessed + CLng(bytesInCurrentBlock)
End While
Try
cryptoStream.Close() 'this will raise error if wrong password used
inputFile.Close()
outputFile.Close()
File.Delete(input)
success += 1
Catch ex As Exception
fail += 1
inputFile.Close()
outputFile.Close()
outputFile = Nothing
File.Delete(output)
End Try
I use that code to decrypt any file. Wrong password detected on cryptostream.close(). Catch this line as error when a wrong key is used to decrypt file. When error happens, just close the output stream and release it (set outputFile to Nothing), then delete output file. It's working for me.

Related

Rijndael PHP vs C# - Invalid KeySize in C# but not in PHP

I try to encrypt a string (json) with Rijndael in C# and come up with a string, which I can offer to a PHP web service. This web service in turn decodes the string using the IV and masterkey (known to them). I have to write the C# code that can talk to the PHP service, I do not control/own the PHP service.
The PHP code for encrypting is as follows:
function encrypt($plaintext) {
$masterkey = 'masterKeyOfLength29Characters';
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $masterkey, $iv);
$crypttext = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
return base64_encode($iv.$crypttext);
}
$param = array("key" => "value");
$encryptedString = rawurlencode(encrypt(json_encode($param)))
The code above I'll have to convert to C#, so I can encrypt my JSON and offer it to the PHP web service.
There are two problems. The first was with the masterkey length, the second (might be related) is with the rawurlencode of the encrypted data (hard for me to test at this point).
var masterkey = "masterKeyOfLength29Characters";
var data = EncryptData(json, masterkey);
// Some code to URL Encode the data, I haven't gotten as far to test this
// since I can't encrypt with the key used in PHP, so I can't call the service
// to test the encoded string from my C# code.
data = HttpUtility.UrlEncode(data);
data = data.Replace("+", "%20");
public static string EncryptData(string json, string encryptionKey) {
Rijndael rj = Rijndael.Create();
rj.Mode = CipherMode.CBC;
rj.Padding = PaddingMode.PKCS7;
rj.BlockSize = 256;
rj.KeySize = 256;
rj.Key = Encoding.UTF8.GetBytes(encryptionKey); // ERROR here
rj.GenerateIV();
var encryptedJSON = EncryptStringToBytes(json, rj.Key, rj.IV);
var r1 = Convert.ToBase64String(rj.IV);
var r2 = Convert.ToBase64String(encryptedJSON);
return r1 + r2;
}
The EncryptStringToBytes does some checks and uses this code (plucked from the many examples on the internet):
using (Rijndael rijAlg = Rijndael.Create()) {
// Basically I do the same here as above, and I could also generate
// the IV here, but then I'd had to return it too. I know I can clean this
// code up quite a bit, but I'd rather focus on getting it to work first ;)
rijAlg.Mode = CipherMode.CBC;
rijAlg.Padding = PaddingMode.PKCS7;
rijAlg.BlockSize = 256;
rijAlg.KeySize = 256;
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) {
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
The error I'll get:
Specified key is not a valid size for this algorithm.
So, the problems in short:
1) How come the PHP code accepts the key of length 29 in the Rijndael 256 (CBC mode), and my C# doesn't? I've played around with the Mode, added the Padding later, set the KeySize (was 256 default already), and I just can't see what I'm doing wrong here.
2) When I use a key of length 32, this one is accepted and my code works. I can also decrypt it in C# (but can't test this in PHP). I would like to solve problem 1, and then continue on problem 2, but maybe someone can give me some understanding here. The encrypted string contains 1 '=' in the IV, and 2x '==' (at the end) in the encrypted json. I've read about padding and such, but I was wondering why no '=' signs are visible in the PHP examples I received. Again, maybe after fixing problem 1 this won't be an issue.
Many thanks for reading and I hope I'm not being too stupid here. After a day of trying yesterday I kind of get the feeling I've tried many different approaches and non seem to work.
Just thought I'd add a tiny bit to what #artjom-b has said.
Firstly, it does work :-)
But in addition you need to change your
rj.Padding = PaddingMode.PKCS7
to use
rj.Padding = PaddingMode.Zeros
Also, technically, your two functions aren't returning the same thing. The PHP returns base 64 of two concatenated bits of binary data whereas the C# returns a concatenation of separate b64 strings. The result will be different in the second half of the returned string.
EDIT: The rough and ready decryption routine:
public string DecryptRijndael(byte[] cipherText, string password, byte[] iv)
{
var key = new byte[32];
Encoding.UTF8.GetBytes(password).CopyTo(key, 0);
var cipher = new RijndaelManaged();
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.None;
cipher.KeySize = 256;
cipher.BlockSize = 256;
cipher.Key = key;
cipher.IV = iv;
byte[] plain;
using (var decryptor = cipher.CreateDecryptor())
{
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
plain = ms.ToArray();
}
}
}
return Encoding.UTF8.GetString(plain);
}
NB: All the caveats and warnings from Artjom B still apply.
You're using an old version of PHP which happily accepts keys that have an invalid length. Rijndael supports key sizes of 16, 24 and 32 bytes and nothing inbetween. The mcrypt extension in PHP silently pads the key with 0x00 bytes up to the next valid key size which is 32 bytes. You will have to do the same thing in C#:
byte[] key = new byte[32];
byte[] password = Encoding.UTF8.GetBytes(encryptionKey);
Array.Copy(password, key, password.Length);
rj.Key = key;
Keep in mind that in order to provide some security a key must have high entropy. A password is not a key and therefore doesn't provide much entropy, because of the limited character set and possible use words. Always derive a key from the password with available derivation functions such as Argon2, scrypt, bcrypt or PBKDF2 with a high cost factor/iteration count and a random salt.
You should also add authentication to your ciphertexts. Otherwise, an attacker might change the ciphertext without you knowing it. This either done by using an authenticated mode like GCM/EAX or running HMAC over the ciphertext to produce the authentication tag.

PBKDF2 Python keys vs .NET Rfc2898

I am trying to write a Python module that will encrypt text that our existing .NET classes can decrypt. As far as I can tell, my code lines, up but it isn't decrypting (I get an 'Invalid padding length' error on the C# side). My pkcs7 code looks good, but research indicates that invalid keys could cause this same problem.
What's different between these two setups?
Python:
derived_key = PBKDF2(crm_key, salt, 256 / 8, iterations)
iv = PBKDF2(crm_key, salt, 128 / 8, iterations)
encoder = pkcs7.PKCS7Encoder()
cipher = AES.new(derived_key, AES.MODE_CBC, iv)
decoded = cipher.decrypt(encoded_secret)
#encode - just stepped so i could debug.
padded_secret = encoder.encode(secret) # 1
encodedtext = cipher.encrypt(padded_secret) # 2
based_secret = base64.b64encode(encodedtext) # 3
I thought that based_secret could get passed up to C# and decoded there. But it fails. The same encrypting c# code is:
var rfc = new Rfc2898DeriveBytes(key, saltBytes);
// create provider & encryptor
using (var cryptoProvider = new AesManaged())
{
// Set cryptoProvider parameters
cryptoProvider.BlockSize = cryptoProvider.LegalBlockSizes[0].MaxSize;
cryptoProvider.KeySize = cryptoProvider.LegalKeySizes[0].MaxSize;
cryptoProvider.Key = rfc.GetBytes(cryptoProvider.KeySize / 8);
cryptoProvider.IV = rfc.GetBytes(cryptoProvider.BlockSize / 8);
using (var encryptor = cryptoProvider.CreateEncryptor())
{
// Create a MemoryStream.
using (var memoryStream = new MemoryStream())
{
// Create a CryptoStream using the MemoryStream and the encryptor.
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
// Convert the passed string to a byte array.
var valueBytes = Encoding.UTF8.GetBytes(plainValue);
// Write the byte array to the crypto stream and flush it.
cryptoStream.Write(valueBytes, 0, valueBytes.Length);
cryptoStream.FlushFinalBlock();
// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
var encryptBytes = memoryStream.ToArray();
// Close the streams.
cryptoStream.Close();
memoryStream.Close();
// Return the encrypted buffer.
return Convert.ToBase64String(encryptBytes);
}
}
}
The Python pkcs7 implementation I'm using is:
https://gist.github.com/chrix2/4171336
First off, I verified that Rfc2898 and PBKDF2 are the same thing. Then, as stated above, the problem appears to be a .net ism. I found on msdn
that the implementation of GetBytes inside of Rfc2898DeriveBytes changes on each call, ie. it holds state. (see the remarks about halfway down the page)
Example in Python (pseudo output):
derived_key = PBKDF2(key, salt, 32, 1000)
iv = PBKDF2(key, salt, 16, 1000)
print(base64.b64encode(derived_key))
print(base64.b64encode(iv))
$123456789101112134==
$12345678==
Same(ish) code in .NET (again, pseudo output):
var rfc = new Rfc2898DeriveBytes(key, saltBytes);
using (var cryptoProvider = new AesManaged())
{
// Set cryptoProvider parameters
cryptoProvider.BlockSize = cryptoProvider.LegalBlockSizes[0].MaxSize;
cryptoProvider.KeySize = cryptoProvider.LegalKeySizes[0].MaxSize;
cryptoProvider.Key = rfc.GetBytes(cryptoProvider.KeySize / 8);
cryptoProvider.IV = rfc.GetBytes(cryptoProvider.BlockSize / 8);
}
Console.Writeline(Convert.ToBase64(cryptoProvider.Key));
Console.Writeline(Convert.ToBase64(cryptoProvider.IV));
$123456789101112134==
$600200300==
Subsequent calls to rfc.GetBytes always produces different results. MSDN says it compounds the key sizes on the calls. So if you call GetBytes(20), twice, it's the same as calling GetBytes(20+20) or GetBytes(40). Theoretically, this should just increase the size of the key, not completely change it.
There are some solutions to get around this issue, which could be generating a longer key on the first call, then slicing it into both a derived key AND an IV, or randomly generating an IV, appending it to the encoded message and peeling it off before decrypting it.
Slicing the python output produces the same results as .NET. It looks like this:
derived_key = PBKDF2(key, salt, 32, 1000)
iv = PBKDF2(key, salt, 32 + 16, 1000) # We need 16, but we're compensating for .NETs 'already called' awesomeness on the GetBytes method
split_key = iv[32:]
print(base64.b64encode(derived_key))
print(base64.b64encode(iv))
print(base64.b64encode(split_key))
$ 123456789101112134== # matches our derived key
$ 12345678== # doesn't match
$ 600200300== # matches. this is the base 64 encoded version of the tailing 16 bytes.
Enjoy,

ASP.NET wrong string length after Decryption

I am getting length of the string wrong after using the following Decryption Method.
public static string DecryptRJ256(string prm_key, string prm_iv, string prm_text_to_decrypt) {
string sEncryptedString = prm_text_to_decrypt;
RijndaelManaged myRijndael = new RijndaelManaged();
myRijndael.Padding = PaddingMode.Zeros;
myRijndael.Mode = CipherMode.CBC;
myRijndael.KeySize = 256;
myRijndael.BlockSize = 256;
byte[] key = Encoding.ASCII.GetBytes(prm_key);
byte[] IV = Encoding.ASCII.GetBytes(prm_iv);
ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV);
byte[] sEncrypted = Convert.FromBase64String(sEncryptedString);
byte[] fromEncrypt = new byte[sEncrypted.Length];
MemoryStream msDecrypt = new MemoryStream(sEncrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return (Encoding.ASCII.GetString(fromEncrypt));
}
For example:
string ID = "yUFYhclPyPubnhMZ+SHJb1wrt44pao3B82jdbL1ierM=";
string finalID = DecryptRJ256(sKy, sIV, ID);
Response.Write(finalID); \\200905410 (**this is correct**)
Response.Write(finalID.Length); \\32 (**this should be 9**)
What am I doing wrong?
You are using zero padding. This pads the message with zero bytes until it reaches the block size (32 bytes in your case). Since zero padding is ambiguous (can't distinguish between an input that ended with zero bytes and the padding), .net doesn't remove it automatically.
So you have two choices:
Use PKCS7 padding for both encryption and decryption (that's what I recommend)
Manually strip all terminal zero bytes from the decrypted plaintext.
Your crypto isn't good either:
Keys and IVs should be binary, not ASCII (use base64 encoding here)
Using ASCII on the plaintext silently corrupts unicode characters - Use utf-8 instead
You need a new random IV for each encryption call and need to read it back during decryption
You should add a MAC, else active attacks (such as padding oracles) can often break it.
Use TransformFinalBlock instead of those memory streams.
Why use Rijndael256 over AES?
When I compiled this with symmetric decryptor object with the current Key, that is without key and IV, I get this as finalID.
???hV?9-2O?o?????}yl?????N?W
exactly 32 characters.
Refining the key and IV would help. I am not sure, but hope this might help.

Padding error after partial reading final block

I am trying to test my cryptography code and keep getting the CyrptographicException "Padding is invalid and cannot be removed" when ever decrypt is disposed. I get a similar error if I try to read past the end of the decrypt stream, even though the CryptoStream.Read documentation indicates that this should not be an issue.
A simplified example:
const int DATA_SET_SIZES = 102399;
byte[] iv,
key,
startingData = new byte[DATA_SET_SIZES],
encryptedData = new byte[((DATA_SET_SIZES - 1) / 16 + 2) * 16],
endingData = new byte[DATA_SET_SIZES];//[((DATA_SET_SIZES - 1) / 16 + 1) * 16];
Random rand = new Random();
rand.NextBytes(startingData); //Get test data.
using (Rijndael cryptAlg = Rijndael.Create())
{
cryptAlg.Mode = CipherMode.CBC;
cryptAlg.Padding = PaddingMode.ISO10126;
iv = cryptAlg.IV; //Use random IV during test.
key = cryptAlg.Key; //Use random Key during test.
using (CryptoStream encrypt = new CryptoStream(new MemoryStream(encryptedData), cryptAlg.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
encrypt.Write(startingData, 0, startingData.Length);
encrypt.FlushFinalBlock();
}
using (CryptoStream decrypt = new CryptoStream(new MemoryStream(encryptedData), cryptAlg.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
int dataRecieved = decrypt.Read(endingData, 0, endingData.Length);
}
}
If I do one of the following then the exception goes away:
Change cryptAlg.Padding = PaddingMode.None after performing the encryption but before creating decrypt.
Change const int DATA_SET_SIZES = 102400 or any other multiple of the block size.
Do not read any data from the last block.
Am I doing something wrong or does the .NET implementation not recognize the end of the stream correctly?
Also, does anyone know why the encrypted data is 1 block longer then would be needed to store the encrypted data? What is in that block?
Your encryptedData buffer is too large. Take input 15 bytes, then you get 32 blocks of buffer back. Since you give the full buffer to the MemoryStream constructor, it will read up to the end of the stream. Block decryption will never fail, so the only thing that will fail is the padding. The last block probably only contains zero's, so the decrypted value is random instead of matching the padding format (most of the time).
Try: new byte[(DATA_SET_SIZES / 16 +1) * 16]

How to make this: J2ME encrypt C# decrypt And J2ME decrypt C# encrypt?

C#
string keystr = "0123456789abcdef0123456789abcdef";
string plainText = "www.bouncycastle.org";
RijndaelManaged crypto = new RijndaelManaged();
crypto.KeySize = 128;
crypto.Mode = CipherMode.CBC;
crypto.Padding = PaddingMode.PKCS7;
crypto.Key = keystr.ToCharArray().Select(c=>(byte)c).ToArray();
// get the IV and key for writing to a file
byte[] iv = crypto.IV;
byte[] key = crypto.Key;
// turn the message into bytes
// use UTF8 encoding to ensure that Java can read in the file properly
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText.ToCharArray());
// Encrypt the Text Message using AES (Rijndael) (Symmetric algorithm)
ICryptoTransform sse = crypto.CreateEncryptor();
MemoryStream encryptedFs = new MemoryStream();
CryptoStream cs = new CryptoStream(encryptedFs, sse, CryptoStreamMode.Write);
try
{
cs.Write(plainBytes, 0, plainBytes.Length);
cs.FlushFinalBlock();
encryptedFs.Position = 0;
string result = string.Empty;
for (int i = 0; i < encryptedFs.Length; i++)
{
int read = encryptedFs.ReadByte();
result += read.ToString("x2");
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
encryptedFs.Close();
cs.Close();
}
}
Java:
private String key = "0123456789abcdef0123456789abcdef";
private String plainText = "www.bouncycastle.org";
cipherText = performEncrypt(Hex.decode(key.getBytes()), plainText);
private byte[] performEncrypt(byte[] key, String plainText)
{
byte[] ptBytes = plainText.getBytes();
final RijndaelEngine rijndaelEngine = new RijndaelEngine();
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine));
String name = cipher.getUnderlyingCipher().getAlgorithmName();
message("Using " + name);
byte[]iv = new byte[16];
final KeyParameter keyParameter = new KeyParameter(key);
cipher.init(true, keyParameter);
byte[] rv = new byte[cipher.getOutputSize(ptBytes.length)];
int oLen = cipher.processBytes(ptBytes, 0, ptBytes.length, rv, 0);
try
{
cipher.doFinal(rv, oLen);
}
catch (CryptoException ce)
{
message("Ooops, encrypt exception");
status(ce.toString());
}
return rv;
}
C# produces: ff53bc51c0caf5de53ba850f7ba08b58345a89a51356d0e030ce1367606c5f08
java produces: 375c52fd202696dba679e57f612ee95e707ccb05aff368b62b2802d5fb685403
Can somebody help me to fix my code?
In the Java code, you do not use the IV.
I am not savvy enough in C# to help you directly, but I can give some information.
Rijndael, aka "the AES", encrypts blocks of 16 bytes. To encrypt a long message (e.g. your test message, when encoding, is 20 bytes long), Rijndael must be invoked several times, with some way to chain the invocations together (also, there is some "padding" to make sure that the input length is a multiple of 16). The CBC mode performs such chaining.
In CBC, each block of data is combined (bitwise XOR) with the previous encrypted block prior to being itself encrypted. Since the first block of data has no previous block, we add a new conventional "zero-th block" called the IV. The IV should be chosen as 16 random bytes. The decrypting party will need the IV. The IV needs not be secret (that's the difference between the IV and the key) so it is often transmitted along the message.
In your Java code, you do not specify the IV, you just create a variable called iv and do not use it. So the Rijndael implementation is on its own for that. Chances are that it generated a random IV. Similarly, you do not give an IV to the Rijndael implementation in the C# code. So it is quite plausible that there again a random IV was selected. But not the same than the one in the Java code, hence the distinct results.
(Note: you 20-byte input string is padded to 32 bytes. You give two "results" in hexadecimal, of length 32 bytes each. This is coherent but means that those results do not include the IV -- otherwise they would be 48-byte long.)
I think the algorithm is built in slighty different way and/or the salt key is interpered in different way.

Categories

Resources