密码工程学习笔记2

知识点归纳

最有收获的内容

知道了怎么选择工作模式

  • 坚决不用ECB

  • 通常是 CBC 和 CTR 二选一

  • 如果能较好地生成唯一的瞬时值,推荐使用CTR模式,不存在碰撞,消息泄露最少

  • 否则还是用CBC的随机IV模式,不好的瞬时值将产生严重的问题

碰撞与密文分析

  • 由于明文是结构化的,所以容易猜测通信数据中,哪些密文对应的明文是相同的。

  • CBC模式一旦出现了一个分组的碰撞,就会泄露一个密文分组的明文。

  • CTR模式没有碰撞问题,因为CTR模式下,任意两个密钥分组都不相同,所以从两个相同的明文或者相同的密文中不能获得更多的信息。

遇到的问题与解决过程

Q1:如何攻击分组密码?具体攻击手段?

《分组密码的攻击方法与实例分析》

  • 差分密码分析
  • 线性密码分析
  • 高阶差分密码分析
  • 截断差分密码分析
  • 不可能差分密码分析
  • 积分攻击
  • 插值攻击
  • 相关密钥攻击

Q2:iv是写在文件的头部,还是应该和密钥一样保护起来?

(一)iv是否有保护的必要?

  • 没有保护的必要。

  • 总:iv相当于在所有密文分组前,加了一个密文分组。其效力等同于一个密文分组。如果要保护iv,那就要保护所有的密文分组。

  • 即使攻击者知道了第一个密文分组是iv,在没有密钥的情况下,依旧无法解密出第一个明文分组。

  • 是否会泄露信息?也不会。iv 和 P1(第一个明文分组) 异或之后,被密钥加密,iv不会泄露对破译有用的信息。

  • 对接收者来说,iv只会影响到C1(第一个密文分组)的破译,对其他密文分组的破译没有影响。

(二)iv是否会泄露工作模式?

  • 如果将iv放在密文的首部,那么密文会比明文多16字节

  • 在攻击方不知道明文长度的情况下,不会泄露工作模式

  • 即便工作模式泄露,也没有恶劣影响

Q3:在SM4的OFB,CFB,CTR模式中,iv初始化向量有什么用?

  • OFB模式中,iv初始化向量 + key + 函数 ==> 第一个流密码 ==> 加密第一组明文
  • 之后,第一个流密码 + key + 函数 ==> 第二个流密码 ==> 加密第二组明文
  • CFB模式中,iv初始化向量等同于第0个密文分组,用于第一组明文的加密,生成第一组密文
  • 类似于CBC
  • CTR模式中,iv初始化向量用于辅助产生好的瞬时值,避免瞬时值出现重复,或者可以被预测
  • 不好的瞬时值将造成恶劣的影响

实践内容

使用OpenSSL实现SM4加解密文件

可以切换5种工作模式,ECB、CBC随机iv模式、OFB、CFB、CTR

