欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

#对称加密和非对称加密

程序员文章站 2024-03-16 20:37:28
...

一、对称加密

​ 加解密使用同一个**,加密效率高,但是加密强度比较低,**分发困难,不能再网络环境中直接发送。对称**加密法主要基于块加密,选取固定长度的**,去加密明文中固定长度的块,生成的密文块与明文块长度一样。显然**长度十分重要,块的长度也很重要。如果太短,则很容易枚举出所有的明文-密文映射;如果太长,性能则会急剧下降对称加密有如下方法:

  • DES/3DES
    • DES现在已经不够安全
      • **长度为8字节
      • 加密方式为分组加密,每组8字节通过算法和**进行运算
      • 明文和密文长度相同
    • 3DES安全但是效率低
      • 三重数据加密, 对每个数据块应用三次DES加密算法
      • **长度为24字节,**会被平均分为三部分
      • 加密过程:加密->解密->加密
      • 解密过程:解密->加密->解密
  • AES
    • 安全,效率高,也是分组加密
    • **长度可选16byte, 24byte, 32 byte,分组长度和**长度相同
  • Blowfish
  • RC2/RC4/RC5
  • IDEA
  • SKIPJACK

二、非对称加密

​ 加解密使用的秘钥不同, 使用的是一个**对,加密效率低,加密强度高,秘钥长度比对称加密长,**对需要使用对应的非对称加密算法生成,这两个**对分别保存在两个文件中。公钥可以公开,私钥 不能公开, 要妥善保管。如果使用公钥加密, 必须使用私钥解密。如果使用私钥加密, 必须使用公钥解密。

  • RSA(数字签名和**交换)
    • 秘钥交换 -> 交换的对称加密的**
      1. 主机A::生成非对称加密的**对, 分发公钥给主机B。
      2. 主机B::得到了主机A的公钥,生成对称加密的秘钥, 使用得到的公钥进程加密,得到密文发送给主机A。
      3. 主机A:接收主机B加密之后的密文,使用私钥解密,得到明文(对称加密的秘钥)。
  • ECC(椭圆曲线加密算法)
  • Diffie-Hellman(DH, **交换)
  • El Gamal(数字签名)
  • DSA(数字签名)

三、哈希算法(单向散列函数)

​ 将任意长度的数据转换成固定长度的数据,长度由哈希算法确定。有很强的抗碰撞性:原始数据不同, 通过哈希算法进行运算, 得到的结果也不同。不可逆性:哈希运算的结果无法转换为原始数据。哈希运算得到的结果可称为:散列值、哈希值、指纹。哈希函数的散列值默认是二进制格式的,哈希算法得到的散列值常用于消息认证码和数字签名。常用的哈希算法有:

  • MD4/MD5,散列值长度为16byte
  • SHA-1,散列值长度为20byte
  • SHA224 ,散列值长度为28byte
  • SHA256,散列值长度为32byte
  • SHA384,散列值长度为48byte
  • SHA512,散列值长度为64byte

四、消息认证码

​ 使用消息认证码的目的是为了确认数据是否被他人篡改。消息认证码=(原始数据+**)进行哈希运算。发送的数据=(原始数据+消息认证码)。消息认证码使用的是对称加密的**。

  • 主机A:
    • 对原始数据和**进行哈希运算 -> 消息认证码
    • 把消息认证码放到原始数据的后边, 发送出去
  • 主机B:
    • 将消息认证码和原始数据拆分
    • 求原始数据和**的散列值
    • 比较散列值和消息认证码
      • 相同: 校验成功
      • 不同: 校验失败

缺点:无法确认数据是由通信的哪一方发送。而数字签名可以实现不可否认性。

五、 数字签名

数字签名是为了证明某条数据/信息属于某某人的, 数字签名使用非对称加密的**。

  • 签名过程:

    • 对原始数据进行哈希运算 -> 散列值
    • 使用非对称加密的私钥, 对散列值加密 -> 密文
    • 把密文放到原始数据的后边, 发送出去
  • 校验签名的过程:

    • 接收签名的数据
    • 原始数据和密文拆分
    • 使用哈希函数求原始数据的散列值 -> 散列值1
    • 使用公钥将密文解析出来 -> 散列值2
    • 比较两个散列值
      • 相同: 校验成功
      • 不同: 校验失败

六、案例

1、安装openssl

安装openssl教程网上比比皆是,且比较简单,这里不提供方法。

2、哈希
  • 头文件
#include <openssl/md5.h>
#include <openssl/sha.h>
  • MD5函数
// 散列值长度的宏 
# define MD5_DIGEST_LENGTH 16

// 初始化函数, 传出函数, 初始化一个MD5_CTX变量
int MD5_Init(MD5_CTX *c);

// 添加数据 -> 进行md5运算的数据
int MD5_Update(MD5_CTX *c, const void *data, size_t len);
	参数:
		- c: MD5_Init初始化得到的
		- data: 进行md5运算的数据
		- len: data字符串长度
// 计算md5散列值
int MD5_Final(unsigned char *md, MD5_CTX *c);
	参数:
		- md: 传出参数, 散列值
		- c: MD5_Init初始化得到的 

// 计算散列值的第二种方式,数据量比较少的时候使用
unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);
	参数:
		- d: 进行md5运算的数据
		- n: d字符串的长度
		- md: 传出参数, 散列值
  • ​ MD5函数案例
	//准备要求散列值的数据
	unsigned char str[] = "hello world!";
	MD5_CTX md5;
	//初始化md5
	MD5_Init(&md5);
	//添加求散列值的数据,需要减去末尾的‘\0’
	MD5_Update(&md5, str, sizeof(str) - 1);
	unsigned char finalMd[MD5_DIGEST_LENGTH];
	char res[MD5_DIGEST_LENGTH * 2 + 1]{ 0 };
	//求散列值
	MD5_Final(finalMd, &md5);
	//将二进制的散列值转换为十六进制
	for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
	{
		snprintf(&res[i * 2], sizeof(res), "%02x", finalMd[i]);
	}
	cout << "md: " << res << endl;
3、对称加密
  • AES生成加解密key函数
# define AES_BLOCK_SIZE 16	// 明文分组的大小
// 加密的时候调用
// aes中的秘钥格式 AES_KEY
int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
	参数:
        - userkey					**字符串, 长度可选: 16byte, 24byte, 32byte
        - bites					**的长度, 单位: bit -> byte = bit/8
        - key						传出参数: 保存了设置的秘钥信息
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
  • CBC方式加密 - 密码分组链接模式
//加解密都是这一个函数
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
                     size_t length, const AES_KEY *key,
                     unsigned char *ivec, const int enc);
	参数:
		- in: 要加密的数据/要解密的数据(明文/密文)
		- out: 加密-> 密文, 解密 -> 明文 (传出参数)
        - length: 第一个参数in的长度, 必须是16的整数倍, 如果不够需要填充
        	- 正好是16的整数倍: (strlen(in)+1) % 16 == 0
            - 不正好, 将大小进行扩充
            	length = ((stlen(in)+1) / 16 +1) * 16
        - key: 秘钥
        - ivec: CBC模式的初始化字符串, 和第一个明文分组进行位运算,16个字符
        	加解密的时候保证该数组中的数据完全相同
        - enc: 加解密的标志
        	# define AES_ENCRYPT     1
			# define AES_DECRYPT     0
  • AES案例
	//CBC的初始化向量
	unsigned char ivec[AES_BLOCK_SIZE];
	memset(ivec, 9, AES_BLOCK_SIZE);
	//准备要加密的数据
	unsigned char str[] = "准备要加密的数据:hello world!";
	//保证length是十六的整数倍
	int length = sizeof(str);
	length = length % 16 == 0 ? length : (length / 16 + 1) * 16;
	//得到加***
	AES_KEY encKey;
	unsigned char userKey[] = "1234567887654321";
	AES_set_encrypt_key(userKey, 128, &encKey);
	//对称加密是基于块加密,明文和密文长度一样
	unsigned char* encStr = new unsigned char[length];
	//使用CBC方式加密
	AES_cbc_encrypt(str, encStr, length, &encKey, ivec, AES_ENCRYPT);
	cout << "加密之后的数据: " << encStr << endl;

	// 解密
	AES_KEY decKey;
	//由于AES_cbc_encrypt中的ivec参数没有const修饰,所以可能是值-结果参数,这里重置一下
	memset(ivec, 9, sizeof(ivec));
	AES_set_decrypt_key(userKey, 128, &decKey);
	unsigned char* decText = new unsigned char[length];
	AES_cbc_encrypt(encStr, decText, length, &decKey, ivec, AES_DECRYPT);
	cout << "解密之后的数据: " << decText << endl;
4、非对称加密
  • 生成RSA**对
#include <openssl/rsa.h>
// 申请一块内存
RSA *RSA_new(void);
BIGNUM* BN_new(void);

