对称加解密概念及实践

关于

HTTP/3使用QUIC传输协议,而她底层使用TLS1.3变种协议进行密钥交换,其中涉及到密码学知识,先慢慢积累一些,看后面整理下成个专栏。

对称加解密

之所以称为对称,是因为加密和解密操作使用同一个密钥,需要注意加密密钥和密码(密码)的区别,前者可以根据后者生成(derive),这个涉及到密码学另一个概念,KDF(Key Derivation Function),本文不会涉及这个概念。

对称加密(Symmetric encryption)使用加密密钥(encryption key)加密数据。对称加密有多种算法(symmetric encryption algorithm),在密码学里面,也叫做cipher,后者更加泛,可以指对称加密算法,也可以是非对称加密的或者TLS中的算法套件等。

需要被加密的数据称为纯文本(plaintext),即使她是二进制数据,音乐或者视频等。加密后的数据称为密文(ciphertext)。 为了加密纯文本,需要以下要素

  1. 选择加密算法(cipher)
  2. 生成加密密钥(encryption key)
  3. 生成初始化向量 IV(Initialization Vector)
  4. 选择算法操作模式(cipher operation mode)或者加密模式(Encryption Mode)
  5. 选择填充类型(padding type)

根据不同算法,有些元素不是必须的,比如,流式算法和一些块式算法操作模式不需要填充(padding)。 对称加解密算法分为两类:块式算法和流式算法。

块式算法

顾名思义,块式算法以数据块为操作单位。比如流行的AES(Advanced Encryption Standard)算法的块大小(block size)为128位,意思是加解密以128位为单位操作,如果纯文本长度大于块大小,数据会分成多个块进行加解密。如果纯文本的长度不是块大小的倍数,最后一个块一般会填充到块大小,这里就需要用到填列类型了,不同的填充类型,有不同的填充方式。

块式算法大致工作流程

正因为块式算法以块为单位加解密,所以不同的操作引入了操作模式。根据操作可以分为ECB(Electronic Code Block), CBC(Cipher Block Chain), CTR(Counter), GCM(Galois Counter Mode)。所以AES根据不同的模式有AES_128_GCM, AEC_128_ECB等算法,中间128代表密钥长度(key length),后面的GCM代表算法操作模式。

Mode Formulas Ciphertext
Electronic codebook(ECB) Yi = F(PlainTexti, Key) Yi
Cipher block chaining(CBC) Yi = PlainTexti XOR Ciphertexti−1 F(Y, Key); Ciphertext0 = IV
Counter(CTR) Yi = F(IV + g (i), Key); IV = token() Plaintext XOR Yi

块式算法介绍

AES(Adanced Encryption Standard)也叫Rijndael加密算法。因为NIST组织选择Rijndael算法标准化后叫做AES。

AES被认为是目前最流行的对称加解密算法。如果你不确定使用什么对称加解密算法时,她是最好的选择,兼具速度和安全。目前X86系列和ARMv8都有相关的模块提供硬件加速。TLS1.3的安全套件中对称加解密使用这个算法,比如AES_128_GCM_SHA256AES_256_GCM_SHA384,上面2个密码套件里面使用key length分别为128bits和256bits。

流式算法

流式算法使用字节为单位或者以二进制位为单位,所以流式算法一般不需要填充,也不需要操作模式,但是有些算法研究者尝试着加入操作模式。因此流式算法更加简单的实现和使用,特别对于流式数据,整个过程相比块式算法更加快。但是密码学专家认为现存的流式算法安全性不如块式算法。

流式算法大致工作流程

首先根据加密密钥(encryption key)和初始化向量(IV)或者随机数(nonce)生成无限长度的伪随机密码数字流(pseudorandom cipher digit stream)或者密钥流(keystream)。整个过程相当于伪随机数发生器,只不过这里的seed是加密密钥和初始化向量。 加密的时候,一般使用密钥流和纯文本做异或(XOR)操作;相反,解密的时候,使用密文和密钥流做异或(XOR)操作。

流式算法介绍

ChaCha20是一种流行的流式加密算法,支持128bits和256bits密钥长度。主要的一般使用256bits的密钥长度,比如OpenSSL。ChaCha20在没有硬件加速的场景下比AES要快,比如手机等。

唯一不足的地方是,她使用32bits的block counter,所以理论上,256G是她的加密安全范围,但是网络数据一般没有那么大量,所以网络中加解密是绰绰有余的。ChaCha20一般不单独使用,和POLY1305(MAC算法)一起使用。TLS1.3中ChaCha20_POLY1305_SHA256套件就是使用她们。

