密码学基础

密码学基础

密码学就是专门研究编制密码和破译密码的技术科学

openssl

base64编码:

1
2
3
4
5
6
7
openssl enc -base64 -in img.png -out base64.txt

enc:通用加密指令
-base64:指定加密方式为 base64
-in 文件名:需要进行 base64 的文件
-out 文件名: base64 后文件保存到哪里(若无此参数则输出到控制台)
-e(默认):表示当前需要执行编码操作

image-20191119143540114

base64解码:

1
2
3
openssl enc -d -base64 -in base64.txt -out image.png

-d:表示当前需要执行解码操作

image-20191119143407533

md5:

查看 md5 输出到控制台中:

1
2
3
openssl dgst -md5 img.png

-md5:指明使用md5算法计算消息摘要,通过 help 查看支持的所有算法

image-20191119143730333

查看 md5 输出到文件中:

1
openssl dgst -md5 -out md5.txt img.png

image-20191119143856955

对称加密:

1
2
3
4
openssl enc -des-cbc -in img.png -out encrypt.png -pass pass:123456

-des-cbc:指定加密方式和分组方式,使用 help 查看所有
-pass:指定对称加密使用的key

image-20191119144152792

调用openssl的API

配置项目:

image-20191119144422490

base64:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>

#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#pragma comment(lib, "libcrypto.lib")

// 1. 在项目属性的目录中添加 openssl 的 lib 和 include 路径
// 2. 用到了什么东西就加上相应的头文件
// 3. 必须需要链接到 libcrypto.lib 静态库

// 要求传入一个需要加密的串,以及串的长度,参数三是否需要换行,返回编码后的数据
char* Base64Encode(const char* input, int length, bool with_new_line)
{
// 创建一个 base64 对象,对象的特点就是使用 write 写入的
// 数据会被自动编码,使用 read 读取的数据会自动解码
BIO* b64 = BIO_new(BIO_f_base64());

// 默认编码之后存在换行符,通常不需要换行符
if (!with_new_line)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

// 再次创建了一个内存对象,对象的特点就是使用 write 写入的
// 数据会自动保存到某一个缓冲区。BIO_push 将两个对象进行关
// 联,也就是说传入的数据首先会进行 base64 编码,然后保存到
// 缓冲区。
b64 = BIO_push(b64, BIO_new(BIO_s_mem()));

// 将传入的数据进行编码,BIO_flush 将操作刷新到对象
BIO_write(b64, input, length);
BIO_flush(b64);

// 从 base64 对象中获取到相应的编码后的内容
BUF_MEM* bptr = NULL;
BIO_get_mem_ptr(b64, &bptr);

// 将编码后的数据拷贝到指定的位置
char* b64encode = new char[bptr->max]{};
memcpy(b64encode, bptr->data, bptr->max);

// 清理 BIO 对象,并返回结果
BIO_free_all(b64);
return b64encode;
}

char* Base64Decode(char* input, int length, bool with_new_line)
{
BIO* b64 = BIO_new(BIO_f_base64());

if (!with_new_line)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

// 编码后的长度和原文大概比例是 4:3,使用编码后的长度
// 进行解码,空间是绝对足够的
char* buffer = (char*)malloc(length);
if (buffer) memset(buffer, 0, length);

// 创建一个内存对象,存入编码后的内容,并关联到 base64 对象
BIO* bmem = BIO_push(b64, BIO_new_mem_buf(input, length));

// 对 base64 read 就是解码数据
BIO_read(bmem, buffer, length);

// 清理并返回原文
BIO_free_all(bmem);
return buffer;
}

int main()
{
auto b64e = Base64Encode("hello15pbo", 10, false);
printf("b64e: %s\n", b64e);

auto b64d = Base64Decode(b64e, strlen(b64e), false);
printf("b64d: %s\n", b64d);

return 0;
}

image-20191119144440003

消息摘要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
using namespace std;

#include <openssl/md5.h>
#include <openssl/sha.h>
#pragma comment(lib, "libcrypto.lib")

