zeussn has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I'm trying to create a C# Application that encrypts a message with the RijndaelManaged and then decrypt that message with perl, using PasswordDeriveByte to create random IV and Keys, but it seems that Perl reads the Salt but not the IV, so it can generate the key and can't decrypt the message. This is the C# Code :
The variables are strOrig (PlainText) and strKey (Passphrase) System.IO.MemoryStream ms = new System.IO.MemoryStream(); System.Security.Cryptography.RijndaelManaged rj = new Syst +em.Security.Cryptography.RijndaelManaged(); rj.Mode = System.Security.Cryptography.CipherMode.CBC; RandomNumberGenerator rng = RandomNumberGenerator.Create() +; byte[] salt = new byte[8]; rng.GetBytes(salt); PasswordDeriveBytes pdb = new PasswordDeriveBytes(strKey, +salt); rj.IV = pdb.GetBytes(16); rj.Key = pdb.GetBytes(32); // Debug so I can see the IV, Key, and Salt Generated string strIV = Encoding.UTF8.GetString(rj.IV); string _strKey = Encoding.UTF8.GetString(rj.Key); string strSalt = Encoding.UTF8.GetString(salt); ms.Write(Encoding.UTF8.GetBytes("Salted__"), 0, Encoding.U +TF8.GetByteCount("Salted__")); ms.Write(salt, 0, salt.Length); ms.Write(rj.IV, 0, rj.IV.Length); byte[] bOrig = Encoding.UTF8.GetBytes(strOrig); System.Security.Cryptography.CryptoStream cs = new System. +Security.Cryptography.CryptoStream(ms, rj.CreateEncryptor(), System.S +ecurity.Cryptography.CryptoStreamMode.Write); cs.Write(bOrig, 0, bOrig.Length); cs.FlushFinalBlock(); string bResult = Convert.ToBase64String(ms.ToArray()); bResult = bResult.Remove(0, 10); ms.Flush(); return bResult;
And this is the Perl Code :
#!/opt/local/bin/perl -w use IO::Socket; use IO::Handle; use MIME::Base64; use Data::Dumper; use POSIX; use Getopt::Long; use strict; require Crypt::CBC; my $msg = "Message_Generated_in_C#"; my $enc_key = "same_as_in_c#_strKey"; my $enc_alg = "Rijndael"; my $cipher = Crypt::CBC->new({ 'key' => $enc_key, 'cipher' => $enc_alg, }); my $base64_decoded_msg = decode_base64($msg); my $decrypted_msg = $cipher->decrypt($base64_decoded_msg); // This is for debug print STDERR " Salt:\n"; &hex_dump($cipher->salt()); print STDERR " Key:\n"; &hex_dump($cipher->key()); print STDERR " IV:\n"; &hex_dump($cipher->iv()); print STDERR " PassPhrase:\n"; &hex_dump($cipher->passphrase()); print STDERR " Block Size: " . $cipher->blocksize() ."\n", " Key Size: " . $cipher->keysize(). "\n\n"; print "$decrypted_msg\n"; // Function for debug sub hex_dump() { my $data = shift; my @chars = split //, $data; my $ctr = 0; my $ascii_str = ''; for my $char (@chars) { if ($ctr % 16 == 0) { print STDERR " $ascii_str\n" if $ascii_str; printf STDERR " 0x%.4x: ", $ctr; $ascii_str = ''; } printf STDERR "%.2x", ord($char); if ((($ctr+1) % 2 == 0) and ($ctr % 16 != 0)) { print STDERR ' '; } if ($char =~ /[^\x20-\x7e]/) { $ascii_str .= '.'; } else { $ascii_str .= $char; } $ctr++; } if ($ascii_str) { my $remainder = 1; if ($ctr % 16 != 0) { $remainder = 16 - $ctr % 16; if ($remainder % 2 == 0) { $remainder = 2*$remainder + int($remainder/2) + 1; } else { $remainder = 2*$remainder + int($remainder/2) + 2; } } print STDERR ' 'x$remainder, $ascii_str; } print STDERR "\n"; return; }
I believe that the problem is in the crypting process and the possition of the generated IV key, but I really don't know where to put it.

