一: 非对称加密
1:非对称加密特点
-
密钥对:公钥、私钥
公钥加密的数据,只有对应的私钥解密 私钥加密的数据,只有对应的公钥解密
-
加密速度慢,效率低。加密强度高。
2:主要的应用场景
2.1 分发密钥 – 保护对称加密的密钥
核心思想:公钥加密
使用 “对端的公钥” 加密数据。对端,使用自己的 私钥解密数据。
实现流程
1. A、B两端
2. A端生成密钥对(公钥、私钥)。 将公钥公开。
3. B端获取公钥。
4. B端生成对称加密密钥(随机字符串)。使用A的公钥加密。---> 密文。
5. B端将密文发送给A端。
6. A端使用自己的 私钥 解密。得到对称加密密钥。
7. A、B端,使用相同的对称加密,加密数据。```
#### 2.2 数字签名 -- 校验数据, 验证对方身份
核心思想:私钥加密
使用自己的 私钥加密数据的散列值。对端使用公钥解密。比对散列值。
```c
实现流程
1. A、B两端。A端签名。B端校验签名。
2. A端,生成密钥对。公钥公开。
3. A端 生成原始数据,使用hash算法得到散列值。
4. A端 使用自己的私钥,对散列值加密。——> 密文。
5. A端 将 “原始数据” + 密文 发送给 B端。
6. B端,使用 A 的公钥解密密文,得到 散列值old。
7. B端,将 A 发送的原始数据,使用相同 hash算法,生成 散列值new。
8. B端,比对散列值old == 散列值new
- 相同:数据未被篡改,数据所有者为 A。验证成功!
- 不同:验证失败。```
## 3: 生成密钥对 -- RSA-API
```c
#include <openssl/rsa.h>
// 申请一块内存, 存储公钥和私钥
RSA *RSA_new(void); // 使用公钥、私钥前,必须先获取RSA类型内存。
// 创建较大数对象
BIGNUM* BN_new(void);
// 初始化较大数
int BN_set_word(BIGNUM *a, unsigned int w);
参数:
- a:BN_new函数返回值。
- w:数值(5位数以内)。—— 越大,生成密钥的时间就越长。
// 释放BIGNUM对象
void BN_free(BIGNUM*);
// 生成密钥对, 存储在内存中
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
参数:
- rsa: RSA_new函数的返回值。
- bits: 密钥的长度。单位:bit(不是字节)。1024*N。 长度越长,加密速度越慢。
- e:初始化后的 较大数。BN_set_word(bn, 12345);
- cb: 回调。传NULL。
#include <openssl/pem.h>
// 写入文件的公、私钥数据并非原始数据, 是base64编码后的数据。生成pem格式文件。
int PEM_write_RSAPublicKey(FILE* fp, const RSA* 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:打开的文件指针。
- r: RSA类型对象。包含:公钥、私钥。
----------------------------------私钥特有
- enc: 生成私钥使用的加密算法(对称加密)。NULL
- kstr: 加密的密钥。 NULL.
- klen: 密钥长度. 0
- cb: 回调函数。NULL
- u: 回调使用的参数。NULL.
【使用时的错误】:
1. 解决控制台,一闪而过。项目右键 --- 属性 --- 连接器 -- 系统 --- 子系统 --- 控制台。
2. 运行,报错:OPENSSL_Uplink(78CA5340,08): no OPENSSL_Applink
- 错误原因,crypto.dll库实现时,使用到 applink.c 文件。制作库时,未导入。
- 解决方法:将如下代码,添加到测试程序。
extern "C"
{
#include <openssl/applink.c> // 将applink.c一起编译生成可执行文件。
};
参照笔记结尾处:【内部错误】小节。
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 读取的公、私钥文指针。
- r:传出。 读公钥文件 —— 传出公钥。
读私钥文件 —— 传出私钥。
- cb:回调函数。NULL
- u:回调参数。NULL ```
##### 封装了fopen()函数, 可以进行密钥对的写入和提取的相关函数
```c
// 创建bio对象 --- 封装了 FILE *fp = fopen();
BIO *BIO_new_file(const char *filename, const char *mode);
// 释放bio对象
int BIO_free(BIO *a);
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);
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中的【公钥】提取出来
RSA *RSAPublicKey_dup(RSA *rsa);
// 将RSA中的【私钥】提取出来
RSA *RSAPrivateKey_dup(RSA *rsa);```
## 4: 加密 -- API
```c
以块方式进行加密的, 加密的数据长度, 不能大于秘钥长度
假设: 密钥长度: 1024bit = 128byte
// RSA_PKCS1_PADDING
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: 明文或密文长度。
0 < flen <= 117(128-11)
- from: 传入。加密:明文(原始数据内容)
解密:密文
- to:传出。 加密:加密后的密文内存存储的位置。 加密的密文长度 == 密钥长度。
解密:明文内存存储的位置。
- rsa:
加密、解密使用的 公、私钥。
- padding: 函数内部自动填充。但,需要我们指定填充方案。
RSA_PKCS1_PADDING —— 对应的填充方案 11 字节。
【返回值】:
加密:返回加密后的密文长度。
解密:返回原始数据的长度。
加密生成的 ==密文长度 == 密钥长度==
- 原始数据 17字节 ——>加密得密文 ——> 128字节(密钥长度)——> 解密 ——>明文 17```
## 5: 数字签名 -- API
```c
// 数字签名
int RSA_sign(int type, const unsigned char *m, unsigned int m_length,
unsigned char *sigret, unsigned int *siglen, RSA *rsa);
参数:
- type: hash算法。(NID_md5/NID_sha1/NID_sha224...)
- m : 传入。 原始数据。(自动生成)
- m_length: m 的长度。0 < flen <= 117(128-11)
- sigret: 传出。密文(原始数据 求散列值 加密后的)
- siglen: 传出。sigret的长度。
- rsa:私钥!
返回值:
成功:1,失败:0
// 校验签名
int RSA_verify(int type, const unsigned char *m, unsigned int m_length,
const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
参数:
- type: hash算法。(NID_md5/NID_sha1/NID_sha224...)
- m : 传入。 原始数据。(接收对端发送的)
- m_length: m 的长度。0 < flen <= 117(128-11)
- sigbuf: 传入。密文(接收对端发送的)
- siglen: sigbuf长度。
- rsa:公钥钥!
返回值:
用来校验:
校验成功:1。
校验失败:0。 ```
# 二:对称加密
## 1:AES 知识
```c
AES是一套对称密钥的密码术,目前已广泛使用,用于替代已经不够安全的DES算法。所谓对称密钥,就是说加密和解密用的是同一个密钥,消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同,这里的密钥是双方保密的,不会让任何第三方知道。
对称密钥加密法主要==**基于块加密**,**选取固定长度的密钥**==,去==**加密明文中固定长度的块,生成的密文块与明文块长度一样**==。显然密钥长度十分重要,块的长度也很重要。如果太短,则很容易枚举出所有的明文-密文映射;如果太长,性能则会急剧下降。==**AES中规定块长度为128 bit**==,而==**密钥长度可以选择128, 192或256 bit**== 。暴力破解密钥需要万亿年,这保证了AES的安全性。```
## 2: 对称加密的特性
+ 加密、解密使用相同的密钥。加密较大数据。
+ 密钥长度:16 byte、24 byte、32 byte
+ 分组长度:16 byte!
+ 分组密文长度 == 分组明文长度 == 16 byte
## 3: 分组密码模式 -- 5种(自行了解)
+ CBC 模式: 密文分组连接模式
分组长度:16 byte!
需要 “初始化向量” 参与
“初始化向量” 本质:字符串。 长度:与分组长度一致。16 byte!
初始化向量 加密、解密时使用的 一致
## 4:AES 加解密 API
#### 4.1 生成封装后的密钥
```c
#include <openssl/aes.h>
# define AES_BLOCK_SIZE 16 // 明文分组的大小
// 封装加密时使用的密钥
int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
// 封装解密时使用的密钥
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
-参数:
userKey:加解密使用的对称密钥
bits:密钥长度,单位bit
key:传出。封装后的密钥。AES_KEY key --> &key;```
#### 4.2 CBC 分组模式加密 - (分组链接模式)
```c
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、out的数据长度。 密文长度 == 明文长度。
分组、填充函数内自动实现。长度,留出足够大空间。
len = 数据长度+\0
length = ((len/16)+1)*16
-key:
AES_set_encrypt_key、AES_set_decrypt_key 传出值。
-ivec:
初始化向量!本质:字符串。与分组长度一致。16字节。
-enc:
加密:# define AES_ENCRYPT 1
解密:# define AES_DECRYPT 0 ```