int md5_encrypt(const void* data, size_t len, unsigned char* md5)
{
// 初始化保存 md5 信息的结构体
MD5_CTX ctx = { 0 };
MD5_Init(&ctx);

// 将需要计算的数据传入到对应的结构中
MD5_Update(&ctx, data, len);

// 从结构中获取计算后的结果
MD5_Final(md5, &ctx);

return 0;
}

int sha1_encrypt(const void* data, size_t len, unsigned char* md5)
{
SHA_CTX ctx = { 0 };
SHA1_Init(&ctx);
SHA1_Update(&ctx, data, len);
SHA1_Final(md5, &ctx);
return 0;
}

int sha256_encrypt(const void* data, size_t len, unsigned char* md5)
{
SHA256_CTX ctx = { 0 };
SHA256_Init(&ctx);
SHA256_Update(&ctx, data, len);
SHA256_Final(md5, &ctx);

return 0;
}

void show_hex(const char* n, unsigned char* hex, size_t length)
{
printf(n);
for (int i = 0; i < length; ++i)
printf("%02X", hex[i]);
printf("\n");
}

int main()
{
unsigned char hex[100] = { 0 };

md5_encrypt("hello15pb", 9, hex);
show_hex("md5: ", hex, 16);

sha1_encrypt("hello15pb", 9, hex);
show_hex("sha1: ", hex, 20);

sha256_encrypt("hello15pb", 9, hex);
show_hex("sha256: ", hex, 32);

return 0;
}

image-20191119144525482

aes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <openssl/evp.h>
#pragma comment(lib, "libcrypto.lib")

// 返回值是加密后的密文长度,传入明文,明文长度和保存密文的缓冲区
int evp_en_cipher(unsigned char* source_string, unsigned char* des_string, int length)
{
// 创建一个通用加解密的对象,设置填充方式
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_set_padding(ctx, 1);

// once_length 是一次加密的长度, out_length 加密后密文的长度
int once_length = 0, out_length = 0;

// 设置加密时使用的算法+分组模式和 key,函数的倒数第二个参数可以
// 用于指定初始化向量,最后一个参数是 1 表示加密,否则解密
unsigned char key[16] = "15pb";
EVP_CipherInit_ex(ctx, EVP_des_ede3_ecb(), NULL, key, nullptr, 1);

// 使用 EVP_CipherUpdate + EVP_CipherFinal 完成整个加密
EVP_CipherUpdate(ctx, des_string, &once_length, source_string, length);
out_length += once_length;
EVP_CipherFinal(ctx, des_string + once_length, &once_length);
out_length += once_length;

// 清理对象并返回长度
EVP_CIPHER_CTX_free(ctx);
return out_length;
}

int evp_de_cipher(unsigned char* source_string, unsigned char* des_string, int length)
{
EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_set_padding(ctx, 1);

int once_length = 0, out_length = 0;

// 解密的代码和加密除 EVP_CipherInit_ex 的最后一个参数外基本相同
unsigned char key[16] = "15pb";
EVP_CipherInit_ex(ctx, EVP_des_ede3_ecb(), NULL, key, nullptr, 0);

EVP_CipherUpdate(ctx, des_string, &once_length, source_string, length);
out_length += once_length;
EVP_CipherFinal(ctx, des_string + once_length, &once_length);
out_length += once_length;

// 为加密后的数据添加空字符
des_string[out_length] = 0;

EVP_CIPHER_CTX_free(ctx);
return out_length;
}

int main(int argc, char* argv[])
{
unsigned char planttext[] =
"123456789123456789"
"123456789123456789"
"123456789123456789"
"123456789123456789"
"123456789123456789"
"123456789123456789";
unsigned char temp_string[1000] = { 0 };

int en_length = evp_en_cipher(planttext, temp_string, 108);
int de_length = evp_de_cipher(temp_string, temp_string, en_length);

printf("%s", temp_string);

return 0;
}

image-20191119144733193

rsa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <openssl\rsa.h>
#include <openssl\pem.h>
#include <openssl\rand.h>
#include <openssl\applink.c>
#pragma comment(lib, "libcrypto.lib")

