要么改变世界,要么适应世界

带你了解CBC加密解密

2022-07-09 18:21:17
62
目录

简介

CBC加密技术属于分组模式加密指将前一个密文分组与当前明文分组的内容混合起来进行加密,这样就可以避免ECB模式的弱点。

对给定的随机密钥,每一块明文对应固定的密文块,即相同的明文组蕴含着相同的密文组,类似电码本的码字,这就是ECB模式

CBC模式的全称Cipher Block Chaining 模式(密文分组组链接模式),之所以叫这个名字是因为密文分组是像链条一样相互连接在一起。

本文参考自文章【Bugku Login4 (CBC字节翻转攻击)】

相关原理

加密过程

先看流程图: image-20220709182948691

  1. 分组:加密之前,先将要传输或者加密的明文数据进行分组,通常按照16个字节分为一组,如果最后一组不够16个字节,则会通过\x0n来补充,n代表补充的个数,例如最后一组的数组如果只有14个字节,那么后面两个字节以\x02进行填充,如果缺失的数目超过了10,则使用进十六制,例如如果最后一组数据只有6字节,那么最后10个字节都使用\x0a进行填充。我们称该明文为plain,每段明文我们成为plain[i]
  2. 生成初始化向量:向量长度和分组长度一致,用于和第一组向量进行异或运算。我们称该向量为iv
  3. 初始向量和第一段明文plain[1]进行异或运算,完事以后再经过特定的加密运算encrypt(),得到密文cipher[1]
  4. cipher[1]继续和明文plain[2]进行异或运算,完事以后再经过特定的加密运算encrypt(),得到密文cipher[2]
  5. ……
  6. 所有的明文都参与运算以后,将得到的cipher[i]组装合并,得到密文段。

解密过程

解密过程实际上就是将加密过程逆着走一遍。

此外,我们还应该明白异或的原理:

0 ⊕ 1 = 1
x ⊕ x = 0
x ⊕ 0 = x

如果数据A和密钥B异或得到密文C,要想恢复A,只要拿着密钥B再和密文C异或,即可恢复明文A,这是因为异或满足结合律,且:

A ⊕ B = C
则有:
A ⊕ B ⊕ B = C ⊕ B
即:
A ⊕ 0 = C ⊕ B
即:
A = C ⊕ B

还是先看流程图:

image-20220709185258788

  1. 分组:与明文分组长度一致。
  2. 将第一段密文cipher[1]进行解密decrypt(),该解密运算使用的函数应该和之前加密定义的函数互为逆函数,得到的中间数据再和之前加密过程定义的iv向量进行异或运算,即可获得第一段明文plain[1]
  3. 第一段密文cipher[1]再和经过解密后的第二段密文cipher[2]进行异或运算,得到第二代明文plain[2]
  4. ……
  5. 将得到的所有明文段组装,形成明文。

模拟

下面我将使用PHP代码模拟上述过程,为了简单起见,加密函数encrypt()就不写那么复杂,直接将待加密的数据和密钥异或,解密的时候将密文和密钥异或即可。

下面是CBC加密和解码的模拟:

<?php 
// 密钥
define("SECRET_KEY", 23);
// 产生长度为 16字节 的随机 iv 向量,一个字符在计算机内部使用8比特表示,即一个字节
function get_random_init_vector() {
	$random_iv = '';
    for ($i = 0;$i < 16;$i++) {
        $random_iv .= chr(rand(1, 128));
    }
    return $random_iv;
}

// 加密运算
function encode($data){
	$tmp = "";
	// 和密钥异或
	for ($i = 0;$i < strlen($data);$i++) {
        $tmp .= chr(ord($data[$i]) ^ SECRET_KEY);
    }
    return $tmp;
}


// 解密运算
function decode($data){
	$tmp = "";
	// 和密钥异或
	for ($i = 0;$i < strlen($data);$i++) {
        $tmp .= chr(ord($data[$i]) ^ SECRET_KEY);
    }
    return $tmp;
}

// CBC 加密
function cbc_encode($plain, $len){
	$iv = get_random_init_vector();
	// 分段
	$plain_tmp = array();
	for($i = 0;$i < $len;$i++){
		$plain_tmp[$i] = substr($plain, $i * 16, 16);
	}
	$cipher = "";
	// 对于每一段明文都要进行加密
	$last_cipher = $iv;
	for($i = 0;$i < $len;$i++){
		// 上一段密文先和本明文做异或运算
		$str_tmp = "";
		for ($j = 0; $j < 16; $j++){
			$str_tmp .= chr(ord($last_cipher[$j]) ^ ord($plain_tmp[$i][$j]));
		}

		// 再进行加密
		$cipher_i = encode($str_tmp);
		$cipher .= $cipher_i;
		$last_cipher = $cipher_i;
	}
	return array(
		'iv' => $iv,
		'cipher' => $cipher
	);
}


// CBC 解密
function cbc_decode($cipher, $iv, $len){
	// 分段
	$cipher_tmp = array();
	for($i = 0;$i < $len;$i++){
		$cipher_tmp[$i] = substr($cipher, $i * 16, 16);
	}
	$plain = "";
	// 对于每一段密文都要进行解密
	$last_cipher = $iv;
	for($i = 0;$i < $len;$i++){
		// 先进行解密
		$plain_i_tmp = decode($cipher_tmp[$i]);

		// 再和上一段密文做异或运算
		$str_tmp = "";
		for ($j = 0; $j < 16; $j++){
			$str_tmp .= chr(ord($last_cipher[$j]) ^ ord($plain_i_tmp[$j]));
		}
		$plain .= $str_tmp;
		$last_cipher = $cipher_tmp[$i];
	}
	return array(
		'plain' => $plain
	);
}


$mydata = "hello, everybody, this is a string for test.....";
$info = cbc_encode($mydata, 3);
echo "数据是:\n";
echo $mydata . "\n";
echo "加密后:" . "\n";
var_dump($info);

echo "根据加密后得到的密文进行解密,得到的数据:" . "\n";

$data = cbc_decode($info['cipher'], $info['iv'], 3);
var_dump($data);
?>

image-20220710144743355

历史评论
开始评论