doumu8911 2010-08-17 17:59
浏览 73

用PHP重写Rijndael 256 C#加密代码

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 on the C# side. Here is the C# code for the encryption.

this.m_plainText = string.Empty;
this.m_passPhrase = "passpharse";
this.m_saltValue = "saltvalue";
this.m_hashAlgorithm = "SHA1";
this.m_passwordIterations = 2;
this.m_initVector = "1a2b3c4d5e6f7g8h";
this.m_keySize = 256;

public string Encrypt()
{
    string plainText = this.m_plainText;
    string passPhrase = this.m_passPhrase;
    string saltValue = this.m_saltValue;
    string hashAlgorithm = this.m_hashAlgorithm;
    int passwordIterations = this.m_passwordIterations;
    string initVector = this.m_initVector;
    int keySize = this.m_keySize;

    // Convert strings into byte arrays.
    // Let us assume that strings only contain ASCII codes.
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding.
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

    // Convert our plaintext into a byte array.
    // Let us assume that plaintext contains UTF8-encoded characters.
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    // First, we must create a password, from which the key will be derived.
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations.
    PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                    passPhrase,
                                                    saltValueBytes,
                                                    hashAlgorithm,
                                                    passwordIterations);

    // Use the password to generate pseudo-random bytes for the encryption
    // key. Specify the size of the key in bytes (instead of bits).
    byte[] keyBytes = password.GetBytes(keySize / 8);

    // Create uninitialized Rijndael encryption object.
    RijndaelManaged symmetricKey = new RijndaelManaged();

    // It is reasonable to set encryption mode to Cipher Block Chaining
    // (CBC). Use default options for other symmetric key parameters.
    symmetricKey.Mode = CipherMode.CBC;

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes.
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                     keyBytes,
                                                     initVectorBytes);

    // Define memory stream which will be used to hold encrypted data.
    MemoryStream memoryStream = new MemoryStream();

    // Define cryptographic stream (always use Write mode for encryption).
    CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                 encryptor,
                                                 CryptoStreamMode.Write);
    // Start encrypting.
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

    // Finish encrypting.
    cryptoStream.FlushFinalBlock();

    // Convert our encrypted data from a memory stream into a byte array.
    byte[] cipherTextBytes = memoryStream.ToArray();

    // Close both streams.
    memoryStream.Close();
    cryptoStream.Close();

    // Convert encrypted data into a base64-encoded string.
    string cipherText = Convert.ToBase64String(cipherTextBytes);

    // Return encrypted string.
    return cipherText;
}

I have some similar PHP code that may help. It doesn't do exactly as needed, but I think it may be a good place to start.

<?php

/*
 * DEFINE CONSTANTS
 */
$HashPassPhrase = "passpharse";
$HashSalt = "saltvalue";
$HashAlgorithm = "SHA1";
$HashIterations = "2";
$InitVector = "1a2b3c4d5e6f7g8h";        // Must be 16 bytes
$keySize = "256";

class Cipher {
    private $securekey, $iv;
    function __construct($textkey) {
        $this->securekey = hash($HashAlgorithm,$textkey,TRUE);
        $this->iv = $InitVector;
    }
    function encrypt($input) {
        return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv));
    }
    function decrypt($input) {
        return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv));
    }
}

$cipher = new Cipher($HashPassPhrase);

$encryptedtext = $cipher->encrypt("Text To Encrypt");
echo "->encrypt = $encryptedtext<br />";

$decryptedtext = $cipher->decrypt($encryptedtext);
echo "->decrypt = $decryptedtext<br />";

var_dump($cipher);

?>

  • 写回答

4条回答 默认 最新

  • dousa1630 2010-08-17 18:15
    关注

    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.

    评论

报告相同问题?

悬赏问题

  • ¥15 蓝桥oj3931,请问我错在哪里
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染