2011-09-06 33 views
17

Tôi đang cố gắng mã hóa/giải mã một chuỗi bằng mã hóa AES 128 bit (ECB). Những gì tôi muốn biết là làm thế nào tôi có thể thêm/loại bỏ các PKCS7 đệm vào nó. Dường như phần mở rộng Mcrypt có thể xử lý mã hóa/giải mã, nhưng phần đệm phải được thêm/xóa theo cách thủ công.Làm thế nào để thêm/loại bỏ PKCS7 đệm từ một chuỗi mã hóa AES?

Bất kỳ ý tưởng nào?

+3

Chỉ cần một lưu ý: Nếu bạn có thể thay đổi, sử dụng [một chế độ hơn ECB (nó là không an toàn)] (http: //en.wikipedia. org/wiki/Block_cipher_modes_of_operation). –

+0

@Paul Không, không thể thay đổi, hệ thống của khách hàng phụ thuộc vào những gì. Bất kỳ cơ hội bạn có thể hướng dẫn tôi với điều padding? –

+0

Tôi có nghĩa là AES, đã sửa nó. –

Trả lời

46

Hãy xem. PKCS # 7 được mô tả trong RFC 5652 (Cú pháp thông báo mật mã).

Sơ đồ đệm được đưa ra trong phần 6.3. Content-encryption Process. Về cơ bản nó nói: nối thêm nhiều byte khi cần để điền vào kích thước khối đã cho (nhưng ít nhất một), và mỗi byte phải có độ dài đệm là giá trị.

Vì vậy, nhìn vào byte được giải mã cuối cùng, chúng tôi biết có bao nhiêu byte để loại bỏ. (Người ta cũng có thể kiểm tra xem tất cả họ có cùng giá trị không.)

Bây giờ tôi có thể cung cấp cho bạn một cặp chức năng PHP để thực hiện việc này, nhưng PHP của tôi hơi bị gỉ. Vì vậy, hoặc tự làm điều này (sau đó cảm thấy tự do để chỉnh sửa câu trả lời của tôi để thêm nó vào), hoặc có một cái nhìn tại user-contributed notes để tài liệu mcrypt - khá một số trong số đó là về đệm và cung cấp một thực hiện PKCS # 7 padding.


Vì vậy, chúng ta hãy nhìn vào first note there chi tiết:

<?php 

function encrypt($str, $key) 
{ 
    $block = mcrypt_get_block_size('des', 'ecb'); 

này được kích thước khối của thuật toán được sử dụng. Trong trường hợp của bạn, bạn sẽ sử dụng aes hoặc rijndael_128 thay vì des, tôi giả sử (tôi đã không kiểm tra). (Thay vào đó, bạn chỉ có thể kéo dài 16 vào đây để AES, thay vì cách gọi hàm.)

 $pad = $block - (strlen($str) % $block); 

này tính toán kích thước đệm. strlen($str) là độ dài dữ liệu của bạn (tính theo byte), % $block cung cấp phần còn lại là modulo $block, tức là số byte dữ liệu trong khối cuối cùng. $block - ... do đó cung cấp số byte cần thiết để điền vào khối cuối cùng này (bây giờ là một số giữa 1$block, bao gồm).

 $str .= str_repeat(chr($pad), $pad); 

str_repeat tạo ra một chuỗi bao gồm một sự lặp lại của cùng một chuỗi, đây là một sự lặp lại của character given by$pad, $pad lần, tức là một chuỗi có độ dài $pad, đầy $pad. $str .= ... gắn chuỗi đệm này vào dữ liệu gốc.

 return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB); 

Đây là bản thân mã hóa. Sử dụng MCRYPT_RIJNDAEL_128 thay vì MCRYPT_DES.

} 

Bây giờ theo một hướng khác:

function decrypt($str, $key) 
{ 
    $str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB); 

