惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
H
Hacker News: Front Page
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
aimingoo的专栏
aimingoo的专栏
V
V2EX
B
Blog RSS Feed
M
MIT News - Artificial intelligence
Engineering at Meta
Engineering at Meta
爱范儿
爱范儿
G
Google Developers Blog
D
Docker
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
P
Proofpoint News Feed
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
S
Schneier on Security
T
Threatpost
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
T
The Exploit Database - CXSecurity.com
F
Fortinet All Blogs
A
About on SuperTechFans
Latest news
Latest news
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
P
Proofpoint News Feed
H
Help Net Security
L
LINUX DO - 热门话题
A
Arctic Wolf
C
CERT Recently Published Vulnerability Notes
Security Latest
Security Latest
Spread Privacy
Spread Privacy
I
InfoQ
V
Vulnerabilities – Threatpost
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LangChain Blog
Hacker News: Ask HN
Hacker News: Ask HN
腾讯CDC
大猫的无限游戏
大猫的无限游戏
P
Privacy & Cybersecurity Law Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
AWS News Blog
AWS News Blog
Cisco Talos Blog
Cisco Talos Blog
D
DataBreaches.Net
Simon Willison's Weblog
Simon Willison's Weblog

博客园 - 川川籽

GO面试题:new 和 map 的区别 minikube dashboard ImagePullBackOff 失败问题的解决方法 hashicorp/raft模块实现的raft集群存在节点跨集群身份冲突问题 macos 13安装openvpn - 川川籽 [转发] Go pprof内存指标含义备忘录 自己搭建一个https的dns,让不同的浏览器使用不同的DNS,使用相同的域名访问到不同的主机上 记一次docker buildx build 推送到本地私有仓库出现 connection refused 的问题 Linux C 获取本机IPV4和IPV6地址列表 Mac M1 安装python3.6.x golang map 和 interface 的一些记录 MacOS M1 安装python3.5 golang random string 【转载】coroutine 与 goroutine 区别 python简单的time ticker 'invalid flag in #cgo LDFLAGS: -w' 问题解决 记录一次python的mysqlclient依赖库报错问题 airflow当触发具有多层subDAG的任务的时候,出现[Duplicate entry ‘xxxx’ for key dag_id]的错误的问题处理 Python3并发写文件 python hash 每次调用结果不一样
使用php的openssl_encrypt和python的pycrypt进行跨语言的对称加密和解密问题
川川籽 · 2021-11-26 · via 博客园 - 川川籽

最近有一个业务需求,需要前端传递一个密码到后端,期间要对传递的密码通过进行对称加密,我们约定使用成熟的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.