使用php的openssl_encrypt和python的pycrypt進行跨語言的對稱加密和解密問題


最近有一個業務需求,需要前端傳遞一個密碼到后端,期間要對傳遞的密碼通過進行對稱加密,我們約定使用成熟的AES加密方法。

前端使用php,后端用python,但是發現前端兄弟加密后的字符串,在python端解密后末尾總會有16字節長度的\x10字符內容,通過python的ord('\x10')輸出可知,這就是數字16的Unicode code。

眾所周知,在使用AES進行對稱加密之前,需要將加密的內容長度補全至16的倍數。如果前端兄弟無法解決加密內容中總有額外的16字節\x10字符的問題,那么后端就要考慮多余的處理邏輯,看起來奇奇怪怪的。

於是我百度了下php的openssl_encrypt函數,發現其中option選項有4個:

- 0
- OPENSSL_RAW_DATA=1
- OPENSSL_ZERO_PADDING=2
- OPENSSL_NO_PADDING=3

其中赫然寫着OPENSSL_NO_PADDING,字面意思很好理解了,應該就是就是不會自動追加(補全)的意思,再看前端兄弟用的是OPENSSL_RAW_DATA。於是替換為OPENSSL_NO_PADDING后,果然沒有了\x10的內容,問題暫時解決了。

然后我又回頭想了一想,為什么OPENSSL_RAW_DATA會自動追加一個16字節的\x10呢,這肯定是有原因的。

因為在之前的測試中,我們在調用php的openssl_encrypt函數之前,已經手動對加密的字符進行了補全,保證其長度是16的倍數。如果不補全會怎樣?

我手動試了一下:

<?php
$str = '1234567890'
$add_data_zero_padding = openssl_encrypt($str, 'AES-128-CBC', $key,  $options=OPENSSL_ZERO_PADDING, $iv);
$add_data_no_padding = openssl_encrypt($str, 'AES-128-CBC', $key,  $options=OPENSSL_NO_PADDING, $iv);
$add_data_raw_data = openssl_encrypt($add_str, 'AES-128-CBC', 'eNg6geeCinee0kee',  $options=OPENSSL_RAW_DATA, 'nesejeiP6du0quie');

var_dump($add_data_zero_padding);
var_dump($add_data_no_padding);
var_dump($add_data_raw_data);

echo "base64 encode:\n";
var_dump(base64_encode($add_data_raw_data));
?>

然后輸出結果就是:

bool(false)
bool(false)
string(32) "�q$B�7��*���vE0�+��J.8t�[Bt�"
base64 encode:
string(44) "jHEkQrs3hBG+DiqE/4B2RTCUK6wE5r1KLjh03VtCdPs="

果然,如果沒有補全,那么OPENSSL_ZERO_PADDINGOPENSSL_NO_PADDING會加密失敗。而OPENSSL_RAW_DATA加密的內容,解密后的字節內容是:

b'NulhIKidvmW6jaFK4T9uqJyuwrlEo\x03\x03\x03'

如此一來,其實不用去細看文檔也能推理出OPENSSL_RAW_DATA自動補全的含義了,因為補全的內容最后還需要還原為原始字符串,怎么知道哪些字符是補全上去的,哪些字符是原始字符呢?

php邏輯是這樣的,我補全的長度至少是1,最長是16,代表這個長度的數字,正好都可以用一個Unicode字符表示,比如1就是\x01,16就是\x10

如果加密的內容長度是15字節,那么就在最后補全一個\x01,還原的時候,只需要讀取最后一個字節內容,轉換為數字,得到1,就知道加密前只追加了1個字節,那么就把末尾的1個字節內容去掉即可。

如果加密的內容長度正好是16字節呢,為了還原,那么就必須要在末尾追加16\x10,還原的時候讀取最后一個字節並轉換為數字,就知道加密時候追加了16字節,那么把末尾的16個字節去掉即可。

其實用python代碼表示這個補全和還原的邏輯如下:

BLOCK_SIZE = 16  # 16 Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE) # 至少會追加16字節的內容
unpad = lambda s: s[:-ord(s[len(s) - 1:])]

chrord 含義如下:

chr(i, /)
    Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.

ord(c, /)
    Return the Unicode code point for a one-character string.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM