• 使用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.
    
  • 相关阅读:
    Java实现Excel导入数据库,数据库中的数据导入到Excel
    MySQL如何把A表查询出来的某个字段的数据插入到新增的字段的下面
    MySQL怎么把小数转换为百分比?
    linux上安装python3和pip----最简单的安装
    linux pip 安装包的时候报错:Could not find a version that satisfies the requirement bs4 (from versions: ) No matching distribution found for bs4
    Excel提取中文
    关于Excel的一些小技巧
    5-24 树种统计 (25分)
    POJ 2663 Tri Tiling
    5-3 树的同构 (25分)
  • 原文地址:https://www.cnblogs.com/yzhch/p/15608188.html
Copyright © 2020-2023  润新知