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.

    评论

报告相同问题?

悬赏问题

  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!