Rewrite Rijndael 256 C# Encryption Code in PHP

前端 未结 4 1167
醉梦人生
醉梦人生 2020-12-17 17:09

I have an encryption/decryption algorithm written in C# - I need to be able to produce the same encryption in PHP so I can send the encrypted text over HTTP to be decrypted

相关标签:
4条回答
  • 2020-12-17 17:38

    Check OpenSSL routines in PHP, they should be able to handle what you need to do.

    0 讨论(0)
  • 2020-12-17 17:39

    You need to derive the key from the pass phrase the same way as the C# code does in the PasswordDeriveBytes. This is documented to do PBKDF1 key derivation, as per RFC2898:

    This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0 standard to derive bytes suitable for use as key material from a password. The standard is documented in IETF RRC 2898.

    there are PHP libraries that implement PBKDF1 out there, but is really simple to write one from scratch based ont he RFC:

    PBKDF1 (P, S, c, dkLen)

    Options: Hash
    underlying hash function

    Input: P
    password, an octet string S salt, an eight-octet string c iteration count, a positive integer dkLen intended length in octets of derived key, a positive integer, at most 16 for MD2 or MD5 and 20 for SHA-1

    Output: DK derived key, a dkLen-octet string

    Steps:

      1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output
         "derived key too long" and stop.
    
      2. Apply the underlying hash function Hash for c iterations to the
         concatenation of the password P and the salt S, then extract
         the first dkLen octets to produce a derived key DK:
    
                   T_1 = Hash (P || S) ,
                   T_2 = Hash (T_1) ,
                   ...
                   T_c = Hash (T_{c-1}) ,
                   DK = Tc<0..dkLen-1>
    
      3. Output the derived key DK.
    

    Updated

    When you find youself in this situation, you usually search for an example implementaiton that shows the values at every step. for instance the one at http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf:

    Password = "password" 
             = (0x)70617373776F7264
    Salt     = (0x)78578E5A5D63CB06
    Count    = 1000
    kLen     = 16
    Key      = PBKDF1(Password, Salt, Count, kLen)
             = (0x)DC19847E05C64D2FAF10EBFB4A3D2A20
    
    P || S = 70617373776F726478578E5A5D63CB06
    T_1=     D1F94C4D447039B034494400F2E7DF9DCB67C308
    T_2=     2BB479C1D369EA74BB976BBA2629744E8259C6F5
    ...
    T_999=   6663F4611D61571068B5DA168974C6FF2C9775AC
    T_1000=  DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE
    Key=     DC19847E05C64D2FAF10EBFB4A3D2A20
    

    So now lets write a PHP function that does this:

    function PBKDF1($pass,$salt,$count,$dklen) { 
        $t = $pass.$salt;
        //echo 'S||P: '.bin2hex($t).'<br/>';
        $t = sha1($t, true); 
        //echo 'T1:' . bin2hex($t) . '<br/>';
        for($i=2; $i <= $count; $i++) { 
            $t = sha1($t, true); 
            //echo 'T'.$i.':' . bin2hex($t) . '<br/>';
        } 
        $t = substr($t,0,$dklen); 
        return $t;      
    }
    

    Now you can see the errs of your ways: you did not specify the all important raw=true parameter to sha1. Lets see what is our function output:

    $HashPassPhrase = pack("H*","70617373776F7264");
    $HashSalt = pack("H*","78578E5A5D63CB06"); 
    $HashIterations = 1000; 
    $devkeylength = 16; 
    $devkey = PBKDF1($HashPassPhrase,$HashSalt,$HashIterations,$devkeylength);
    echo 'Key:' . bin2hex(substr($devkey, 0, 8)) . '<br/>';
    echo 'IV:' . bin2hex(substr($devkey, 8, 8)) .'<br/>';
    echo 'Expected: DC19847E05C64D2FAF10EBFB4A3D2A20<br/>';
    

    this output exactly the expected result:

    Key:dc19847e05c64d2f
    IV:af10ebfb4a3d2a20
    Expected: DC19847E05C64D2FAF10EBFB4A3D2A20
    

    Next, we can validate that the C# function does the same:

                byte[] password = Encoding.ASCII.GetBytes("password");
                byte[] salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06};
    
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(
                    password, salt, "SHA1", 1000);
    
                byte[] key = pdb.GetBytes(8);
                byte[] iv = pdb.GetBytes(8);
    
                Console.Out.Write("Key: ");
                foreach (byte b in key)
                {
                    Console.Out.Write("{0:x} ", b);
                }
                Console.Out.WriteLine();
    
                Console.Out.Write("IV: ");
                foreach (byte b in iv)
                {
                    Console.Out.Write("{0:x} ", b);
                }
                Console.Out.WriteLine();
    

    this produces the very same output:

    Key: dc 19 84 7e 5 c6 4d 2f
    IV: af 10 eb fb 4a 3d 2a 20
    

    QED

    bonus explanation

    Please don't do crypto if you don't know exactly what you're doing. Even after you get the PHP implementaiton correct, your posted C# code has some serious problems. You are mixing byte arrays with stirng representing hex dumps, you use a hard coded IV instead of deriving it from the passphrase and salt, is just overall plain wrong. Please use an off-the shelf encryption scheme, like SSL or S-MIME and do not re-invent your own. You will get it wrong.

    0 讨论(0)
  • 2020-12-17 17:41

    Is there a good reason why you can't just use http://php.net/manual/en/function.mcrypt-module-open.php and use rijndael-256 as the algorithm????

    0 讨论(0)
  • 2020-12-17 17:48

    It looks like your main problem is that you're using PHP's hash() in place of the PasswordDeriveBytes() step on the C# side. Those two methods are not equivalent. The latter implements the PBKDF1 password derivation algorithm, while hash() is just a hash. It looks like PEAR might have a PBKDF1 implementation, but otherwise you might have to write it yourself.

    You also need to make sure your text encoding is consistent on both sides, if you haven't already.

    Finally, you should consider not doing what you're doing because cryptography is harder than it looks. Since you're using HTTP, you can make use of the SSL protocol in lieu of writing your own. This will net you far better security and less hassle on low-level details like keeping incremental IVs in sync and whatnot.

    0 讨论(0)
提交回复
热议问题