Các giải mã. (Tất nhiên, bạn sẽ thay đổi thuật toán, như trên). $ str bây giờ là chuỗi được giải mã, bao gồm cả phần đệm.

 $block = mcrypt_get_block_size('des', 'ecb'); 

Đây lại là kích thước khối. (Xem bên trên.)

 $pad = ord($str[($len = strlen($str)) - 1]); 

Điều này có vẻ hơi lạ. Better viết nó trong nhiều bước:

$len = strlen($str); 
    $pad = ord($str[$len-1]); 

$len bây giờ là chiều dài của chuỗi đệm, và $str[$len - 1] là ký tự cuối cùng của chuỗi này. ord chuyển đổi số này thành một số. Do đó $pad là số mà trước đây chúng tôi đã sử dụng làm giá trị điền cho phần đệm và đây là độ dài đệm.

 return substr($str, 0, strlen($str) - $pad); 

Vì vậy, bây giờ chúng tôi đã cắt $pad byte cuối cùng khỏi chuỗi. (Thay vì strlen($str) chúng tôi cũng có thể viết $len tại đây: substr($str, 0, $len - $pad).).

} 

?> 

Lưu ý rằng thay vì sử dụng substr($str, $len - $pad), người ta cũng có thể viết substr($str, -$pad), như substr hàm trong PHP có một đặc biệt xử lý cho toán hạng âm/lập luận, để đếm từ ngày kết thúc của chuỗi. (Tôi không biết nếu điều này là nhiều hơn hoặc ít hiệu quả hơn so với chiều dài đầu tiên và tính toán chỉ mục theo cách thủ công.)

Như đã nói ở trên và ghi chú trong nhận xét của rossum, thay vì chỉ đơn giản là loại bỏ phần đệm như đã làm ở đây, bạn nên kiểm tra xem nó có đúng không - tức là xem substr($str, $len - $pad) và kiểm tra xem tất cả các byte của nó là chr($pad). Điều này phục vụ như một kiểm tra nhẹ chống lại tham nhũng (mặc dù kiểm tra này là hiệu quả hơn nếu bạn sử dụng một chế độ chuỗi thay vì ECB, và không phải là một thay thế cho một MAC thực).


(Và vẫn còn, hãy nói với khách hàng của bạn họ nên suy nghĩ về việc thay đổi sang một chế độ an toàn hơn ECB).

+0

Nếu bạn có thể đưa ra một số mã mẫu để thực hiện điều này trong mã giả hoặc java, điều đó thật tuyệt vời. Tôi vẫn không hoàn toàn hiểu làm thế nào để làm điều này, nghĩa là kích thước khối đã cho, byte giải mã cuối cùng là gì, vv –

+0

Kích thước khối cho AES là 128 bit, tức là 16 byte. Bạn sẽ cần một số cách để biết tin nhắn của bạn đã kết thúc, tất nhiên ... nhưng vấn đề chỉ đơn giản là lấy nguồn được đưa ra trong một trong các ghi chú cho tài liệu PHP? Mã giả của tôi sẽ không tốt hơn nhiều. –

+0

Chỉ cần mã nguồn trên hướng dẫn sử dụng php không được viết quá rõ ràng, tôi thực sự không hiểu nó đang làm gì. Nếu bạn có thể đăng một số Java với ý kiến ​​và một số có thể, điều đó sẽ thực sự hữu ích. –

6

tôi đã tạo ra hai phương pháp để thực hiện các padding và unpadding. Các hàm được ghi lại bằng cách sử dụng phpdoc và yêu cầu PHP 5. Như bạn sẽ thấy hàm unpad chứa nhiều xử lý ngoại lệ, tạo ra không ít hơn 4 thông điệp khác nhau cho mỗi lỗi có thể xảy ra.

Để nhận được kích thước khối cho PHP mcrypt, bạn có thể sử dụng mcrypt_get_block_size, cũng xác định kích thước khối sẽ bằng byte thay vì bit.

