I'm trying to convert this C# code to Python (2.5, GAE). The problem is that the encrypted string from the python script is different each time the encryption (on the same string) is run.
string Encrypt(string textToEncrypt, string passphrase)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
rijndaelCipher.KeySize = 128;
rijndaelCipher.BlockSize = 128;
byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase);
byte[] keyBytes = new byte[16];
int len = pwdBytes.Length;
if (len > keyBytes.Length)
{
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
rijndaelCipher.IV = new byte[16];
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt);
return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length));
}
Python code: (PKCS7Encoder: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html)
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encryptor = AES.new(key, mode, iv)
encoder = PKCS7Encoder()
def function(self):
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
The C# code is inherited. Python code must be encrypted and decrypted the same way so that the C# code can decode the value correctly.
Note: I am a noob at python :)
Edit: sorry. should have made the distinction that there was a function being called.
Thanks!
Your C# code is invalid.
The Encrypt function takes in the passphrase as string passphrase but then tries to reference it in this line byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
Change key to passphrase.
The two functions now produce identical results for me:
Python
secret_text = 'The rooster crows at midnight!'
key = 'A16ByteKey......'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
padded_text = encoder.encode(secret_text)
e = AES.new(key, mode, iv)
cipher_text = e.encrypt(padded_text)
print(base64.b64encode(cipher_text))
# e = AES.new(key, mode, iv)
# cipher_text = e.encrypt(padded_text)
# print(base64.b64encode(cipher_text))
C# (with the typo fix mentioned above)
Console.WriteLine(Encrypt("The rooster crows at midnight!", "A16ByteKey......"));
Python Result
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
C# Result
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
I suspect you're re-using 'e' in your python code multiple times. If you uncomment the last two lines of my python script, you'll see the output is now different. But if you uncomment the last three lines, you'll see the output is the same. As Foon said, this is due to how CBC works.
CBC (Cipher-block chaining) works when encrypting a sequence of bytes in blocks. The first block is encrypted by incorporating the IV with the first bytes of your plaintext ("The rooster..."). The second block uses the result of that first operation instead of the IV.
When you call e.encrypt() a second time (e.g. by uncommmenting the last two lines of the python script) you pick up where you left off. Instead of using the IV when encrypting the first block, it will use the output of the last encrypted block. This is why the results look different. By uncommening the last three lines of the python script you initialize a new encryptor which will use the IV for its first block, causing you to get identical results.
changed python code to:
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
def function(self):
encryptor = AES.new(key, mode, iv)**
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
in case anyone reaches this page via google
This esotic PKCS7 encoder is anything else then a function that pads with a static lenght.
So I implemented it with a very chip of code
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
# the block size for the cipher object; must be 16, 24, or 32 for AES
BLOCK_SIZE = 16
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
# PKCS7 method
PADDING = '\x06'
mode = AES.MODE_CBC
iv = '\x08' * 16 # static vector: dangerous for security. This could be changed periodically
#
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
def CryptIt(password, secret):
cipher = AES.new(secret, mode, iv)
encoded = EncodeAES(cipher, password)
return encoded
def DeCryptIt(encoded, secret):
cipher = AES.new(secret, mode, iv)
decoded = DecodeAES(cipher, encoded)
return decoded
I hope that this could help.
Cheers
Microsoft's implementation of PKCS7 is a bit different than Python's.
This article helped me with this problem:
http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html
His code for pkcs7 encoding and decoding is on github here:
https://github.com/janglin/crypto-pkcs7-example
With that PKCS7 library, this code worked for me:
from Crypto.Cipher import AES
aes = AES.new(shared_key, AES.MODE_CBC, IV)
aes.encrypt(PKCS7Encoder().encode(data))
Related
I'm trying to decrypt in c#, using bouncycastle, the result of the following php code:
<?php
$plaintext = 'The quick brown fox jumps over the lazy dog';
$cipher = "AES-128-CTR";
$key = "F5UgsDQddWGdgjddJtNgg6xE3V9uwaCR";
if (in_array($cipher, openssl_get_cipher_methods()))
{
$ivlen = openssl_cipher_iv_length($cipher);
$iv = '2782614358578542';
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv, $tag);
echo $ciphertext."\n";
}
?>
My c# code is as follows:
public string Decrypt(string toDecrypt, string keyStr, string ivStr)
{
byte[] inputBytes = Convert.FromBase64String(toDecrypt);
byte[] keyBytes = Encoding.UTF8.GetBytes(keyStr);
byte[] iv = Encoding.UTF8.GetBytes(ivStr);
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/PKCS7PADDING");
cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", keyBytes), iv));
byte[] decryptedBytes = cipher.DoFinal(inputBytes);
return Encoding.UTF8.GetString(decryptedBytes);
}
The php code returns
7/q61uOzeC4iycFIMrjvh01zvjOuCtnX8eWob8MAA5kIfMOIx915ctBIyw==
But when I make in C# the following call
Decrypt("7/q61uOzeC4iycFIMrjvh01zvjOuCtnX8eWob8MAA5kIfMOIx915ctBIyw==", "F5UgsDQddWGdgjddJtNgg6xE3V9uwaCR", "2782614358578542");
I get the following gibberish:
���yF�l����c:�-��7K�(�,�X�.[�W"�ܜ��J�
Which makes me think that I'm missing or doing something wrong with the encoding but I can't seem to figure it out.
AES-128 uses a 16 bytes key. In the PHP code, however, a 32 bytes key is used, which PHP implicitly truncates by considering only the first 16 bytes.
So the fix is to shorten the key in the C# code accordingly, i.e. use F5UgsDQddWGdgjdd.
Alternatively, change the AES variant in the PHP code to AES-256 (aes-256-ctr), which results in the ciphertext Tw05j9QDfaBK7zbyt9jine8xWqnzNB2Pim7rtv7gDba2TsE7ejvvjP5YKA==.
Additionally, in the C# code, apply AES/CTR/NoPadding, since CTR is a stream cipher mode that does not require padding, which is why padding is implicitly disabled in the PHP code.
Note that for security reasons, key/IV pairs must not be reused, especially for CTR. Therefore, for a fixed key, a static IV must not be applied, but a random one (which is passed to the decrypting side together with the ciphertext, usually concatenated).
I'm trying to port this C# code to PHP:
private static string DecryptString(string content, string password)
{
Rijndael aes;
byte[] retVal = null;
byte[] contentBytes;
byte[] passwordBytes;
byte[] ivBytes;
try {
//Get the content as byte[]
contentBytes = Convert.FromBase64String(content);
//Create the password and initial vector bytes
passwordBytes = new byte[32];
ivBytes = new byte[16];
Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length);
Array.Copy(passwordBytes, ivBytes, 16);
//Create the cryptograpy object
aes = Rijndael.Create();
aes.Key = passwordBytes;
aes.IV = ivBytes;
aes.Padding = PaddingMode.PKCS7;
string mode = aes.Mode.ToString();
//Decrypt
retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length);
}
catch {}
return Encoding.Unicode.GetString(retVal);
}
The content parameter is a 44 character long string, base 64 encoded, the password parameter is a 6 character long string.
This is the PHP code I put together:
$content = "[44 char base 64 encoded string]";
$password = "[6 char password]";
$padding = 32 - (strlen($password) % 32);
$password .= str_repeat(chr($padding), $padding);
$iv = substr($password, 0, 16);
$data = base64_decode($content);
$decrypted = openssl_decrypt($data, 'AES-256-CBC', $password, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
The result of the C# code is a 10 character long number. The result from PHP is some 32 character long gibberish - I guess binary data.
Can anybody help me to fix that code, or has an idea what I can try?
As mentioned by zaph in the comments this code is not considered safe and i advise against using it in a production environment. Quoting zaph's comment:
Essentially the code is not secure. Keys should be created from a password with a key derivation function such as PBKDF2. IVs should be random for each encryption, never the password/key. The IV can be and generally are prepended to the encrypted data, they do not need to be secret.
That being said, here is a PHP equivalent of your C# code:
function DecryptString($content, $password){
$password = mb_convert_encoding($password, "utf-16le");
$padding = 32 - (strlen($password) % 32);
$password .= str_repeat("\0", $padding);
$iv = substr($password, 0, 16);
$decrypted = openssl_decrypt($content, "AES-256-CBC", $password, false, $iv);
$decoded = mb_convert_encoding($decrypted, "utf-8", "utf-16le");
return $decoded;
}
C# Unicode strings are Little Endian UTF-16 encoded. In order to decode them properly in PHP we'll have to use mb_convert_encoding.
PHP test:
$password = "012345";
$content = "EJWgZ/26wp+Cb5utbM1aMk8XfqPQide4dzjQzzzYfj8=";
echo DecryptString($content, $password);
//0123456789
C# test:
string password = "012345";
string content = "EJWgZ/26wp+Cb5utbM1aMk8XfqPQide4dzjQzzzYfj8=";
Console.WriteLine(so.DecryptString(content, password));
//0123456789
Some tips:
PHP's openssl_decrypt uses PKCS padding by default, and can handle base64 encoded data. We can take advantage of those features by setting the options parameter to false.
IVs should be random bytes. This is important because using a static IV makes your encryption vulnerable to attacks. You can create secure random IVs with RNGCryptoServiceProvider for C#, and openssl_random_pseudo_bytes for PHP.
Passwords should be as long and unpredictable as possible - 123456 is not a good password! Although you could use your password as a key (if it has the right size), it's best to use a key created with a KDF. You can use Rfc2898DeriveBytes for C#, and hash_pbkdf2 for PHP.
If you don't check the authenticity of the message, then your data could be altered, or your service could be vulnerable to padding oracle attacks. You can use a MAC to verify your message (HMACSHA256 for C#, hash_hmac for PHP) or use an authenticated mode like GCM.
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,
I've spent a couple hours now trying to figure this out, but I just can't get it to work. I've got a C# encryption routine that I need to match in php. I can't change the C# version, that's not an option (3rd party is firm on this).
Here's the C# code:
//In C#
// Console.WriteLine(ApiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl"));
// Results in:
// XvHbR/CsLTo=
public static string ApiEncode(string data, string secret)
{
byte[] clear;
var encoding = new UTF8Encoding();
var md5 = new MD5CryptoServiceProvider();
byte[] key = md5.ComputeHash(encoding.GetBytes(secret));
TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
des.Key = key;
des.Mode = CipherMode.ECB;
des.Padding = PaddingMode.PKCS7;
byte[] input = encoding.GetBytes(data);
try { clear = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); }
finally
{
des.Clear();
md5.Clear();
}
return Convert.ToBase64String(clear);
}
Here's the best of what I've come up with in PHP:
//In PHP
// echo apiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl");
// Results in:
// 5aqvY6q1T54=
function apiEncode($data, $secret)
{
//Generate a key from a hash
$key = md5(utf8_encode($secret), true);
//Create init vector
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ecb), MCRYPT_RAND);
//Pad for PKCS7
$blockSize = mcrypt_get_block_size('tripledes', 'ecb');
$len = strlen($data);
$pad = $blockSize - ($len % $blockSize);
$data .= str_repeat(chr($pad), $pad);
//Encrypt data
$encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb'); //, $iv);
return base64_encode($encData);
}
To the best of my knowledge, I'm handling the PKCS7 padding properly on the PHP side. I'm not sure what else to try.
One thing to note, the C# is happening on windows, and the PHP on linux, not sure that should make a difference.
The padding length in your PHP version is based on the length of the password. This is incorrect. It should be based on the length of your message instead.
Try replacing strlen($password) with strlen($data).
The second problem is that the mcrypt library requires 24-byte keys. Triple DES applies regular DES three times, so you can call the 8-byte key used in each round of DES K1, K2, and K3. There are different ways to choose these keys. The most secure is to choose three distinct keys. Another way is to set K3 equal to K1. The least secure method (equivalent to DES) is to make K1 = K2 = K3.
Most libraries are "smart" enough to interpret a 16-byte 3DES key as the second option above: K3 = K1. The .NET implementation is doing this for you, but the mcrypt library is not; instead, it's setting K3 = 0. You'll need to fix this yourself, and pass mcrypt a 24-byte key.
After computing the MD5 hash, take the first 8 bytes of $key, and append them to the end of $key, so that you have a 24-byte value to pass to mcrypt_encrypt().
I found a solution, check this link, may help you. http://sanity-free.com/131/triple_des_between_php_and_csharp.html
And here is the decrypt function just in case:
public static string Decrypt(string cypherString)
{
byte[] key = Encoding.ASCII.GetBytes("icatalogDR0wSS#P6660juht");
byte[] iv = Encoding.ASCII.GetBytes("iCatalog");
byte[] data = Convert.FromBase64String(cypherString);
byte[] enc = new byte[0];
TripleDES tdes = TripleDES.Create();
tdes.IV = iv;
tdes.Key = key;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform ict = tdes.CreateDecryptor();
enc = ict.TransformFinalBlock(data, 0, data.Length);
return UTF8Encoding.UTF8.GetString(enc, 0, enc.Length);
}
Take a look at encoding.getBytes, you need the secret key Bytes from UTF8...
It appears the C# version does not set the IV. This could be an issue if you dont know what it is because msdn says:
The IV property is automatically set to a new random value whenever you create a new instance of one of the SymmetricAlgorithm classes or when you manually call the GenerateIV method.
It looks like in the PHP version, you are using an IV. You could try not supplying the IV and hope the C# version also uses zeros.
Edit: Looks like for ECB, the IV is ignored.
You might also need to encoding the key like in the C# version using utf8-encode
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.