程式狂想筆記

一個攻城師奮鬥史

0%

PHP 用 AES 加密解密結果作簽章不一樣 (PKCS) & 一些加密雜記

最近同事搞不定 PHP7 AES 加密
由於我們從 PHP5 升級到 PHP7
我們換成 openssl 但是到 Android APP 解憑證的時候竟然驗證結果會不一樣??
被同事抓去救火(同事也處理不行 XD)
然後不熟加密原理的我
第一件事情就是去 github 上面找 ChaoLiFL/PHP-Java-AES-Encrypt-Decrypt: Pass data AES encrypted between PHP 5.x, PHP 7.1, PHP 7.2 and Java. Support two ways of encryption and decryption: Base64, Hexadecimal.
沒想到剛好可以用!!! (運氣好)
不過還是卡在 簽章 結果會不一樣

AES 一些加密範例

今天又找了幾個 AES 加密

當日狀況

首先紀錄,先整理那天的狀態
原本我同事會先做 簽章 → AES 加密
他們做簽章 check 都通過
但是到 AES 一直失敗
後來他們使用 openssl 一直試 AES
我用了 ChaoLiFL/PHP-Java-AES-Encrypt-Decrypt: Pass data AES encrypted between PHP 5.x, PHP 7.1, PHP 7.2 and Java. Support two ways of encryption and decryption: Base64, Hexadecimal. 可以用了
(但可能沒有注意 console 後面有空白)
魔鬼總是藏在細節!!

後來套用我那個加密,發現 APP 做簽章反查竟然不正確??
這時候我同事發現後面有空白!!!
原因是這個導致最簽章反查導致不正確
但是 PHP 內容印出來也不正確

簽章

首先講一下,我目前認知簽章是怕內容有被串改
所以正常近來如如內容 array 會改他做 json_encode
在做 base64_url_encode(內容)
會做 hash_hmac PHP: hash_hmac - Manual

這時候還會做 base64 url encode(簽章)

why 用 base64 url encode ?
http://xxxx/?token=xxxxxxx
這時候不做 base64 url encode,可能就會有問題

這之間可能可以去換一些符號 (+/改為*-或._) 如上補充 url 不能放+/

裡面也有提到 base64 url encode
PS:Base64 - 維基百科,自由的百科全書,原因為何我也不確定,目前猜測可能是怕放在 url 上面??

這時候內容會是簽章+ .(間隔符號)+內容
出來的時候我們會用 AES 加密
這時候因為是 binary,所以還會做 Base64(也可以用自己去換一些符號)

APP 端接收到要做檢查
首先解 base64 url decode(或自訂反解 fucntion)
去做 AES 解密
這時候會是 binary(但是 PHP 出來是 string?) 這邊可能還要確認
我們要再做 base64 url decode

這邊講詳細一點
簽章+ .(間隔符號)+內容
這時候可以做反簽章
簽章跟內容是分開獨立各做base64 url decode

內容解出來,在自己做 hash_hmac
可以得到自己簽章內容

這時候跟風包給的簽章內容有沒有一致?
一致就是這個資料沒有被串改

簽章講完了,重頭戲要開始了

為什麼送到 Android 有奇怪的空白字元??(PKCS7)

是 PKCS7 的關係

簡單說明一下,因為 AES 加密必須是 16 倍數(應該= =|||,是聽到這個原因)
當你資料沒有給到一定的數字,它會自動幫你補

我們當下用 workaround 方式先去解在我們資料帶===講好符號先去解取
但最佳解不是這樣做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* PKCS7Encoder class
*
* 提供基于PKCS7算法的加解密接口.
*/
class PKCS7Encoder
{
public static $block_size = 32;


/**
* 对需要加密的明文进行填充补位
* @param $text 需要进行填充补位操作的明文
* @return 补齐明文字符串
*/
function encode($text)
{
$block_size = self::$block_size;
$text_length = strlen($text);
//计算需要填充的位数
$amount_to_pad = $block_size - ($text_length % $block_size);
if ( $amount_to_pad == 0 )
$amount_to_pad = $block_size;
//获得补位所用的字符
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ( $index = 0; $index < $amount_to_pad; $index++ )
$tmp .= $pad_chr;
return $text . $tmp;
}


/**
* 对解密后的明文进行补位删除
* @param decrypted 解密后的明文
* @return 删除填充补位后的明文
*/
function decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > self::$block_size)
$pad = 0;
return substr($text, 0, (strlen($text) - $pad));
}
}
// ---------------------
// 作者:qq_34089779
// 来源:CSDN
// 原文:https://blog.csdn.net/qq_34089779/article/details/79066961
// 版权声明:本文为博主原创文章,转载请附上博文链接!
1
2
3
4
5
6
7
8
9
10
11
12
13
function PKCS7Padding($str, $block_size) {
$padding_char = $block_size - (strlen($str) % $block_size);
$padding_str = str_repeat(chr($padding_char),$padding_char);
return $str.$padding_str;
}
function PKCS7UnPadding($str) {
$char=substr($str,-1,1);
$num=ord($char);
if($num>0 && $num <= strlen($str)) {
$str = substr($str, 0, -1 * $num);
}
return $str;
}

主要的填充算法有填充 NUL(“0”) 和 PKCS7,Mcrypt 默認使用的 NUL(“0”) 填充算法,當前已不被推薦,OpenSSL 則默認模式使用 PKCS7 對數據進行填充並對加密後的數據進行了 base64encode 編碼,所以建議開發中使用 PKCS7 對待加密數據進行填充,已保證通用性(alipay sdk 中雖然使用了 Mcrypt 加密簇,但使用 PKCS7 算法對數據進行了填充,這樣在一定程度上親和了 OpenSSL 加密算法)。

原文網址:https://kknews.cc/zh-tw/other/9pgvlpl.html

php 如何 openssl_encrypt 加密解密 - 个人文章 - SegmentFault 思否
有說OPENSSL_ZERO_PADDING會自動填充
但測試結果沒有效果 OPENSSL_RAW_DATA 方式【会用 PKCS#7 进行补位】

簡單說我對簽章的定義

簽章主要目的加密,而是防止資料有被串改

ebc 是沒有 iv 的,cbc 是有 iv 的
有用 iv 會比較安全,但是 iv 長度是有限制的
lyhiving/aes: 兼容 php、js、JAVA 的 AES 加密
詳細的我就沒研究

當天跟所有公司留到半夜 4:30 回去,也許這就是技術債!!!
希望不要有上班回家的時候不要再看到太陽出來了

題外小記
matrix 做翻書效果,搭配 向量矩陣 和缓动函数速查表
有很好展示效果

jlmakes/rematrix: Matrix transformations made easy.
The CSS3 Matrix Construction Set
演算法筆記 - Linear Function