密码学
加密三要素
- 加密三要素:
-
明文/密文
- 明文 -> 原始数据
- 密文 -> 加密之后的数据
-
秘钥
- 定长的字符串
- 对称加密 ->自己生成
- 非对称加密 -> 有对应的算法可以直接生成
-
算法
- 加密算法
- 解密算法
-
举例:
bash明文: 123 秘钥: 111 加密算法: 明文 + 秘钥 解密算法: 密文 - 秘钥 加密: 123 + 111 = 密文 == 234 解密: 234 - 11 = 明文 == 123
-
常用的加密方式
- 对称加密
- 秘钥比较短
- 秘钥只有一个
- 加密解密使用的秘钥是相同的
- 加密的效率高
- 加密强度相对较低( 相对于非对称加密 )
- 秘钥分发困难 -> 因为秘钥要保密不能泄露
秘钥不能直接在网络环境中进行发送
(如果A向B发送此时C可以拦截密钥并且修改发送的数据)
- 非对称加密
- 秘钥比较长
- 秘钥有两个, 所有的非对称加密算法都有生成密钥对的函数
- 这两个秘钥对保存到不同的文件中, 一个文件是公钥(比较小), 一个是私钥(比较大)
- 公钥 -> 可以公开的
- 私钥 -> 不能公开
- 加解密使用的秘钥不同
- 如果使用公钥加密,
必须
私钥解密 - 如果使用私钥加密,
必须
公钥解密
- 如果使用公钥加密,
- 效率低
- 加密强度相对较高( 相对于对称加密 )
- 秘钥可以直接分发 -> 分发的公钥
- 非对称加密举例:
bash
场景是A发送数据给B
1、如果A生成密钥对发送公钥给B,此时C也会截取公钥,这方式行不通(私钥加密公钥解密)
2、如果是B生成密钥对,B把公钥给A,A拿私钥解开,C是截取不了的,C只能拿到公钥拿不到私钥(公钥加密私钥解密)
对称加密
加密算法
- DES/3DES
- DES -> 已经被破解了, 不安全
- 秘钥长度 8byte
- 对数据分段加密, 每组8字节
- 得到的密文和明文长度是相同的
- 3DES -> 3重des
- 安全的, 效率比较低
- 对数据分段加密, 每组8字节
- 得到的密文和明文长度是相同的 == 8字节
- 秘钥长度24字节, 在算法内部会被平均分成3份, == 每份8字节
- 看成是3个秘钥
- 每个8字节
- 加密处理逻辑:
- 加密 -> 秘钥1 * 加密算法
- 解密 -> 秘钥2 * 解密算法
- 加密 -> 秘钥3 * 加密算法
- 三组秘钥都不同, 加密的等级是最高的
- 详解加密处理逻辑:
- 第一次加密使用 K1 得到了一个密文 C1。第二次使用 K2 对 C1 进行解密,实际上是在对 C1 做逆向操作,类似于还原,但由于密钥不同(K2 而非 K1),这个"解密"并不会恢复到明文,而是得到一个新的中间数据 C2。这个中间数据 C2 再经过第三次加密,使用 K3 得到最终的密文 C3。
- AES
- 最安全, 效率最高的公开的对称加密算法
- 秘钥长度: 16字节, 24字节, 32字节
- 秘钥越长加密的数据越安全, 效率越低
分组加密, 每组长度 16 字节
- 每组的密文和明文的长度相同 == 16byte
- Blowfish
- RC2/RC4/RC5
- IDEA
- SKIPJACK
bash
DES举例:
1.DES算法的密钥长度为八个字节,采用分组加密方式,每组八个字节。
2.加密时,数据被分成八字节一组进行加密,最终得到的密文与明文长度相同。
3.最后一组数据可能需要进行填充,解密时去除填充数据。
如果是八字节的倍数字节而最后一个字节是除了8意外的其它数字(0~7),会自动填充八个字节,然后在解密的时候去除填充的数据
如果不是八字节的倍数字节(例如17),那么后面就会被填充7(24-17),在解密的时候会去除七个字节
非对称加密
加密算法
- RSA(数字签名和密钥交换)
- 项目中用的是rsa
- ECC(椭圆曲线加密算法 - 数字签名)
- Diffie-Hellman(DH, 密钥交换)
- El Gamal(数字签名)
- DSA(数字签名)
秘钥交换过程
c
// 假设通信的双方为: 客户端C, 服务器端S
// Wie什么要交换?
/*
1. 非对称加密秘钥分发方便, 但是效率低 -> 改进: 需要使用对称加密
2. 使用对称加密 -> 秘钥分发困难 -> 改进: 使用非对称加密进行秘钥分发
- 分发是对称加密的秘钥, 本质就是一个字符串
*/
// 秘钥交换的过程:
/*
1. 在服务器端生成一个非对称加密的密钥对: 公钥, 私钥
2. 服务器将公钥发送给客户端, 客户端有了公钥
3. 在客户端生成一个随机字符串 -> 这就是对称加密需要使用的秘钥
4. 在客户端使用公钥对生成的对称加密的秘钥进行加密 -> 密文
5. 将加密的密文发送给服务器
6. 服务器端使用私钥解密 -> 对称加密的秘钥
7. 双方使用同一秘钥进行对称加密通信
*/
Hash算法 (单向散列函数)
特点:
- 不管原始数据有多长, 通过哈希算法进行计算, 得到的结果的长度是固定的
- 是一个二进制的字符串
- 只要是原始数据不一样, 得到的结果就不一样
- 原始数据差一丢丢, 得到的结果也是完全不同的(原始数据1234、1233只差一点,结果确实差很多,反之亦然)
- 有很强的抗碰撞性:不知道原始数据,不停地去试错这个原始数据看能不能得到相同的散列值,如果结果相同代表碰撞上了
- 碰撞: 原始数据不同, 但是通过同样的哈希算法进行计算能得到相同的结果
- 推导的结论:
- 数据不同得到的结果就不同
- 应用场景:
- 数据校验:在下载的网页下面可能有MD5|SHA-1,这是提供的一个散列值,我如果把文件下载下来后再进行哈希运算发现和它提供的散列值不一样那么代表程序被篡改过。
- 登录验证:比如在linux设置密码,在数据库保存的不是设置的原始密码,保存的是密码经过哈希运算保存的结果。
- 秒传:有个本地文件,里面的内容经过哈希运算生成散列值,会把这个散列值传给服务器,服务器会把这个哈希值和数据库已经存在的哈希值进行比对,如果数据库有这个哈希值就代表服务器有这个文件,这就表示上传成功(秒传成功)
- 不可逆
- 得到的结果不能推导出原始数据
哈希运算的结果:- 散列值
- 指纹
- 摘要
- 常见的哈希算法:
- MD4/MD5
- 散列值长度: 16字节
- 抗碰撞性已经被破解
- SHA-1
- 散列值长度: 20字节
- 抗碰撞性已经被破解
- SHA-2
- sha224
- 散列值长度: 224bit / 8 = 28byte
- sha256
- 散列值长度: 256bit / 8 = 32byte
- sha384
- 散列值长度: 384bit / 8 = 48byte
- sha512
- 散列值长度: 512bit / 8 = 64byte
- sha224
- SHA3-224/SHA3-256/SHA3-384/SHA3-512
Hash算法不可逆
Hash算法是不可逆的,如果要用Hash算法加密那么是无法还原成原来的数据,如果想要进行还原那么就要使用对称和非对称加密。
消息认证码 -> HMAC(对称密钥)
作用:
- 在通信的时候, 校验通信的数据有没有被篡改(完整性)
- 没有加密的功能
使用:- 消息认证码的本质是一个散列值
(原始数据 + 秘钥) * 哈希函数 = 消息认证码
- 最关键的数据: 秘钥
校验的过程:- 数据发送方A, 数据接收方B
- 在A或B端生成一个秘钥: X, 进行分发 -> A和B端都有了 秘钥: X
- 在A端进行散列值运算: (原始数据 + x) * 哈希函数 = 得到散列值
- 在A端: 将原始数据和散列值同时发送给B
- 在B端: -> AB端使用的哈希算法是相同的
- 接收数据
- 校验: (接收的原始数据 + x) * 哈希函数 = 散列值New
- 比较散列值: 散列值New 和 接收的散列值 是不是相同
- 相同: 没篡改
- 不同: 被修改了
缺点:- 秘钥分发困难
- 不能区分消息的所有者
数字签名(非对称密钥)
作用:
- 校验数据有没有被篡改(完整性)
- 鉴别数据的所有者(签名者,也就是使用私钥对数据进行签名)(消息的发送者)
- 不能对数据加密
数字签名的过程: -> 私钥加密数据
-解释:非对称加密加密数据的效率很低,并不是直接对数据进行加密,如果数据有1G非对称加密效率很低,所以加密是对原始值进行哈希运算的哈希值进行加密。- 签名方方生成一个非对称加密的密钥对, 分发公钥
- 使用哈希函数对原始数据进行哈希运算 -> 散列值
- 使用私钥对散列值加密 -> 密文
- 将原始数据和密文一起发送给接收者(原始数据是明文)原始数据可能被截取篡改那么校验的时候就是不同的散列值
校验签名的过程:- 校验方接收签名的一方分发的公钥
- 接收签名者发送的数据:
接收的原始数据 + 签名
- 对数据进行判断:
- 对
接收的原始数据
进行哈希运算 -> 散列值new
- 和签名的时候使用的哈希函数相同(必须相同)
- 使用公钥对签名(密文) 解密 -> 得到了散列值old
- 比较两个散列值
- 相同: 数据的所有者确实是A, 并且数据没有被篡改
- 不同: 数据的所有者不是A(错误的公钥解密生成其它散列值)或者数据被篡改了
openssl
openssl是个安全套接字层密码库(是可以对套接字通信的数据进行加密
),包括主要的密码算法、常用的密钥(非对称加密的密钥
)和证书封装管理功能及SSL协议,并openssL提供丰富的应用程序供测试或其它目的使用。ssl是Secure Sockets Layer(安全套接层协议)的缩写,可以在Intemet上提供秘密性传输Netscape公司在推出第一个Web浏览器的同时,提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet上保密通讯的工业标准。
windows流程
1、下载openssl
2、在vs中新建一个桌面先导
3、打开项目属性
4、openssl头文件目录路径拷贝
5、点击确定,这样openssl头文件目录就装好了
6、库(动态库)目录
7、包含头文件加的openssl表示子目录,如果你前面配置的include文件的目录时候,openssl的文件是在include文件里面的文件
8、附加依赖项
测试代码
测试代码如下:
c
#define _CRT_SECURE_NO_WARNINGS
#include <openssl/md5.h> // md5 头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getMD5(const char* str, char* result)
{
MD5_CTX ctx;
// 初始化
MD5_Init(&ctx);
// 添加数据
MD5_Update(&ctx, str, strlen(str));
// 计算结果
unsigned char md[16] = { 0 };
MD5_Final(md, &ctx);
for (int i = 0; i < 16; ++i)
{
sprintf(&result[i * 2], "%02x", md[i]);
}
}
int main()
{
char result[33] = { 0 };
getMD5("hello, md5", result);
printf("md5 value: %s\n", result);
system("pause");
return 0;
}
这个错误是找不到动态库
openssl在链接的时候是动态链接
在调用MD5_init才去调用库,但是这个库我们是没有指定的,我们需要再次进行指定。
8、接上,属性->链接器->输入->附加依赖项
好家伙给老子报错
解决方法1:
前面说MD5是16字节,这个的输出结果是16进制字符串的形式,每个字符都转换成16进制的格式再显示出来,两个16进制位占1个字节。
A--->65---->0010 0001--->0x41
解决方法2:
拷贝bin所在的目录地址,这是我的C:\OpenSSL-Win32\bin
linux流程
powershell
tar zxvf openssl-1.1.1c.tar.gzip //解压
进入解压后的文件
powershell
cd openssl-1.1.1c
一步步
powershell
./config
make
make test (可选)
sudo make install
测试
哈希
MD5api
bash
# define MD5_DIGEST_LENGTH 16 // md5哈希值长度
// 初始化函数, 初始化参数 c
int MD5_Init(MD5_CTX *c);
参数c: 传出参数
// 添加md5运算的数据-> 没有计算
// 该函数可以进行多次数据添加 -> 函数多次调用
int MD5_Update(MD5_CTX *c, const void *data, size_t len);
参数:
- c: MD5_Init() 初始化得到的
- data: 传入参数, 字符串
- len: data数据的长度
// 对添加的数据进行md5计算
int MD5_Final(unsigned char *md, MD5_CTX *c);
参数:
- md: 传出参数, 存储得到的哈希值 数组
- c: MD5_Init() 初始化得到的
// 通过传递的参数, 直接生成一个md5哈希值
// 只能添加一次数据
unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md);
参数:
- d: 传入, 要进行md5运算的字符串
- n: 字符串的的长度
- md: 传出, 存储md5的哈希值
返回值: 这个地址的函数第三个参数md地址
其它api
bash
# define SHA_DIGEST_LENGTH 20
# define SHA224_DIGEST_LENGTH 28
# define SHA256_DIGEST_LENGTH 32
# define SHA384_DIGEST_LENGTH 48
# define SHA512_DIGEST_LENGTH 64
int SHA1_Init(SHA_CTX *c);
int SHA1_Update(SHA_CTX *c, const void *data, size_t len);
int SHA1_Final(unsigned char *md, SHA_CTX *c);
unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md);
int SHA224_Init(SHA256_CTX *c);
int SHA224_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA224_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);
int SHA256_Init(SHA256_CTX *c);
int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA256_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);
int SHA384_Init(SHA512_CTX *c);
int SHA384_Update(SHA512_CTX *c, const void *data, size_t len);
int SHA384_Final(unsigned char *md, SHA512_CTX *c);
unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md);
int SHA512_Init(SHA512_CTX *c);
int SHA512_Update(SHA512_CTX *c, const void *data, size_t len);
int SHA512_Final(unsigned char *md, SHA512_CTX *c);
unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md);
案例
cpp
#define _CRT_SECURE_NO_WARNINGS
#define SHA_DIGEST_LENGTH 20
#include<iostream>
#include <openssl/sha.h>
using namespace std;
void sha1Test()
{
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, "hello", strlen("hello"));
SHA1_Update(&ctx, ",world", strlen(",world"));
unsigned char* md = new unsigned char[SHA_DIGEST_LENGTH];
char* res = new char[SHA_DIGEST_LENGTH * 2 + 1];//最终字符串
SHA1_Final(md,&ctx);
//格式转换
for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
{
sprintf(&res[i * 2], "%02x", md[i]);
}
//cout << "sha1: " << md << endl;
cout << "res: " << res << endl;
}
//void Test1()
//{
// char p[] = "hello";
// cout << "p: " << p << " " << strlen(p) << " " << sizeof(p) << endl;// 5 6
//
//}
int main()
{
//Test1();
sha1Test();
return 0;
}
哈希类的封装
cpp
// c++中不建议使用宏 -> 常量/枚举/内联->空间换时间
class MyHash
{
public:
enum HashType{M_MD5, M_SHA1, M_SHA224, M_SHA512, M_SHA384, M_SHA512};
MyHash(HashType type) // 得到一个哈希对象, 创建不同的哈希对象
{
m_type = type;
switch(type)
{
case M_MD5:
MD5_Init();
break;
case M_sha1:
SHA1_Init();
break;
}
}
~MyHash();
// 添加数据
void addData(string str)
{
switch(m_type)
{
case M_MD5:
MD5_Update();
break;
case M_sha1:
SHA1_Update();
break;
}
}
// 计算哈希值
string result()
{
switch(m_type)
{
xxx_Final();
// 转换 -> 16进制格式
}
}
private:
HashType m_type;
MD5_CTX m_md5;
}
非对称加密
RSA 算法密钥长度越长,安全性越好,加密解密所需时间越长。
密钥长度增长一倍,公钥操作所需时间增加约 4 倍,私钥操作所需时间增加约 8 倍,公私钥生成时间约增长 16 倍;
复习:
- 特点
- 秘钥是一个密钥对:
公钥
,私钥
- 公钥加密, 必须私钥解密
- 私钥加密, 必须公钥解密
- 加密强度比较高, 效率低
- 不会使用非对称加密, 加密特别大的数据
- 秘钥是一个密钥对:
- 应用场景:
- 秘钥分发 -> 对称加密
- 核心思想: 加密的时候,
公钥加密, 私钥解密
- 分发步骤:
- 假设A, B两端
- A端生成了一个密钥对, 分发公钥, B端有了公钥
- B端生成一个对称加密的秘钥, 使用公钥加密 -> 密文
- B将密文发送给A
- A接收数据 -> 密文, 使用私钥对密文解密 -> 对称加密的秘钥
- 核心思想: 加密的时候,
- 签名 -> 验证数据是否被篡改, 验证数据的所有者
- 核心思想:
私钥加密, 公钥解密
- A, B两端, 假设A要发送数据
- A端生成一个密钥对, 将公钥进行分发, 自己留私钥
- 签名
- A对原始数据进行哈希运算 -> 哈希值
- A使用私钥对哈希值加密 -> 密文
- 将原始数据+密文发送给B
- 校验签名
- B接收数据: 密文 + 收到的原始数据
- 使用公钥对密文解密 -> 哈希值old
- 使用has算法对收到的数据进行哈希运算 -> 哈希值new
- 比较这两个哈希值
- 相同: 校验成功
- 不同: 失败
- 核心思想:
- 秘钥分发 -> 对称加密
生成RSA密钥对
c
#include <openssl/rsa.h>
// 申请一块内存, 存储了公钥和私钥
// 如果想得到RSA类型变量必须使用 RSA_new();
RSA *RSA_new(void);
void RSA_free(RSA *);
BIGNUM* BN_new(void);
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 (n正整数)
- e: 比较大的数(5位以内)
- 通过 BN_new 得到对应的变量
- 初始化: BN_set_word(e, 12345);
- cb: 回调函数, 用不到, 直接写NULL
// rsa公钥私钥类型是一样的: RSA类型
// 将参数rsa中的公钥提取出来
RSA *RSAPublicKey_dup(RSA *rsa);
- rsa参数: 秘钥信息
- 返回值: rsa公钥
// 将参数rsa中的私钥提取出来
RSA *RSAPrivateKey_dup(RSA *rsa);
- rsa参数: 秘钥信息
- 返回值: rsa私钥
// 创建bio对象
// 密钥对写磁盘文件的时候, 需要编码 -> base64
// 封装了fopen
BIO *BIO_new_file(const char *filename, const char *mode);
参数:
- filename: 文件名
- mode: 文件打开方式和fopen打开方式的指定相同
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);
参数:
- bp: 通过BIO_new_file();函数得到该对象
- r: 传递一个RSA* rsa指针的地址, 传出参数-> 公钥/私钥
- cb: 回调函数, 用不到, 指定为NULL
- u: 给回调传参, 用不到, 指定为NULL
//
//
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);
// 写入文件中的公钥私钥数据不是原始数据, 写入的编码之后的数据
// 是一种pem的文件格式, 数据使用base64进行编码
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: 存储了密钥对
- 私钥独有的参数
- enc: 指定的加密算法 -> 对称加密 -> NULL
- kstr: 对称加密的秘钥 -> NULL
- klen: 秘钥长度 -> 0
- cb: 回调函数, 用不到, NULL
- u: 给回调传参, 用不到, NULL
加密
以块的方式进行加密的, 加密的数据长度, 不能大于秘钥长度
- 假设: 秘钥长度: 1024bit = 128byte
c
// 公钥加密
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 <= 秘钥长度-11
- from: 传入, 要加密/解密的数据
- to: 传出, 存储数据, 加密->存储密文, 解密->存储明文
- rsa: 秘钥: 公钥/私钥
- padding: 指定填充方案, 数据填充, 不需要使用者做
- RSA_PKCS1_PADDING -> 使用该方案会填充11字节
案例
cpp
#define _CRT_SECURE_NO_WARNINGS
#define SHA_DIGEST_LENGTH 20
#include<iostream>
#include<openssl/sha.h>
#include<openssl/rsa.h>
#include<openssl/pem.h>//将密钥写文件的时候
#include<openssl/aes.h>
extern "C"
{
#include<openssl/applink.c>
};
using namespace std;
void sha1Test()
{
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, "hello", strlen("hello"));
SHA1_Update(&ctx, ",world", strlen(",world"));
unsigned char* md = new unsigned char[SHA_DIGEST_LENGTH];
char* res = new char[SHA_DIGEST_LENGTH * 2 + 1];//最终字符串
SHA1_Final(md,&ctx);
//格式转换
for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
{
//第一个参数是存储格式化后的数组的地址
sprintf(&res[i * 2], "%02x", md[i]);
}
//cout << "sha1: " << md << endl;
cout << "res: " << res << endl;//字符串直接打印整个字符串
}
//生成rsa密钥对
void generateRsaKey()
{
//1、创建rsa变量
RSA* rsa = RSA_new();
//1.5、创建BIGNUM对象
BIGNUM* e = BN_new();
BN_set_word(e, 12345);
//2、生成密钥对 ---> 密钥对在内存中
RSA_generate_key_ex(rsa, 1024, e, NULL);
#if 1
RSA* pubKey = RSAPublicKey_dup(rsa);
RSA* priKey = RSAPrivateKey_dup(rsa);
//3、将密钥对写入到磁盘--->持久化
#if 0
FILE* fp = fopen("public.pem", "w");
PEM_write_RSAPublicKey(fp,rsa);
fclose(fp);
fp = fopen("private.pem", "w");
PEM_write_RSAPrivateKey(fp, rsa, NULL, NULL, 0, NULL, NULL);
fclose(fp);
#endif
BIO* bio = BIO_new_file("public-1.pem", "w");
PEM_write_bio_RSAPublicKey(bio, rsa);
BIO_free(bio);
bio = BIO_new_file("private-1.pem", "w");
PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
BIO_free(bio);
#endif
}
// 公钥加密
string encryptPublicKey()
{
//1、准备加密数据
string text = "让编程改变世界...";
//2、准备密钥->公钥
//从磁盘文件读密钥
//使用bio的方式
BIO* bio = BIO_new_file("public-1.pem", "r");
RSA* pubKey = RSA_new();//不new会报错
if (PEM_read_bio_RSAPublicKey(bio, &pubKey, NULL, NULL) == NULL)
{
cout << "读公钥失败了..." << endl;
return string();
}
BIO_free(bio);//读完释放
//3、加密->密文
//数据被加密之后,长度和密钥长度相同
//通过函数计算密钥长度
int keyLen = RSA_size(pubKey);
char* buf = new char[keyLen];
//将加密的数据存到buf里 返回值就是密文长度
int len=RSA_public_encrypt(text.size(), (const unsigned char *)text.data(),
(unsigned char*)buf, pubKey, RSA_PKCS1_PADDING);
//4、将密文返回
cout << "加密之后的数据:" << buf << endl;
cout << "加密之后的数据长度:" << len << endl;
return string(buf, len);
}
// 私钥解密
string decryptPrivateKey(string str)
{
//1、准备秘钥 ->私钥
//从磁盘文件读秘钥
//使用bio的方式
BIO* bio = BIO_new_file("private-1.pem", "r");
RSA* priKey = RSA_new();
//2、将读取的私钥放入priKey
if (PEM_read_bio_RSAPrivateKey(bio, &priKey, NULL, NULL) == NULL)
{
cout << "读私钥失败..." << endl;
return string();
}
BIO_free(bio);
//3、解密 -> 明文
//数据被加密之后, 长度和秘钥长度相同
//通过函数计算秘钥长度
int keyLen = RSA_size(priKey);
char* buf = new char[keyLen];
// 返回值是解密之后的数据长度 == 原始数据长度
int len = RSA_private_decrypt(str.size(), (const unsigned char*)str.data(),
(unsigned char*)buf, priKey, RSA_PKCS1_PADDING);
//4、将明文返回
cout << "buf:" << buf << endl;
return string(buf, len);
}
//签名和验证签名
void rsaSigAndVerfiy()
{
//1、签名数据
string text = "1234567890";
//2、密钥
RSA* pubKey = RSA_new();
RSA* priKey = RSA_new();
BIO* pubBio = BIO_new_file("public.pem", "r");
PEM_read_bio_RSAPublicKey(pubBio, &pubKey, NULL, NULL);
BIO_free(pubBio);
BIO* prilBio = BIO_new_file("private.pem", "r");
PEM_read_bio_RSAPrivateKey(prilBio, &priKey, NULL, NULL);
BIO_free(prilBio);
//3、签名
int len = RSA_size(priKey);
unsigned int outLen = 0;
unsigned char* out = new unsigned char[len];
RSA_sign(NID_sha1, (const unsigned char*)text.data(), (unsigned int)text.size(),
out, &outLen, priKey);
//给到用户的数据
string sigbuf((char*)out, outLen);
//4、验证签名
int ret = RSA_verify(NID_sha1, (const unsigned char*)text.data(), (unsigned int)text.size(),
(const unsigned char*)sigbuf.data(), (unsigned int)sigbuf.size(), pubKey);
cout << "ret:" << ret << endl;
}
void aesCBCCrypto()
{
//1、准备数据
const char* pt = "我是你爸爸的儿子的爸爸的儿子的爸爸的儿子的爸爸的儿子的爸爸的儿子的爸爸";
//2、准备密钥
const char* key = "1234567887654321";
//3、初始化密钥
AES_KEY encKey;
AES_set_encrypt_key((const unsigned char*)key, 128, &encKey);
//4、加密
//计算长度
int length = 0;
int len = strlen((char*)pt) + 1;
if (len % 16 != 0)
{
length = ((len / 16) + 1) * 16;
}
else
{
length = len;
}
unsigned char* out = new unsigned char[length];
unsigned char ivec[AES_BLOCK_SIZE];//块大小 ---> 16
memset(ivec, 9, sizeof(ivec));
//密文存储在out中
AES_cbc_encrypt((const unsigned char*)pt, out, length, &encKey, ivec, AES_ENCRYPT);
//5、解密
unsigned char* data = new unsigned char[length];
AES_KEY deckey;
memset(ivec, 9, sizeof(ivec));//还需要一次初始化
AES_set_decrypt_key((const unsigned char*)key, 128, &deckey);
AES_cbc_encrypt(out, data, length, &deckey, ivec, AES_DECRYPT);
//6、打印
cout << "还原的数据:" << data << endl;
delete[]out;
delete[]data;
}
int main()
{
//generateRsaKey();
//sha1Test();
//string str = encryptPublicKey();
//string str1 = decryptPrivateKey(str);
//cout << "解密数据: " << str1 << endl;
//rsaSigAndVerfiy();
aesCBCCrypto();
return 0;
}