zoukankan      html  css  js  c++  java
  • 使用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.
    
  • 相关阅读:
    【原创】简单快速软件开发平台,C/S架构二次开发平台
    【原创】进销存快速开发框架 (Winform三层架构+DevExpress+MsSQL)
    MES软件开发工具
    Winform C/S架构TMS物流运输管理系统司机车辆GPS+手机APP定位参考设计
    C#权限管理框架介绍|C/S系统快速开发框架权限系统设计
    C# Winform程序调用WebApi接口实现增删改查(CRUD)实例源码教程
    ASP.NETWebApi实例教程:如何部署和发布WebApi到IIS服务器详解
    Web后端开发框架|WebApi后端主流开发框架介绍
    Asp.Net开源服务端框架,WebApi后端框架(C#.NET)
    Winform布局开源框架,Winform控件框架,插件化框架
  • 原文地址:https://www.cnblogs.com/yzhch/p/15608188.html
Copyright © 2011-2022 走看看