永強持續教你加解密:對稱篇(二)

永強被嚇壞了!因爲永強看到了某個微信羣有人指出公衆號裏上篇打醬油附送的那篇文章《震驚!北京一男子竟然用swoole做了這種事!》的內容實在是太low了,這種low文章就不要拿出來發了。

但是給永強留下了面積巨大的心理陰影。他尚未見識過社交網絡的惡毒嘴臉。

所以永強本來昨天要發的文章拖到了今天,但是永強實在是怕了,他怕被人噴了被人罵了。雖然我百般鼓勵,但他還是心有餘悸。儘管我都已經直接告訴他“你那玩意根本就沒人看”了,他還是依然不敢發了。然後我不得不擺出PS大法給他做了一張圖,他看了看那張圖後又收了我6.66元的微信紅包,決定繼續鼓起勇氣發了。

是時候表現一下我的PS精湛技術了!

圖片描述

“我們歷經千辛萬苦,摸打滾爬過數不清的錯誤,發射了不知道多少枚長征系列,耗費了一代航天人的心血,終於看到了地球與月亮通信的曙光,然後就在五分鐘,我們驚訝地發現,原來老王的smartmesh技術早就實現了,甚至連地球文明與外形文明的通信都給出了完美的解決方案…” ——— 尼古拉斯 * 趙永強

衆所周知,作爲精通各種技術表演的我早就已經不屑於採用ppt的方式吹牛了,一般我都是直接上機操作表演,當然了,程序都是提前寫好了的,全是mock的假數據,腳本實現自動化,無論誰來操作都是流暢的,一切都是完美的!

作爲一個追求完美的人,我還得繼續接着吹上次聊到結尾,好像是遺留了兩個問題:

  • ecb、cfb、cbc等這些後綴是什麼意思
  • iv向量又是什麼意思

鑑於DES和3DES已經屬於不建議使用的方法了,所以這次我們直接用AES加密進行裝逼表演,比如下面這坨代碼,你們複製粘貼走運行一下:

<?php
$ava_methods = openssl_get_cipher_methods();
// 選用aes-128-ecb
$my_method   = 'aes-128-ecb';
if ( !in_array( $my_method, $ava_methods ) ) { 
  exit( '錯誤的加密方法'.PHP_EOL );
}
// 加密用的密碼
$key  = "1234567812345678";
// 加密的內容
$data = "12345678abcdxxoo12345678abcdxxoo";
$enc_data = openssl_encrypt( $data, $my_method, $key, OPENSSL_RAW_DATA );
$hex = bin2hex( $enc_data );
echo $hex.' : '.strlen( $hex ).PHP_EOL;

我這裏運行結果是:

c1391e34caf38f8c2a477cbda3772533c1391e34caf38f8c2a477cbda3772533d96aa42b59151a9e9b5925fc9d95adaf : 96

分析一下上面代碼:這次我們選用的加密方法是AES-128-ECB,這個128是什麼意思?128就是密鑰長度的意思:128bit;如果你留心的話,還會注意到有aes-192-ecb和aes-256-ecb,其實就是指加密密鑰長度爲192bit、256bit,然後是值得注意的一個地方是:

$enc_data = openssl_encrypt( $data, $my_method, $key, OPENSSL_RAW_DATA );
最後一個參數是OPENSSL_RAW_DATA,如果選用這個option的話,經過加密後的數據會是奇怪的二進制數據,無法直接通過文本方式查看,所以要看的話必須先使用bin2hex函數處理一下。

注意了哈,我選的這個密鑰1234567812345678是有特殊用意的,這個密鑰的長度是16字節也就是128bit,而我們選用的aes加密方法中要求的密鑰長度就是128bit,那麼我們嘗試將密鑰增加幾位變成:1234567812345678abc,然後其他代碼不做任何改動,再次執行加密,結果如下:

c1391e34caf38f8c2a477cbda3772533c1391e34caf38f8c2a477cbda3772533d96aa42b59151a9e9b5925fc9d95adaf : 96

就是說用“1234567812345678”和“1234567812345678abc”加密後的數據都是一樣的。看起來如果我們選用128bit密鑰長度的話,一旦密鑰長度超過128bit後面多餘的部分會被直接無視掉~~~

然後我們再嘗試將密鑰“1234567812345678”縮短一個字節,改成“123456781234567”,其他地方代碼不做任何改動,運行一波兒,結果如下:

c202e5b1dc36c3147e50d02df7ab700cc202e5b1dc36c3147e50d02df7ab700cda89b056d926d3fea2e59ffc552b1d98 : 96

這次不行了,已經不一樣了~

然後,我們將注意力放到明文和密文上來:

明文:12345678abcdxxoo12345678abcdxxoo
密文:c1391e34caf38f8c2a477cbda3772533c1391e34caf38f8c2a477cbda3772533d96aa42b59151a9e9b5925fc9d95adaf

仔細觀察有一個比較屌的地方,我們將密文每隔32個字符長度就分割一下,你們感受一下:

c1391e34caf38f8c2a477cbda3772533
c1391e34caf38f8c2a477cbda3772533
d96aa42b59151a9e9b5925fc9d95adaf

臥槽,竟然有前兩段是一樣的!???臥槽。。。 。。。