// 生成**对, **对存储在内存中
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
	参数:
		- rsa: 结构体, 通过RSA_new()得到一个实例, **对最终存储在该变量中
		- bits: 秘钥的长度单位-bit, 字节数: bits/8 
        - e: BIGNUM结构体变量, 通过BN_new(),可以得到一个实例
        - cb: 函数指针, 通常不同, 指定为NULL

// 将参数rsa中的公钥提取出来
RSA *RSAPublicKey_dup(RSA *rsa);
// 将参数rsa中的私钥提取出来
RSA *RSAPrivateKey_dup(RSA *rsa);

// 写入文件中的公钥私钥数据不是原始数据, 写入的编码之后的数据
// 是一种pem的文件格式, 数据使用base64进行编码
BIO *BIO_new_file(const char *filename, const char *mode);
	参数:
		- filename: 操作的磁盘文件
		- mode: 对磁盘文件的操作方式 == fopen的打开文件的方式
			- r / w / r+ / w+ 
int PEM_write_bio_RSAPublicKey(BIO* bp, const RSA* r);
int PEM_write_bio_RSAPrivateKey(BIO* bp, const RSA* r, const EVP_CIPHER* enc, 
	unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
int PEM_write_RSAPublicKey(FILE* fp, const RSA* r);
	参数:
		- fp: 通过fopen打开公钥文件
		- r: 这边变量中有公钥信息
int PEM_write_RSAPrivateKey(FILE* fp, const RSA* r, const EVP_CIPHER* enc, 
	unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
	参数:
		- fp: 通过fopen打开私钥文件
		- r: 这边变量中有私钥信息
		- enc: 对私钥数据加密使用的加密算法 - 不加密指定为NULL
		- kstr: NULL, 据分析:这是对称加密算法的秘钥
		- klen: 对称加密的秘钥长度, 没有秘钥指定为0
		- cb: 回调函数, 不使用, NULL
		- u: 给回调函数传参, NULL


//读取RSA**对
RSA* PEM_read_bio_RSAPublicKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
RSA* PEM_read_bio_RSAPrivateKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
RSA* PEM_read_RSAPublicKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
RSA* PEM_read_RSAPrivateKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
	参数:
		- fp: 使用fopen打开公钥/私钥文件
		- r: 传出参数, 公钥/私钥信息被写入该变量
		- cb: 回调函数, NULL
		- u: 给回调传参, NULL
	返回值: 
		成功: 这个指针指向第二个参数的地址 r指针指向的地址
		失败: NULL
  • 加解密
int RSA_public_encrypt(int flen, const unsigned char *from,
                       unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, const unsigned char *from,
                        unsigned char *to, RSA *rsa, int padding);
int RSA_private_encrypt(int flen, const unsigned char *from,
                        unsigned char *to, RSA *rsa, int padding);
int RSA_public_decrypt(int flen, const unsigned char *from,
                       unsigned char *to, RSA *rsa, int padding);
参数:
	- flen: 要加密/解密的数据长度, 不能大于秘钥长度
			数据的最大长度: 秘钥长度 - 11
	- from: 要加密/解密的数据 -> 传入参数
	- to: 存储加密或解密得到的数据 -> 传出参数
	- rsa: 公钥/私钥数据
	- padding: 填充, 一般使用 RSA_PKCS1_PADDING ->11个字节
  • 签名
int RSA_sign(int type, const unsigned char *m, unsigned int m_length,
             unsigned char *sigret, unsigned int *siglen, RSA *rsa);
	参数:
		- type: 指定使用什么样的哈希算法
			NID_MD5/NID_SHA1/NID_SH224
		- m: 要进行签名的数据
		- m_length: 要签名的数据长度, 第二个参数字符串长度
		- sigret: 传出参数, 签名之后的数据
		- siglen: 得到的签名的长度
		- rsa: 私钥
int RSA_verify(int type, const unsigned char *m, unsigned int m_length,
               const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
	参数:
		- type: 指定使用什么样的哈希算法
		- m: 要验证签名的数据
		- m_length: 要签名的数据长度, 第二个参数字符串长度
		- sigret: 传入参数, 对方签名得到的数据, 要校验的签名
		- siglen: 要校验的签名的长度
		- rsa: 公钥
	返回值:
		- 校验失败: !=1
        - 成功: 1
5、其他

​ 在使用非对称加密函数时,需要包含一个源文件->applink.c,如果创建的是c++项目,需要使用extern。

extern "c"
{
    #include<openssl/applink.c>
}
相关标签: C++ c++