#include "SM4.h"
void hex_str_to_byte(const char *in, int len, unsigned char *out)
{
	char *str = (char *)malloc(len);
    memset(str, 0, len);
    memcpy(str, in, len);
    for (int i = 0; i < len; i+=2){
        //小写转大写
        if(str[i] >= 'a' && str[i] <= 'f'){
        	str[i] = str[i] - 0x20;
		}
        if(str[i+1] >= 'a' && str[i] <= 'f'){
        	str[i+1] = str[i+1] - 0x20;
		}
        //处理前4bit
        if(str[i] >= 'A' && str[i] <= 'F'){
        	out[i/2] = (str[i]-'A'+10)<<4;
		}else{
			out[i/2] = (str[i] & ~0x30)<<4;
		} 
        //处理后4bit, 并组合起来
        if(str[i+1] >= 'A' && str[i+1] <= 'F'){
        	out[i/2] |= (str[i+1]-'A'+10);
		}else{
			out[i/2] |= (str[i+1] & ~0x30);
		}
	}
	free(str);
}
//输入对称加密算法 和 加密模式
//输出密文到文件 
int SM4_encrypt(const EVP_CIPHER *type,const char *filename,const char *outfilename)
{
	FILE *fp1 = fopen(filename,"rb");
	FILE *fp2 = fopen(outfilename,"wb");
	
	//初始化 
	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
	EVP_CIPHER_CTX_init(ctx);
	EVP_EncryptInit_ex(ctx,type,NULL,NULL,NULL);
	
	//随机生成密钥
	EVP_CIPHER_CTX_rand_key(ctx,SM4Key);
	
	//判断模式
	unsigned char iv[IV_LEN];
	if(type != EVP_sm4_ecb()){
		//随机iv模式
		EVP_CIPHER_CTX_rand_key(ctx,iv);
		EVP_EncryptInit_ex(ctx,type,NULL,SM4Key,iv);
		//先在密文头部写入16字节的iv 
		fwrite(iv,1,IV_LEN,fp2);
#ifdef DEBUG
		printf("iv: ");
		for(int i=0;i<IV_LEN;i++){
			printf("%02x",iv[i]);
		}
		printf("\n");
#endif
	}else{
		EVP_EncryptInit_ex(ctx,type,NULL,SM4Key,NULL);
	}
	
	//读取明文,写入密文 
	unsigned char inbuf[1024] = {0};
	unsigned char outbuf[4096] = {0};
	int outlen;
	int inlen = 0;
	while((inlen = fread(inbuf,1,sizeof(inbuf),fp1))){
		EVP_EncryptUpdate(ctx,outbuf,&outlen,inbuf,inlen);
		fwrite(outbuf,1,outlen,fp2);
		fflush(fp2);
	}
	int finlen;
	EVP_EncryptFinal_ex(ctx,outbuf,&finlen);
	fwrite(outbuf,1,finlen,fp2);
	fflush(fp2);
	
	fclose(fp1);
	fclose(fp2);
	EVP_CIPHER_CTX_cleanup(ctx);
	return 1;
} 


int SM4_decrypt(const EVP_CIPHER *type,const char *filename,const char *outfilename)
{
	FILE *fp1 = fopen(filename,"rb");
	FILE *fp2 = fopen(outfilename,"wb");
	
	//上下文初始化 
	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
	EVP_CIPHER_CTX_init(ctx);
	
	//读出iv,解密初始化 
	unsigned char iv[IV_LEN];
	if(type != EVP_sm4_ecb()){
		fread(iv,1,IV_LEN,fp1);
		EVP_DecryptInit_ex(ctx,type,NULL,SM4Key,iv);
	}else{
		EVP_DecryptInit_ex(ctx,type,NULL,SM4Key,NULL);
	}
	
	//读写输入文件,输出文件 
	unsigned char inbuf[1024] = {0};
	unsigned char outbuf[4096] = {0};
	int outlen;
	int inlen = 0;
	while((inlen = fread(inbuf,1,sizeof(inbuf),fp1))){
	//	printf("inlen = %d\n",inlen);
		EVP_DecryptUpdate(ctx,outbuf,&outlen,inbuf,inlen);
		fwrite(outbuf,1,outlen,fp2);
		fflush(fp2);
	}
	int finlen;
	EVP_DecryptFinal_ex(ctx,outbuf,&finlen);
	fwrite(outbuf,1,finlen,fp2);
	fflush(fp2);
	
	fclose(fp1);
	fclose(fp2);
	EVP_CIPHER_CTX_cleanup(ctx);
	return 1;
}

