永强持续教你加解密:对称篇(二)

永强被吓坏了!由于永强看到了某个微信群有人指出公众号里上篇打酱油附送的那篇文章《震惊!北京一男子居然用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:⚠️对明文进行分组的方式是固定的,惟一不一样的就是分组长度不同而已;模式是指对多个明文从第一个开始轮流加密到最后一个的这个过程,是怎么轮流执行的。

图片描述

相关文章
相关标签/搜索