/** 
* Right-pads the data string with 1 to n bytes according to PKCS#7, 
* where n is the block size. 
* The size of the result is x times n, where x is at least 1. 
* 
* The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. 
* This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. 
* 
* @param string $plaintext the plaintext encoded as a string containing bytes 
* @param integer $blocksize the block size of the cipher in bytes 
* @return string the padded plaintext 
*/ 
function pkcs7pad($plaintext, $blocksize) 
{ 
    $padsize = $blocksize - (strlen($plaintext) % $blocksize); 
    return $plaintext . str_repeat(chr($padsize), $padsize); 
} 

/** 
* Validates and unpads the padded plaintext according to PKCS#7. 
* The resulting plaintext will be 1 to n bytes smaller depending on the amount of padding, 
* where n is the block size. 
* 
* The user is required to make sure that plaintext and padding oracles do not apply, 
* for instance by providing integrity and authenticity to the IV and ciphertext using a HMAC. 
* 
* Note that errors during uppadding may occur if the integrity of the ciphertext 
* is not validated or if the key is incorrect. A wrong key, IV or ciphertext may all 
* lead to errors within this method. 
* 
* The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. 
* This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. 
* 
* @param string padded the padded plaintext encoded as a string containing bytes 
* @param integer $blocksize the block size of the cipher in bytes 
* @return string the unpadded plaintext 
* @throws Exception if the unpadding failed 
*/ 
function pkcs7unpad($padded, $blocksize) 
{ 
    $l = strlen($padded); 

    if ($l % $blocksize != 0) 
    { 
     throw new Exception("Padded plaintext cannot be divided by the block size"); 
    } 

    $padsize = ord($padded[$l - 1]); 

    if ($padsize === 0) 
    { 
     throw new Exception("Zero padding found instead of PKCS#7 padding"); 
    }  

    if ($padsize > $blocksize) 
    { 
     throw new Exception("Incorrect amount of PKCS#7 padding for blocksize"); 
    } 

    // check the correctness of the padding bytes by counting the occurance 
    $padding = substr($padded, -1 * $padsize); 
    if (substr_count($padding, chr($padsize)) != $padsize) 
    { 
     throw new Exception("Invalid PKCS#7 padding encountered"); 
    } 

    return substr($padded, 0, $l - $padsize); 
} 

này không làm mất hiệu lực câu trả lời của Paulo Ebermann dưới mọi hình thức, về cơ bản cũng trả lời tương tự trong mã & PHPDoc thay vì như mô tả.


Lưu ý rằng việc trả lại một lỗi padding để kẻ tấn công có thể dẫn đến một cuộc tấn công oracle đệm mà hoàn toàn phá vỡ CBC (khi CBC được sử dụng thay vì ECB hoặc một mật mã xác nhận an toàn).

0

Chỉ cần gọi hàm sau đây sau khi bạn giải mã dữ liệu

function removePadding($decryptedText){ 
    $strPad = ord($decryptedText[strlen($decryptedText)-1]); 
    $decryptedText= substr($decryptedText, 0, -$strPad); 
    return $decryptedText; 
} 
+0

Mã câu trả lời sẽ không hoạt động với đệm trống mà PHP mcrypt mặc định. Đối với lớp đệm PKCS # 7/PKCS # 5, cần phải kiểm tra xem phần đệm có hợp lệ hay không. Xem xét việc sử dụng khóa sai, $ strPad rất có thể sẽ là sai, có khả năng một giá trị lớn hơn độ dài của dữ liệu. Nhưng không trả lại một lỗi đệm xấu, mà có xu hướng tạo ra một oracle đệm, thay vì chỉ làm gì. Hầu hết các thư viện đều hỗ trợ đệm PKCS # 7 và sẽ tự động thêm phần đệm vào mã hóa và loại bỏ phần đệm trên giải mã – không cần phải làm gì nữa. – zaph

Các vấn đề liên quan