Replies are listed 'Best First'.
Re: Encrypting a Message in C# with Rijndael and Decrypting it on Perl
by Perlbotics (Archbishop) on Jul 10, 2009 at 20:14 UTC

    It seems you don't need the Crypt::CBC module since you can use Crypt::Rijndael directly configured in CBC mode, as the following example (taken from the manual) shows:

    use Crypt::Rijndael; # keysize() is 32, but 24 and 16 are also possible # blocksize() is 16 $cipher = Crypt::Rijndael->new( "a" x 32, Crypt::Rijndael::MODE_CBC() + ); # <-- key and CBC-MODE $cipher->set_iv($iv); # <-- here's your I-Vector $crypted = $cipher->encrypt($plaintext); # - OR - $plaintext = $cipher->decrypt($crypted);
    If you provide the output of your programs or initialise the variables of your Perl program with meaningful values, more specific help on the Perl part might be possible. I am not sure if most of the Monks here have a C# development environment at hand in order to create their own cipher-texts ... at least I haven't.

      Actually I just find the solution. The problem was that the PasswordDeriveByte System from C# don't create the same kind of Key/IV that the Perl CBC system (that uses openssl system). However, here is the C# code to generate de Key and IV
      private void GenerateKeyIV(string strKey, byte[] bSalt, out byte[] bEn +cKey, out byte[] bEncIV) { // Extracted from http://www.jensign.com/JavaScience/dotne +t/DeriveKeyM/ // by Michel Gallant int HASHLENGTH = 16; byte[] bKey = Encoding.UTF8.GetBytes(strKey); int count = 1; int miter = 3; byte[] keymaterial = new byte[HASHLENGTH * miter]; byte[] data00 = new byte[bKey.Length + bSalt.Length]; Array.Copy(bKey, data00, bKey.Length); Array.Copy(bSalt, 0, data00, bKey.Length, bSalt.Length); MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = null; byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; for (int j = 0; j < miter; j++) { if (j == 0) result = data00; else { Array.Copy(result, hashtarget, result.Length); Array.Copy(data00, 0, hashtarget, result.Length, d +ata00.Length); result = hashtarget; } for (int i = 0; i < count; i++) result = md5.ComputeHash(result); Array.Copy(result, 0, keymaterial, j * HASHLENGTH, res +ult.Length); } bEncKey = new byte[32]; bEncIV = new byte[16]; Array.Copy(keymaterial, 0, bEncKey, 0, 32); Array.Copy(keymaterial, 32, bEncIV, 0, 16); }

      Your snippet doesn't handle padding.

      There's no reason not to use Crypt::CBC.

        There's no reason not to use Crypt::CBC.
        Unless performance is important.
        use strict; use warnings; use Crypt::CBC; use Crypt::Rijndael; use Crypt::GCrypt; my $cbc = Crypt::CBC->new( -key => 'a' x 32, -cipher => 'Rijndael' ); $cbc->start('encrypting'); my $rij = Crypt::Rijndael->new( 'a' x 32, Crypt::Rijndael::MODE_CBC ); my $gcry = Crypt::GCrypt->new( type => 'cipher', algorithm => 'rijndael', mode => 'cbc' ); $gcry->start('encrypting'); $gcry->setkey( 'a' x 32 ); $gcry->setiv( 'b' x 16 ); my $plaintext = 'plain text' x 8192; use Benchmark qw( cmpthese ); cmpthese - 10, { 'Crypt::CBC' => sub { $cbc->crypt($plaintext); }, 'Crypt::Rijndael' => sub { $rij->encrypt($plaintext); }, 'Crypt::GCrypt' => sub { $gcry->encrypt($plaintext); }, }; $cbc->finish; $gcry->finish; __END__ Rate Crypt::CBC Crypt::Rijndael Crypt::GCrypt Crypt::CBC 74.2/s -- -83% -89% Crypt::Rijndael 430/s 479% -- -39% Crypt::GCrypt 704/s 849% 64% --

        Upd: fix to improve Crypt::CBC performance

Re: Encrypting a Message in C# with Rijndael and Decrypting it on Perl
by zwon (Abbot) on Jul 10, 2009 at 19:33 UTC

    I think you should first decode message, extract salt and iv, and after that provide them to Crypt::CBC->new. Though I'm not sure that schemes used by Crypt::CBC and C# are compatible.

      That shouldn't be the problem because if you do the encrypt with the perl function it work whith the decrypt code.

        Have a look onto Crypt::CBC source, particularly onto _generate_iv_and_cipher_from_datastream and _salted_key_and_iv functions. Crypt::CBC doesn't read IV from encrypted data, it computes IV from the salt and passphrase. Note, that C# implementation may use another algorithm to compute encryption key from the salt and passphrase.