2012-02-08 23 views
6

Dưới đây là các yêu cầu của tôi:Tại sao một cái gì đó được mã hóa trong PHP không khớp với cùng một chuỗi được mã hóa trong Ruby?

Tôi cần mã hóa chuỗi bằng PHP sử dụng mã hóa AES (bao gồm iv ngẫu nhiên), Base64 mã hóa nó, sau đó mã hóa URL để nó có thể được truyền dưới dạng thông số URL.

Tôi đang cố gắng để có được kết quả tương tự trong cả PHP và Ruby, nhưng tôi không thể làm cho nó hoạt động.

Đây là mã PHP của tôi:

function encryptData($data,$iv){ 
    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); 
    $iv_size = mcrypt_enc_get_iv_size($cipher); 
    if (mcrypt_generic_init($cipher, 'g6zys8dlvvut6b1omxc5w15gnfad3jhb', $iv) != -1){ 
     $cipherText = mcrypt_generic($cipher,$data); 
     mcrypt_generic_deinit($cipher); 
     return $cipherText; 
    } 
    else { 
     return false; 
    } 
} 
$data = 'Mary had a little lamb'; 
$iv = '96b88a5f0b9efb43'; 
$crypted_base64 = base64_encode(encryptData($data, $iv)); 

Đây là mã Ruby của tôi:

module AESCrypt 
    def AESCrypt.encrypt(data, key, iv) 
    aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    aes.encrypt 
    aes.key = key 
    aes.iv = iv 
    aes.update(data) + aes.final  
    end 
end 

plaintext = "Mary had a little lamb" 
iv = "96b88a5f0b9efb43" 
@crypted = AESCrypt::encrypt(plaintext, "g6zys8dlvvut6b1omxc5w15gnfad3jhb", iv) 
@crypted_base64 = Base64.encode64(@crypted) 
@crypted_base64_url = CGI.escape(@crypted_base64) 

Điều phẩn nộ là cả hai mẫu mã sản xuất tương tự nhưng không băm giống hệt nhau. Ví dụ, đoạn mã trên tạo (mã hóa base64, không URL mã hóa):

PHP: /aRCGgLBMOOAarjjtfTW2Qg2OtbPDLhx3KmgfgMzDJU=

Ruby: /aRCGgLBMOOAarjjtfTW2XIZhZ9VjBx8PdozxSL8IE0=

bất cứ ai có thể giải thích những gì tôi đang làm sai ở đây? Ngoài ra, nó dễ dàng hơn cho tôi (vì tôi là một người Ruby, không phải PHP thường) để sửa mã Ruby thay vì mã PHP. Vì vậy, nếu bạn muốn cung cấp một giải pháp trong Ruby mà sẽ kết hợp tốt với PHP, tôi sẽ đánh giá cao nhất. Oh, và ngoài ra, trong sản xuất iv thực sự sẽ là ngẫu nhiên, nhưng đối với ví dụ này tôi đặt nó là vĩnh viễn giống nhau để có thể so sánh đầu ra.

EDIT:

Nhờ câu trả lời Eugen Rieck, tôi đến một giải pháp. Ruby sẽ chặn các khối, nhưng PHP thì không, và bạn phải thực hiện nó theo cách thủ công. Thay đổi mã PHP để sau, và bạn sẽ có được chuỗi mã hóa mà trên của Ruby mã có thể giải mã một cách dễ dàng:

$iv = '96b88a5f0b9efb43'; 
$data = 'Mary had a little lamb'; 

function encryptData($data,$iv){ 
    $key = 'g6zys8dlvvut6b1omxc5w15gnfad3jhb'; 
    $padded_data = pkcs5_pad($data); 
    $cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $padded_data, MCRYPT_MODE_CBC, $iv); 
    return $cryptogram; 
} 

function pkcs5_pad ($text, $blocksize){ 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
} 
+2

Không thể giúp giải pháp nhưng tôi nghĩ rằng điều này có thể có liên quan đến khối đệm (vì chuỗi đầu ra giống nhau cho các ký tự đầu tiên). Bạn có thể muốn thử thêm phần đệm vào văn bản thuần túy theo cách thủ công dựa trên kích thước khối. – Mikk

+2

Mã Ruby của bạn đang gọi AES-256. Mã PHP của bạn đang gọi AES-128. Bạn có chắc chắn điều này là chính xác? IV của bạn rõ ràng là 128 ... – Charles

+0

@Charles Tôi cũng không hiểu điều này. Tuy nhiên, đây là cách duy nhất mọi thứ hoạt động.Chuyển đổi PHP để gọi AES-256 làm cho Ruby đưa ra một lỗi "giải mã xấu" từ OpenSSL. (Xem chủ đề được liệt kê trong bình luận của tôi cho câu trả lời dưới đây, nó thảo luận thêm về điều này) Ngoài ra [this] (http://www.chilkatsoft.com/p/php_aes.asp) cung cấp giải thích đầy đủ về các kỳ quặc mã hóa của PHP. Tôi nghĩ rằng nó làm sáng tỏ câu hỏi của bạn. – John

Trả lời

8

Hóa ra, đây là khá dễ dàng: Các padding là thủ phạm.

AES là một thuật toán mã khối, do đó, nó hoạt động trên các khối có kích thước cố định. Điều này có nghĩa là, khối cuối cùng sẽ được đệm, và bạn biết đấy, điều tốt về tiêu chuẩn là, có quá nhiều thứ để lựa chọn. PHP sử dụng không đệm, bạn sẽ phải xem xét AESCrypt để tìm hiểu những gì Ruby sử dụng.

Cùng tồn tại vấn đề với các thư viện JS AES và PHP khác nhau. Chúng tôi đã kết thúc trong allways làm padding của riêng của chúng tôi, vì điều này đã khiến tôi vào cơn giận dữ máu đỏ khá nhiều lần.

Nguồn gốc này giải thích, tại sao phần đầu tiên (mang thông tin) giống hệt nhau, trong khi phần thứ hai (mang đệm) khác nhau.

+0

Bạn có nghĩa là PHP sử dụng không đệm (tức là không có đệm) hoặc nó sử dụng đệm không (tức là đệm khối cuối cùng với 000000)? Hôm nay tôi đã theo dõi [câu hỏi này] (http://stackoverflow.com/questions/1864700/part-ii-how-to-make-ruby-aes-256-cbc-and-php-mcrypt-rijndael-128 -play-well-toge) mà dường như chỉ ra rằng Ruby đánh chặn các khối, nhưng mcrypt thì không. Tôi đã chỉnh sửa câu hỏi của mình ở trên để bao gồm câu trả lời cuối cùng. – John

+2

@John Không thể ** không ** sử dụng padding: Một mật mã khối theo định nghĩa cần một khối dữ liệu hoàn chỉnh để làm việc, vì vậy "không có đệm ở tất cả" không phải là một tùy chọn. PHP điền vào một khối không đầy đủ với 0x00, đó là một trong nhưng không phải là khả năng duy nhất. IIRC OpenSSL sử dụng RFC1423. Xem ví dụ http://www.chilkatsoft.com/p/p_119.asp để biết thêm thông tin –