#define PUB_KEY_FILE "pubkey.pem" // 公钥路径
#define PRI_KEY_FILE "prikey.pem" // 私钥路径

// 函数方法生成密钥对
void generate_rsa_key()
{
// 生成 rsa 密钥对, 参数一密钥长度,参数二公钥指数 e,参数三四可以不指定
RSA* keypair = RSA_generate_key(1024, RSA_F4, NULL, NULL);

// 从生成的密钥对中读取私钥到内存对象
BIO* pri = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
// 获取密钥长度并且申请空间进行保存
size_t pri_len = BIO_pending(pri);
char* pri_key = (char*)calloc(pri_len + 1, sizeof(char));
BIO_read(pri, pri_key, pri_len);
// 将生成的私钥写入到指定的文件中
FILE* private_file = nullptr;
if (fopen_s(&private_file, PRI_KEY_FILE, "w") == NULL)
{
if (pri_key && private_file)
{
fputs(pri_key, private_file);
fclose(private_file);
}
}

BIO* pub = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPublicKey(pub, keypair);
size_t pub_len = BIO_pending(pub);
char* pub_key = (char*)calloc(pub_len + 1, sizeof(char));
BIO_read(pub, pub_key, pub_len);
FILE* public_file = nullptr;
if (fopen_s(&public_file, PUB_KEY_FILE, "w") == NULL)
{
if (pub_key && public_file)
{
fputs(pub_key, public_file);
fclose(public_file);
}
}

// 释放对应的资源,防止泄露
RSA_free(keypair);
BIO_free_all(pub);
BIO_free_all(pri);

free(pri_key);
free(pub_key);
}

/*加密最大长度为加密长度-41*/
RSA* get_public_key()
{
// 打开公钥文件
FILE* public_file = nullptr;
if (fopen_s(&public_file, PUB_KEY_FILE, "r") == NULL)
{
// 从指定文件中读取公钥
RSA* rsa = PEM_read_RSAPublicKey(public_file, NULL, NULL, NULL);
if (public_file) fclose(public_file);
return rsa;
}
return nullptr;
}

RSA* get_private_key()
{
// 打开私钥文件
FILE* private_file = nullptr;
if (fopen_s(&private_file, PRI_KEY_FILE, "r") == NULL)
{
// 从指定文件中读取公钥
RSA* rsa = PEM_read_RSAPrivateKey(private_file, NULL, NULL, NULL);
if (private_file) fclose(private_file);
return rsa;
}
return nullptr;
}

BYTE* rsa_encrypt(BYTE* data, int data_len, RSA* rsa)
{
int rsa_len = RSA_size(rsa);
BYTE* encrypt = (BYTE*)malloc(rsa_len);

// RSA 1024 加密的明文分组长度不能超过 117, 生成的密文长度是 128
// 且 RSA 1024 不支持分组加密,意味着只能自己使用循环进行加密。
if (data_len > 117) return nullptr;

// RSA_PKCS1_PADDING 填充方式,随机添加一些数据进行加密和解密
RSA_public_encrypt(data_len, data, encrypt, rsa, RSA_PKCS1_PADDING);

return encrypt;
}


// 解密数据,
BYTE* rsa_decrypt(BYTE* data, RSA* rsa)
{
int rsa_len = RSA_size(rsa);
BYTE* decrypt = (BYTE*)malloc(rsa_len);
RSA_private_decrypt(rsa_len, data, decrypt, rsa, RSA_PKCS1_PADDING);

return decrypt;
}

int main()
{
// 生成 rsa 密钥对并读取到 rsa 对象中
generate_rsa_key();
RSA* public_key = get_public_key();
RSA* private_key = get_private_key();

// 构建需要加密的文件
BYTE plaintext[117] = { 0 };
memset(plaintext, 'a', 117);

// 加密和解密
BYTE* encrypt = rsa_encrypt(plaintext, 117, public_key);
BYTE* decrypt = rsa_decrypt(encrypt, private_key);

return 0;
}