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.
Related
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.
Here is the code used to encrypt in coldfusion
<cfset strBase64Value = encrypt(strValue,24 character key,AES) />
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580 which seems odd to me considering it is only uppercase and numbers.
What C# library should I use to properly decrypt it ?
Also looking at this information, it seems that by default it uses the UUEncode algorithm to encode.
Should I ask the encrypter to use Base64 as encoding parameter ?
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580
Then they are using "Hex", not the default "UUEncode". Either "hex" or "base64" is fine. As long as you both agree upon the encoding, it does not really matter.
You can use RijndaelManaged to decrypt the strings. However, the default encryption settings for ColdFusion and C# differ slightly. With the encrypt function:
"AES" is short for "AES/ECB/PKCS5Padding"
"ECB" mode does not use an IV
Key strings are always base64 encoded
NB: Despite the name difference, for the SUN provider, PKCS5Padding (CF/Java) corresponds to PaddingMode.PKCS7 (C#). As mentioned in this thread, the "... SUN provider in Java indicate[s] PKCS#5 where PKCS#7 should be used - "PKCS5Padding" should have been "PKCS7Padding". This is a legacy from the time that only 8 byte block ciphers such as (triple) DES symmetric cipher were available."
So you need to ensure your C# settings are adjusted to match. With that in mind, just decode the encrypted text from hex and the key string from base64. Using the slightly ugly example in the API, just adjust the algorithm settings to match those used by the encrypt() function:
Encrypt with ColdFusion
<cfscript>
plainText = "Nothing to see";
// 128 bit key base64 encoded
keyInBase64 = "Y25Aju8H2P5DR8mY6B0ezg==";
// "AES" is short for "AES/ECB/PKCS5Padding"
encryptedText = encrypt(plainText, keyInBase64, "AES", "hex");
WriteDump( encryptedText );
// result: 8889EDF02F181158AAD902AB86C63951
</cfscript>
Decrypt with C#
byte[] bytes = SomeMethodToConvertHexToBytes( encryptedText );
byte[] key = Convert.FromBase64String( keyInBase64 );
string decryptedText = null;
using (RijndaelManaged algorithm = new RijndaelManaged())
{
// initialize settings to match those used by CF
algorithm.Mode = CipherMode.ECB;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
ICryptoTransform decryptor = algorithm.CreateDecryptor();
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decryptedText = srDecrypt.ReadToEnd();
}
}
}
}
Console.WriteLine("Encrypted String: {0}", encryptedText);
Console.WriteLine("Decrypted String: {0}", decryptedText);
Keep in mind you can (and probably should) adjust the settings, such as using the more secure CBC mode instead of ECB. You just need to coordinate those changes with the CF developer.
If anyone had similar problem with JAVA I just implemented encryption and decryption of string previously encrypted/decrypted in coldfusion with "Hex" and "tripledes". Here is my code:
private static final String PADDING = "DESede/ECB/PKCS5Padding";
private static final String UTF_F8 = "UTF-8";
private static final String DE_SEDE = "DESede";
private String secretKey;
public String encrypt(String message) throws Exception {
secretKey = getSecretKey();
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key);
final byte[] plainTextBytes = message.getBytes();
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return Hex.encodeHexString(cipherText);
}
public String decrypt(String keyToDecrypt) throws Exception {
secretKey = getSecretKey();
byte[] message = DatatypeConverter.parseHexBinary(keyToDecrypt);
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher decipher = Cipher.getInstance(PADDING);
decipher.init(Cipher.DECRYPT_MODE, key);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, UTF_F8);
}
This is the first time that I have worked with AES encryption, and I am somewhat confused. I am encrypting some sensitive data (no passwords just names, address, id number etc). I found a nice example of how to implement AES into my application. I understand that I supply the Password, Salt, Hash Algorithm, and Password Iterations to make the Key. This then creates the cipher text. Couple of questions I have is why is the KeySize dived by 8? If I wanted someone else to decrypt this what do I need to give them? Should the password, salt and IV be different every time I encrypt something new, if that is the case how to I ensure that the other person I want to use this information is always aware of that?
public static string Encrypt(string PlainText, string Password, string Salt, string HashAlgorithm, int PasswordIterations, string InitialVector, int KeySize)
{
try
{
byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);
MemoryStream MemStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
cryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] CipherTextBytes = MemStream.ToArray();
MemStream.Close();
cryptoStream.Close();
MemStream.Dispose();
cryptoStream.Dispose();
Encryptor.Dispose();
return Convert.ToBase64String(CipherTextBytes);
}
catch (Exception ex)
{
throw ex;
}
}
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
The keysize is divided by 8 because it's typically specified as a number of bits, but is being stored in a byte array.
The values that you keep the same are subject to the design of your program. In practice, the IV should change every time, but should you choose, you could keep the key and salt constant.
I know that the IV is included in the output of the stream, so you don't have to include it explicitly for decryption (it will be read from the stream). You shouldn't have to include the salt either for a similar reason. I believe that the only value you'd need to decrypt the cyphertext is the key. I may not be correct about this. You can always write a decryptor and see what you need to pass to make it work.
KeySize is in bits. And as we know on any normal machine 1 byte = 8 bits, hence division.
And what you need to give someone to be able to decrypt the data? Key and IV (Initialization Vector) will be enough.
That said - how do you usually do this? You send the AES encrypted data along with Key and IV encrypted using some asymetric encryption algorithm. This gives you higher level of protection - only someone with private key will be able to read Key and IV and use them do decrypt the most interesting data. You use generated Key and IV of some significant length to make yourself safe.
Why to do this this way? Because asymetric encryption is slow (very slow and for a good reason - to be harder to break), but symetric encryption (like AES) gives you high security if used with long key and is at the same time pretty fast. Combining both you get performance and security at high level.
I'm trying to decrypt a string, but I'm getting the
Specified initialization vector (IV) does not match the block size for this algorithm.
I've been searching SO and the web for a while and I understand my IV is 32 bytes and should be 16 bytes, but I can't figure out how to achieve it. The string to get has been encrypted using AES/CBC/PKCS5Padding and my code (well, actually I've found it somewhere in the web) is
var btKey = Encoding.ASCII.GetBytes("7c6e1257d0e81ff55bda80cc904365ae");
var btIV = Encoding.ASCII.GetBytes("cf5e4620455cd7190fcb53ede874f1a8");
aesAlg.Key = btKey;
aesAlg.IV = btIV;
aesAlg.Padding = PaddingMode.PKCS7;
// Create a decrytor to perform the stream transform.
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(encodedTicketAsBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)){
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plainText = srDecrypt.ReadToEnd();
}
}
}
What I don't understand is the use of the aesAlg.Padding, to be honest I couldn't find yet an easy, to my understanding, example of this in C#.
Any help?,
Thanks!!
The key you have is almost certainly a bunch of hex values and not ascii characters. you are doing:
var btIV = Encoding.ASCII.GetBytes("cf5e4620455cd7190fcb53ede874f1a8");
which treats it like any other string and converts it to its binary ascii bytes. Those look like hex digits to me. Every 2 characters is a single byte value. You probably want something like
var btIV = new byte[] {0xcf,0x5e,0x46,0x20,0x45,0x5c,0xd7,0x19,0x0f,0xcb,0x53,0xed,0xe8,0x74,0xf1,0xa8};
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.