2010-08-17 48 views
12

Tôi có thuật toán mã hóa/giải mã được viết bằng C# - Tôi cần có khả năng tạo mã hóa giống nhau trong PHP để tôi có thể gửi văn bản được mã hóa qua HTTP để được giải mã ở phía C# . Đây là mã C# cho mã hóa.Viết lại Rijndael 256 C# Mã hóa mã trong PHP

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; 
} 

Tôi có một số mã PHP tương tự có thể hữu ích. Nó không thực hiện chính xác khi cần thiết, nhưng tôi nghĩ đó có thể là một nơi tốt để bắt đầu.

<?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); 

?>

Trả lời

17

Bạn cần phải lấy được chìa khóa từ pass phrase theo cùng một cách như mã C# trong PasswordDeriveBytes. Đây là tài liệu để làm PBKDF1 nguồn gốc quan trọng, theo RFC2898:

lớp này sử dụng một phần mở rộng của thuật toán PBKDF1 định nghĩa trong PKCS # 5 v2.0 tiêu chuẩn để lấy byte thích hợp để sử dụng làm nguyên liệu chủ chốt từ một mật khẩu . Tiêu chuẩn này được ghi chép lại trong IETF RRC 2898.

có thư viện PHP mà thực hiện PBKDF1 trên mạng, nhưng thực sự là đơn giản để viết một từ đầu dựa ont ông RFC:

PBKDF1 (P, S, c, dkLen)

Options: hash
tiềm ẩn hàm băm

Input: P
mật khẩu, một chuỗi octet S muối, một tám-octet chuỗi c đếm lặp, một số nguyên dương dkLen dự định chiều dài trong octet của chính nguồn gốc, một số nguyên dương, tối đa là 16 cho MD2 hoặc MD5 và 20 cho SHA -1

Output: DK có nguồn gốc chìa khóa, một chuỗi dkLen-octet

bước:

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. 

Đã cập nhật

Khi bạn tìm thấy chính mình trong tình huống này, bạn thường tìm kiếm ví dụ implementaiton hiển thị giá trị ở mỗi bước. ví dụ như một ở 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 

Vì vậy, bây giờ cho phép viết một hàm PHP mà thực hiện điều này:

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;  
} 

Bây giờ bạn sẽ nhìn thấy errs cách của bạn: bạn không nói rõ tất cả quan trọng raw=true tham số là sha1.Cho phép xem ra chức năng của chúng ta là gì:

$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/>'; 

sản lượng này một cách chính xác kết quả mong đợi:

Key:dc19847e05c64d2f 
IV:af10ebfb4a3d2a20 
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20 

Tiếp theo, chúng ta có thể xác nhận rằng C# chức năng cũng làm như vậy:

  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(); 

này tạo ra cùng một đầu ra:

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

QED

thưởng giải thích

Xin đừng làm crypto nếu bạn không biết chính xác những gì bạn đang làm. Ngay cả sau khi bạn nhận được bản triển khai PHP đúng, mã C# đã đăng của bạn có một số vấn đề nghiêm trọng. Bạn đang trộn mảng byte với khuấy đại diện cho bãi chứa hex, bạn sử dụng một mã hóa cứng IV thay vì lấy nó từ cụm từ mật khẩu và muối, chỉ là tổng thể sai đồng bằng. Vui lòng sử dụng lược đồ mã hóa kệ ngoài, như SSL hoặc S-MIME và không tự phát minh lại. Bạn sẽ nhận được nó sai.

+0

Cảm ơn bạn đã trả lời, tôi nghĩ rằng tôi hiểu những gì bạn đang cố gắng để nói ở đây, nhưng không đủ để thực sự đưa nó vào sử dụng. Có thể có ví dụ được mã hóa đầy đủ không? –

+0

Đây là hàm PHP mà tôi đã đưa ra cho PBKDF1 $ HashPassPhrase = "passpharse"; $ HashSalt = "saltvalue"; $ HashIterations = 2; $ devkeylength = 32; $ devkey = PBKDF1 ($ HashPassPhrase, $ Hashsalt, $ HashIterations, $ devkeylength); \t chức năng PBKDF1 ($ pass, $ muối, $ count, $ dklen) \t { \t \t $ t = sha1 ($ pass.$ muối); \t \t for ($ i = 1; $ i <$ đếm; $ i ++) \t \t { \t \t \t $ t = sha1 ($ t); \t \t} \t \t $ t = substr ($ t, 0, $ dklen-1); \t \t trả lại $ t; \t} Nhưng tôi không nghĩ rằng tôi đang nhận được kết quả phù hợp. –

+0

Chức năng PBKDF1 của bạn đang sử dụng $ dklen-1 ... tại sao ??? –

4

Có vẻ như vấn đề chính của bạn là bạn đang sử dụng PHP hash() thay cho bước PasswordDeriveBytes() ở phía C#. Hai phương pháp đó không tương đương nhau. Sau này thực hiện thuật toán dẫn xuất mật khẩu PBKDF1, trong khi hash() chỉ là một băm. Nó trông giống như PEAR might have a PBKDF1 implementation, nhưng nếu không bạn có thể phải tự viết nó.

Bạn cũng cần đảm bảo mã hóa văn bản của mình nhất quán ở cả hai bên, nếu bạn chưa có.

Cuối cùng, bạn nên cân nhắc không làm những gì bạn đang làm vì cryptography is harder than it looks. Vì bạn đang sử dụng HTTP, bạn có thể sử dụng giao thức SSL thay cho việc viết của riêng bạn. Điều này sẽ giúp bạn bảo mật tốt hơn nhiều và ít rắc rối hơn về các chi tiết cấp thấp như giữ IV tăng dần đồng bộ và không có điều gì.

+0

Cảm ơn bạn đã trả lời này. Có, tôi nhận ra vấn đề chính là với PasswordDeriveBytes(). Bạn có nói rằng việc chuyển mật khẩu văn bản rõ ràng qua HTTPS bằng SSL thì an toàn hơn mã hóa nó ở một bên và giải mã nó ở bên kia không? –

+0

@Derek Armstrong: vâng! SSL được thiết kế để bảo vệ những thứ như mật khẩu văn bản thuần túy, giống như lược đồ của bạn. Sự khác biệt chính là SSL đã được xem xét và sửa đổi bởi các nhà mã hóa tốt nhất trên hành tinh trong 15 năm qua. Tôi nghĩ rằng nó cũng sẽ dễ sử dụng hơn nhiều, đặc biệt là kể từ khi bạn đã nói chuyện qua HTTP. – ladenedge

+1

Tôi cũng đã thêm một ghi chú vào câu trả lời của Remus, PasswordDeriveBytes là một lược đồ [không độc quyền, không có giấy tờ] (http://bouncy-castle.1462172.n4.nabble.com/NET-PasswordDeriveBytes-td1462616.html) cho tất cả byte được yêu cầu từ nó vượt quá đầu ra tối đa của PBKDF1 (20 byte). –

0

Có lý do chính đáng khiến bạn không thể chỉ sử dụng http://php.net/manual/en/function.mcrypt-module-open.php và sử dụng rijndael-256 làm thuật toán ????

+0

Mô-đun đó thậm chí còn thấp hơn cấp độ mà anh ta đã có! – ladenedge

+0

Vâng, điều đó sẽ không hoạt động. Tôi cần phải SALT trong số những thứ khác. –

0

Kiểm tra các thường trình OpenSSL trong PHP, chúng sẽ có thể xử lý những gì bạn cần làm.