圖片描述

仔細看了一把明文12345678abcdxxoo12345678abcdxxoo,分析一下,臥槽!:

12345678abcdxxoo
12345678abcdxxoo

難道說明文“12345678abcdxxoo”被密鑰“1234567812345678”加密後後的密文就是“c1391e34caf38f8c2a477cbda3772533”?

時機已然成熟了!是時候繼續深入裝逼了!爲什麼會出現這個結果?現在我們開始說“ecb、cfb、cbc等這些後綴是什麼意思”。

你若有所思的猜測到:“難道說對稱加密的時候,都是將明文先分塊,然後再分別對分塊加密?”,我欣慰地看着你說:“嗯,是的,肯定是,不然老子往下沒法寫了,我特麼都快編不下去了…”

  • DES和3DES會將明文以64bit(8字節)作爲一個單元進行分組;
  • AES則會將明文以128bit(16字節)作爲一個單元進行分組;
  • 無論是AES還是DES,當最後一個分組的數據長度不滿足分組標準長度的時候,會用某種填充方式進行填充;
  • AES對一個16字節分組加密完畢後,分組大小依然爲16字節;

比如說這段明文“12345678abcdxxoo12345678abcdxxoo”,一共是32字節,理論上說就會被先按照16字節分組:“12345678abcdxxoo”是一組,剩下的“12345678abcdxxoo”是另外一組,我們用程序驗證一下:

<?php
$ava_methods = openssl_get_cipher_methods();
$my_method   = 'aes-128-ecb';
if ( !in_array( $my_method, $ava_methods ) ) { 
  exit( '錯誤的加密方法'.PHP_EOL );
}
$key  = "123456781234567";
// 注意!這段明文長度剛好爲32字節!
$data = "12345678abcdxxoo12345678abcdxxoo";
echo '明文長度:'.strlen( $data ).PHP_EOL;
$enc_data = openssl_encrypt( $data, $my_method, $key, OPENSSL_RAW_DATA );
echo '密文長度:'.strlen( $enc_data ).PHP_EOL;

注意明文長度我選擇剛好爲32字節。保存運行一下,至於你們那裏是什麼結果我不知道,反正我這裏是這樣的:

圖片描述

我日,感覺被打臉了,爲毛加密後多出了16字節?

我們將明文從32字節的“12345678abcdxxoo12345678abcdxxoo”修改成33字節的“12345678abcdxxoo12345678abcdxxooa”,這樣的話,明文會被分成三個16字節的分組,由於最後一個分組只有一個字節,所以剩餘15字節會被填充:

圖片描述

似乎印證了我們一個猜測:當最後一個明文分組小於要求分組標準大小時,不會產生新的分組;當最後一個明文分組大於等於要求分組標準大小時,會產生一個新的分組。

我不想填充怎麼辦?修改一下加密和解密函數的最後那個OPENSSL_NO_PADDING選項即可,你們感受一下:

<?php
$ava_methods = openssl_get_cipher_methods();
// 選用aes-128-ecb
$my_method   = 'aes-128-ecb';
if ( !in_array( $my_method, $ava_methods ) ) {
  exit( '錯誤的加密方法'.PHP_EOL );
}
// 加密用的密碼
$key  = "1234567812345678";
// 加密的內容
$data = "12345678abcdxxoo12345678abcdxxoo";
echo '明文:'.$data.',長度爲'.strlen( $data ).PHP_EOL;
$enc_data = openssl_encrypt( $data, $my_method, $key, OPENSSL_NO_PADDING );
echo '密文:'.$enc_data.',長度爲'.strlen( $enc_data ).PHP_EOL;
$hex = bin2hex( $enc_data );
echo "密文十六進制:".$hex.',長度爲'.strlen( $hex ).PHP_EOL;
$dec_data = openssl_decrypt( $data, $my_method, $key, OPENSSL_NO_PADDING );
$dec_data = openssl_decrypt( $enc_data, 'aes-128-ecb', $key, OPENSSL_NO_PADDING );
echo "解密:".$dec_data.PHP_EOL;

上面代碼運行一下,結果如下圖:

圖片描述

有時候一些同學在做跨語言加解密的時候,基本上都是栽在了填充上。具體表現就是PHP加密後讓Java解密,然後發現解密失敗;或者Java加密PHP解密結果也是掛了。這個時候首先檢查一下PADDING這裏,基本上都是這樣的問題。

那麼說了這麼多,總結一下:

AES和DES以及3DES這種加密方式被稱爲分組密碼,分組密碼每次只能加密固定長度的明文,所以如果明文很長的話,就需要輪流爲每個分組明文進行加密,AES的分組長度是128bit,而DES的分組長度爲64bit;如果一旦需要對多個分組進行輪流加密,加入明文被分成了三個明文分組,那麼就需要對三個明文進行迭代加密(粗暴理解就是輪流加密),然而會有很多種不同的迭代方式,這種不同的迭代方式專業名詞就叫“模式”,這些模式有:ECB、CBC、OFB、CFB、CTR… …

PS:⚠️對明文進行分組的方式是固定的,唯一不同的就是分組長度不一樣而已;模式是指對多個明文從第一個開始輪流加密到最後一個的這個過程,是怎麼輪流執行的。

圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章