算法应用实践

OpenSSL ChaCha20

c 复制代码
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>

void print_hex(char *label, unsigned char *data, int len)
{
	fprintf(stdout, "%s(%d): ", label, len);
	for (int i = 0; i < len; i++)
	{
		fprintf(stdout, "%02x ", data[i]);
	}
	fprintf(stdout, "\n");
}

unsigned char *hexstr2buf(const char *str)
{
	size_t len = strlen(str);
	if (len % 2 != 0)
		return NULL;
	size_t res_len = len / 2 + 1;
	unsigned char *res = (unsigned char *)malloc(res_len);

	for (int i = 0; i < len; i += 2)
	{
		sscanf(str + i, "%2hhx", res + i / 2);
	}
	*(res + res_len - 1) = '\0';
	return res;
}

int main()
{
	int ok = 0;
	/* 测试数据来源: https://datatracker.ietf.org/doc/html/rfc8439#section-2.4.2 */
	char *key_hex = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
	char *iv_hex = "01000000000000000000004a00000000"; // 4bytes(32bits)counter + 12bytes(96bits)nonce
	char *plaintext_hex = "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e";
	unsigned char *key, *iv, *plaintext;
	unsigned char out[4096];
	int outlen = 0;

	key = hexstr2buf(key_hex);
	iv = hexstr2buf(iv_hex);
	plaintext = hexstr2buf(plaintext_hex);

	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
	ok = EVP_CipherInit_ex(ctx, EVP_chacha20(), NULL, key, iv, 1);
	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 16, NULL);

	ok = EVP_CipherUpdate(ctx, out, &outlen, plaintext, strlen(plaintext));

	print_hex("out", out, outlen);

	outlen = 0;
	EVP_EncryptFinal_ex(ctx, out, &outlen);
	print_hex("out", out, outlen);

	OPENSSL_free(NULL);
	OPENSSL_free(key);
	OPENSSL_free(iv);
	OPENSSL_free(plaintext);
	EVP_CIPHER_CTX_free(ctx);

	return 0;
}

Python

Python中使用加解密第三方库主要有pycrypto, pycryptodom, pycryptography。pycrypto已经不再维护,pycryptodom可以兼容前者的接口。pycryptograph也是很推荐的密码学库,aioquic中使用了此库。

python 复制代码
# pyCrypto不在维护,pyCryptodom的API兼容前者。作者推荐pyCryptography和pyCryptodom
# pyCryptodom库的ChaCha20和ChaCha20-Poly1305是遵循RFC 8439(https://datatracker.ietf.org/doc/html/rfc8439)
# 但是不能指定counter
# pyCryptography 不遵循RFC 8439

# 运行
# python3 -m pip install pycryptodom
# python3 chacha20_poly1305.py

from binascii import hexlify

from Crypto.Cipher import ChaCha20, ChaCha20_Poly1305

# https://www.pycryptodome.org/src/cipher/chacha20
# PyCryptodome ChaCha20
# 测试数据来源: https://datatracker.ietf.org/doc/html/rfc8439#section-2.4.2
key = bytes.fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
nonce = bytes.fromhex("000000000000004a00000000")
cipher = ChaCha20.new(key=key, nonce=nonce)
plaintext = bytes.fromhex(
    "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e"
)

ciphertext = cipher.encrypt(plaintext)
print("ChaCha20 ciphertext: {hexlify(ciphertext)}")

# https://www.pycryptodome.org/src/cipher/chacha20_poly1305
# PyCryptodome ChaCha20-Poly1305
# 测试数据来源: https://datatracker.ietf.org/doc/html/rfc8439#section-2.8.2
cp_key = bytes.fromhex(
    "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
)
cp_iv = bytes.fromhex("070000004041424344454647")
aad = bytes.fromhex("50515253c0c1c2c3c4c5c6c7")
cp = ChaCha20_Poly1305.new(key=cp_key, nonce=cp_iv)
cp.update(aad)
cp_ciphertext, cp_tag = cp.encrypt_and_digest(plaintext)
print(f"ChaCha20-Poly1305 ciphertext: {hexlify(cp_ciphertext)}")
print(f"ChaCha20-Poly1305 tag: {hexlify(cp_tag)}")
相关推荐
栈老师不回家43 分钟前
Vue 计算属性和监听器
前端·javascript·vue.js
AskHarries1 小时前
Java字节码增强库ByteBuddy
java·后端
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
佳佳_1 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
许野平2 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app