int main(int argc,const char* argv[])
{
	if(strcmp(argv[2],"-d")==0){
		//解密 ./a.out cbc -d -K aaaaaaaaaaaaaa 1.enc 1.jpg
		hex_str_to_byte(argv[4],KEY_LEN*2,SM4Key);
		if(strcmp(argv[1],"cbc")==0){
			SM4_decrypt(EVP_sm4_cbc(),argv[5],argv[6]);
		}else if(strcmp(argv[1],"ecb")==0){
			SM4_decrypt(EVP_sm4_ecb(),argv[5],argv[6]);
		}else if(strcmp(argv[1],"cfb")==0){
			SM4_decrypt(EVP_sm4_cfb(),argv[5],argv[6]);
		}else if(strcmp(argv[1],"ofb")==0){
			SM4_decrypt(EVP_sm4_ofb(),argv[5],argv[6]);
		}else if(strcmp(argv[1],"ctr")==0){
			SM4_decrypt(EVP_sm4_ctr(),argv[5],argv[6]);
		}else{
			printf("usage:  ./a.out cbc -d -K aaaaaaaaaaaaaa infile outfile");
			printf("没有%s解密模式\n",argv[1]);
			return 1; 
		}
	}else{
		//加密 ./a.out cbc 1.jpg 2.jpg
		if(strcmp(argv[1],"cbc")==0){
			SM4_encrypt(EVP_sm4_cbc(),argv[2],argv[3]);
		}else if(strcmp(argv[1],"ecb")==0){
			SM4_encrypt(EVP_sm4_ecb(),argv[2],argv[3]);
		}else if(strcmp(argv[1],"cfb")==0){
			SM4_encrypt(EVP_sm4_cfb(),argv[2],argv[3]);
		}else if(strcmp(argv[1],"ofb")==0){
			SM4_encrypt(EVP_sm4_ofb(),argv[2],argv[3]);
		}else if(strcmp(argv[1],"ctr")==0){
			SM4_encrypt(EVP_sm4_ctr(),argv[2],argv[3]);
		}else{
			printf("usage:  ./a.out cbc infile outfile");
			printf("没有%s加密模式\n",argv[1]);
			return 2;
		}
	}
#ifdef DEBUG 
	printf("key: ");
		for(int i=0;i<KEY_LEN;i++){
			printf("%02x",SM4Key[i]);
		}
		printf("\n");
#endif 
	return 0; 
}

运行结果:ECB模式

gcc SM4_ED.c -lcrypto -DDEBUG          #如果不想输出随机KEY的值,关闭Debug

./a.out ecb 1.jpg 2.jpg                #KEY = 5f22c2023ef43dfae0789870559d50ea

openssl sm4-ecb -K 5f22c2023ef43dfae0789870559d50ea -in 1.jpg -out 1.jpg.enc 

diff 1.jpg.enc 2.jpg                   #与openssl命令的结果一致

./a.out ecb -d -K 5f22c2023ef43dfae0789870559d50ea 2.jpg 3.jpg      #解密2.jpg 为 3.jpg

diff 1.jpg 3.jpg                       #解密结果和明文相同

运行结果:CBC模式

./a.out cbc 1.jpg 2.jpg

iv: 528d0855ec87cb539b9c359fa5cb005f
key: ee91523c69530f49069706c83601ee04

diff 2.jpg 1.jpg.enc     #由于a.out将iv写入了密文首部,而openssl命令没有写入iv,所以两种密文不同 

xxd 2.jpg | head -n 5

xxd 1.jpg.enc | head -n 5

openssl sm4-cbc -K ee91523c69530f49069706c83601ee04 -iv 528d0855ec87cb539b9c359fa5cb005f -in 1.jpg -out 1.jpg.enc 

./a.out cbc -d -K ee91523c69530f49069706c83601ee04 2.jpg 3.jpg

diff 1.jpg 3.jpg         #解密结果和明文相同

运行结果:OFB模式

运行结果:CFB模式

运行结果:CTR模式

微精通

CTR模式使用初始化向量iv

博客:CFB模式解读

SM1算法的EBC、CBC、OFB模式及其介绍

博客:iv初始化向量的安全问题

关于CBC模式下iv的问题

《Windows C/C++ 加解密实战》

博客:EVP中文手册

OpenSSL官方文档

Linux man -k命令 grep -nr命令

我的码云链接

posted @ 2023-02-28 15:16  191206  阅读(125)  评论(0编辑  收藏  举报