i have the following PHP decryption routine which is working flawlessly and need help converting it to c#. i have tried many approaches but none of them is working.
i have managed to match the hash function output between c# and php.
also matched the output of the conversion from and to base64.
PHP Code:
function decrypt($encrypted_txt, $secret_key, $secret_iv)
{
$encrypt_method = "AES-256-CBC";
// hash
$key = hash('sha256', $secret_key);
// iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
$output = openssl_decrypt(base64_decode($encrypted_txt), $encrypt_method, $key, 0, $iv);
return $output;
}
secret_key= "t-3zafRa";
secret_key_hash = "d03a4d94b29e7f55c80726f1152dcebc9f03f4c698470f72083af967cf786b6b";
the problem is that the key hash is a 64 bytes which is invalid for the AES-256 but i am not sure how it's working in php and how the openssl_decrypt php function is dealing with the keys.
i have also tried to pass the MD5 of the key hash but also failed to decrypt.
byte[] asciiBytes = ASCIIEncoding.ASCII.GetBytes(keyhash);
byte[] hashedBytes = MD5CryptoServiceProvider.Create().ComputeHash(asciiBytes);
string keymd5 = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower(); //To match with PHP MD5 output
C# Hashing function:
static string sha256(string randomString)
{
var crypt = new System.Security.Cryptography.SHA256Managed();
var hash = new System.Text.StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
foreach (byte theByte in crypto)
{
hash.Append(theByte.ToString("x2"));
}
return hash.ToString();
}
C# Decryption routine:
static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (key == null || key.Length <= 0)
throw new ArgumentNullException("key");
if (iv == null || iv.Length <= 0)
throw new ArgumentNullException("iv");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, Padding = PaddingMode.None, KeySize = 256, BlockSize = 128, Key = key, IV = iv };
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
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();
srDecrypt.Close();
}
}
}
return plaintext;
}
any help or ideas are highly appreciated.
openssl_decrypt simply takes as many bytes for the key as required for the algorithm. As your algorithm is "AES-256-CBC" is uses 32 bytes (256 bits), as AES-256 is defined to as AES with a 256 bit key (and 14 rounds, rather than 10 or 12).
The way PHP does this is either by adding 00 valued bytes to the right in case the key is too small, or - as in your case - by simply ignoring the bytes after the 32st one. That's not a good way to behave of any kind of cryptographic library, especially for a high level language like PHP, but the OpenSSL wrapper library does it anyway.
So you have to extract the first 32 bytes from the hex encoded key and use that as key in C# to be compatible. Using different hash functions is of course not going to work, MD5 and SHA-256 are entirely incompatible (by design). Of course, you now have 16 hex encoded bytes left, which means you are using AES-256 with 128 bit keys, leaving you with 128 bit security. And yes, you need to use PKCS#7 padding in C#.
Note that using CBC with a static IV is not secure. Using CBC mode for transport mode security is not secure. Using SHA-256 or any normal hash over a password or key with little entropy is not secure. Storing keys in strings is generally not secure.
Getting crypto working is hard enough; getting it secure is much harder and requires understanding what the heck you're doing in the first place. You need to start off with a good protocol for your specific use case (and that's skipping a few steps already).
Related
I am working on a project for secure file transfer which encrypts files using c# client on the customer side. i need to decrypt the files on server side using php and maybe phpseclib. The code here i copied from a msdn example. But i cant work out the decrypt function in php.
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = passwordBytes;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.KeySize = 256;
AES.BlockSize = 256;
AES.Mode = CipherMode.CBC;
AES.Padding = PaddingMode.Zeros;
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
using (CryptoStream cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
This is the php code which doesnt work:
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setKey($pw);
$aes->setKeyLength(256);
$aes->disablePadding();
$file = "enc.txt";
$fh = fopen($file, "r");
$contents = trim(fread($fh, filesize($file)));
fclose($fh);
//echo "Encoded: \n\n" . $contents;
$contents = $aes->decrypt($contents);
#$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
#$padding = $block - (strlen($clear) % $block);
#$dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $pw, base64_decode($contents), MCRYPT_MODE_CBC, $pw);
echo "Decoded: \n\n" . $contents;
Can someone help me fixing this or give me a hint what i do wrong?
No initialization vector used when decrypting. You need to send the initialization vector (IV) along with the data - your PHP code is never calling $aes->setIV from phpseclib, so it will never be able to decrypt the text because phpseclib uses an IV of all zeros if one is not set according to the docs. I would personally recommend generating a secure random IV from C# using RijndaelManaged.GenerateIV, but apparently it's considered acceptable to derive the IV from a PBKDF2 key. PBKDF2 (specified in RFC 2898) is the key-stretching algorithm Rfc2898DeriveBytes implements. Regardless, you need to re-produce the IV on the PHP side, whether that means transmitting the IV with the encrypted data (which is completely fine) or re-deriving the IV on the PHP side.
Using the password as the salt is a REALLY BAD IDEA. The salt needs to be of sufficient length and cryptographically randomly generated. Using the password as the salt completely defeats the point of having a salt. MSDN has some sample code that shows how to generate a cryptographically random salt in conjunction with using Rfc2898DeriveBytes, but the important part is here:
byte[] saltBytes = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
The salt must be transmitted with the encrypted data. You need to send the PBKDF2 salt bytes along with the IV bytes and encrypted data. phpseclib will need all of those to properly initialize itself and decrypt the data. You'll probably want to use phpseclib's setPassword to do this, like so:
$salt = ...; // get the salt to your PHP code somehow
$iv = ...; // get the IV to your PHP code
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setPassword($pw, 'pbkdf2' /* key extension algorithm */,
'sha1' /* hash algorithm */, $salt /* generated salt from C# */,
1000 /* number of iterations, must be same as C# code */,
256 / 8 /* key size in bytes, 256 bit key / 8 bits per byte */
);
$aes->setIV($iv);
Keep the other answers in mind about blocksize. 128 bits is the standard AES blocksize, so make sure both C# and phpseclib can function correctly with a larger blocksize, or just use the AES standard for both.
If you are trying to use AES set the block size to 128-bits, that is the only block size that is supported. Using a different block size means you are using Rijndael encryption which is not well supported cross platform.
AES supports multiple key sizes of 128, 192 and 256 bits. Sometimes there is confusion when using a Rijndael implementation to use AES encryption.
In the Java code I see AES.BlockSize = 256;. Technically, AES has a fixed block size of 128 bits. Rijndael supports variable block sizes but AES doesn't. If you want to make use of variable block sizes in PHP with phpseclib you'd need to do this:
$pw = "this_is_my_pw";
$aes = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CBC);
$aes->setKey($pw);
$aes->setKeyLength(256);
$aes->setBlockLength(256);
$aes->disablePadding();
Also, your key is 13 bytes long. AES keys need to be either 16 bytes (128 bits) long, 24 bytes (192 bits) long or 32 bytes (256 bits) long. idk what js lib you're using but phpseclib 1.0/2.0 null pads keys if they're not long enough. The newest version of phpseclib - currently under development - throws exceptions.
Or maybe you mean to be using a password based key derivation function? phpseclib provides two that can be utilized via setPassword() but if that were the case you'd need to know what method and parameters were being utilized by the js lib.
I am building a iPhone app which uses a c# web service. My iPhone app takes in some data and encrypts it and passes it to the web service. How do I decrypt the data in C#?
My iPhone app contains the following code:
NSString *pString = #"Some string to be encoded";
NSString *key = #"My encryption key";
NSData *pData = [pString dataUsingEncoding:NSUTF8StringEncoding];
pData = [pData AES256EncryptWithKey:key];
NSString *pID = [pData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];
EDIT: The data is already stored in the web service so I can't readily change the encryption approach. The C# application is not on the server so there is no possibility of compromising the key.
I have tried the following C# code to decrypt the data:
static string DecryptString(string encryptedText, string key)
{
byte[] encryptedString = Convert.FromBase64String(encryptedText);
byte[] encryptionKey = Encoding.UTF8.GetBytes(key.Substring(0, 32));
using (var provider = new AesCryptoServiceProvider())
{
provider.Mode = CipherMode.CBC;
provider.Padding = PaddingMode.PKCS7;
provider.Key = encryptionKey;
using (var ms = new MemoryStream(encryptedString))
{
// Read the first 16 bytes which is the IV.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
using (var decryptor = provider.CreateDecryptor())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
return sr.ReadToEnd();
}
}
}
}
}
}
However, I get the following exception:
System.Security.Cryptography.CryptographicException was unhandled
HResult=-2146233296 Message=Padding is invalid and cannot be
removed.
The encryptedText received by DecryptString is 80 bytes in length.
The sample ObjC code uses by default CBC modem, PKCS#7 padding and a default iv of 16 0x00 bytes.
The C# also uses CBC mode and PKCS#7 padding. The decryption code expects a 16-byte iv pre-pended to the encrypted data and that does not exist.
byte[] iv = new byte[16];
ms.Read(iv, 0, 16);
provider.IV = iv;
This needs to be changed so that iv is set to an array of 16 0x00 bytes and the ms.Read(iv, 0, 16) statement needs to be deleted so the decrypt function gets all of the encrypted data.
Notes:
Using a devault anything in encryption is a bad idea, always provide the correect length data.
Authentication of the encrypted data needs should be added so that it can be determined if there an incorrect key or the data has been tampered with.
There really should be a version number and a random IV used and prepended to the encrypted so you should really consider correcting this. This demonstrates why a version number generally needs to be provided and used.
RNCryptor covers the above issues.
The handling of the encryption key also needs to be considered so that is is as secure as necessary.
You need to first decode the base-64 encoded string to a byte[] - see Convert.FromBase64String(). Then you need to use the Aes class to decrypt it - there's an example on its documentation page.
I need to encrypt/decrypt some strings. I've build my wrapper class according to the msdn documentation but with some changes.
Since I want to encrypt/decrypt data with a given string/passphrase, I don't use AesManaged for creating a key. (The user should be able to encrypt/decrypt with a key he enters, and therefore I cannot use the key from AesManaged and I cannot save the key).
I instead create the key by using Rfc2898DeriveBytes (PBKDF2) with a given salt. The given salt is used since I do not store the key and I think because of this, the salt must be always the same.
I then create an IV, encrypt the given string and concatenate the IV and the encrypted string. This will then eventually got saved in a file. This means the IV gets save together with the encrypted data.
Questions:
Is it ok to store the IV together with the encrypted data?
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Is this encryption done using AES128 or AES256?
Will the IV be always 16 bytes, or can this change?
static void Main(string[] args)
{
const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
const string password = "m1Sup3rS3cre!Password";
string encrypted = EncryptString(stringToEncrypt, password);
string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);
Console.WriteLine("Original: {0}", stringToEncrypt);
Console.WriteLine("Round Trip: {0}", roundtrip);
Console.ReadLine();
}
static string EncryptString(string plainText, string password)
{
string encryptedString;
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
var encrypted = msEncrypt.ToArray();
encryptedString = Encoding.Default.GetString(aesAlg.IV);
encryptedString += Encoding.Default.GetString(encrypted);
}
}
}
return encryptedString;
}
static string DecryptStringFromBytes_Aes(string cipherText, string password)
{
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = PasswordAsByte(password);
aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();
using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
private static byte[] PasswordAsByte(string password)
{
byte[] salt = Encoding.Default.GetBytes("foobar42");
Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);
return passwordBytes.GetBytes(32);
}
No, this is not okay.
1) You're using Encoding.Default in various places. Don't do that - it means you're at the whim of the platform you're on. Always use an explicit encoding, ideally UTF-8 in most cases.
2) You're using Encoding.GetString / Encoding.GetBytes to convert arbitrary binary data to a string and back. That's almost bound to lose data. (It happened to succeed on my machine, but it really depends on the encoding - and it's fundamentally a bad idea.) Encoding is designed for data which is inherently text data, and you're just applying an encoding one way or the other. Your encrypted data is inherently binary data. Use Convert.ToBase64String and Convert.FromBase64String instead.
For your other questions:
Yes, it's okay to store the IV with the encrypted data, as far as I know.
You could use the same approach for the password: generate a different salt each time, and store that with the encrypted text. Not sure whether that's generally recommended or not, I'm afraid.
I believe you're controlling whether the key size is 128 or 256 bits, with your call to passwordBytes.GetBytes(32) - that's a 256-bit key, so it's AES256.
I believe the IV size for AES is always 16 bytes (128 bits)
Normally salt is used together with cryptographic hashing of say passwords to protect against dictionary attacks. To get the same kind of protection for symmetric encryption with AES you should use a random initialization vector. So when you encrypt create a random IV and prepend it to the message (in cleartext). When you decrypt get the IV from the encrypted message and use it to decrypt the message. Then the ciphertext of the same message encrypted with the same key will be different.
So, yes, it is OK to store the IV together with the encrypted data.
You do not need a different salt every time because the purpose of the random IV is similar in how salt makes dictionary attacks on hashes harder.
AES can use key sizes of 128, 192 or 256 bits so to use AES 256 you need a 256 bit key (32 bytes) which is what you use.
AES uses a 128 bit block which requires a 128 bit IV (or 16 bytes).
Is it ok to store the IV together with the encrypted data?
Yes, it is ok. Moreover, you're using AesManaged without explicit setting of Mode - it this case mode is CBC, and in CBC mode IV should preceed cyphertext.
Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
Rfc2898DeriveBytes is pretty standard way to derive key from text password. There is no need to reinvent way of deriving key from password, just use Rfc2898DeriveBytes as you're doing it now.
Is this encryption done using AES128 or AES256?
It is AES256 since you're using 32-byte password.
Will the IV be always 16byte, or can this change?
The size of the IV property must be the same as the BlockSize property divided by 8. So it is 16 for 128-bit blocks.
Somebody asked me how I would decrypt a given AES 256-bit encrypted string if I knew the secret key. I'm not very familiar with encryption, so I sat down to look into the problem.
I found this example on MSDN, and tried to modify it to do only the Decrypt:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
internal class AesExample
{
public static void Main()
{
var encryptedString = "U2FsdGVkX1/cHT8XuHCfpw0AV4jpaO8JfLqUeCRJqjY=";
var secret = "SPARKY";
// I know this is not the correct way to get my input byte arrays...
// Just illustrating that I DO need byte arrays.
var encryptedBytes = Encoding.UTF8.GetBytes(encryptedString);
var secretBytes = Encoding.UTF8.GetBytes(secret);
try
{
using (var aes = new AesManaged())
{
aes.Key = secretBytes;
// Decrypt the bytes to a string.
var decryptedString = Decrypt(encryptedBytes, aes.Key, aes.IV);
//Display the original data and the decrypted data.
Console.WriteLine("Encrypted: {0}", encryptedString);
Console.WriteLine("Decrypted: {0}", decryptedString);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
private static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
{
// Declare the string used to hold
// the decrypted text.
string plaintext;
// Create an AesManaged object
// with the specified key and IV.
using (var aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
// Create the streams used for decryption.
using (var msDecrypt = new MemoryStream(cipherText))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
}
Of course as soon as I hit the following line, a CryptographicExcetion is thrown with the message "Specified key is not a valid size for this algorithm."
==> aes.Key = secretBytes
Someone suggested taking a SHA1 hash of the secret and trimming that to 20 byes. I tried that, and I started getting a new CryptographicException with the message "Length of the data to decrypt is invalid."
So, I have a few questions:
1) Is this even possible given only the encrypted text and secret key?
2) If so, are them some base assumptions one would need to make, like the CipherMode? I was reading that the ECB mode doesn't have a initialization vector. That's why I ask.
3) What would I need to do to put the inputs (encrypted text and secret key) into the correct Byte[] format for the decryption to work?
Thanks!
You probably need more information to make this work. To answer your specific questions:
Yes, except that you don't have the secret key. "SPARKY" is not a valid AES key, as DavidH mentions, though passwords are routinely used to derive secret keys through what are called key derivation functions. You could try running your password through Rfc2898DeriveBytes (a popular KDF in .NET) to derive different AES keys that might work, but it too takes parameters that you apparently don't have. You could also try various SHA hash digests of your password, though again 20 bytes is not a valid AES key - you need a 16, 24 or 32 byte key.
If you don't have an IV, then yes, you'll have to assume the encryption uses ECB. (But note that in general you should never use ECB mode.)
Your encrypted string appears to be encoded using base64. Converting it to a byte array is simple enough in .NET using Convert.FromBase64String(encryptedString);.
This sounds like a fun exercise, but you're probably just going to end up frustrated without a bit more information.
AES key lengths are 128, 192, and 256 bit depending on the cipher you want to use. You must ensure that your string is the appropriate length of bytes.
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.