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

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

可是給永強留下了面積巨大的心理陰影。他還沒有見識過社交網絡的惡毒嘴臉。微信

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

是時候表現一下個人PS精湛技術了!網絡

圖片描述

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

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

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

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

鑑於DES和3DES已經屬於不建議使用的方法了,因此此次咱們直接用AES加密進行裝逼表演,好比下面這坨代碼,大家複製粘貼走運行一下:3d

<?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;

我這裏運行結果是:code

c1391e34caf38f8c2a477cbda3772533c1391e34caf38f8c2a477cbda3772533d96aa42b59151a9e9b5925fc9d95adaf : 96

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

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

圖片描述

相關文章
相關標籤/搜索