一、OpenSSL简介
在计算机网络上,OpenSSL是一个开放源代码的[软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。这个包广泛被应用在互联网的网页服务器上。
SSL是Secure Sockets Layer
(安全套接层协议)的缩写,可以在Internet
上提供秘密性传输。Netscape
公司在推出第一个Web
浏览器的同时,提出了SSL
协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet
上保密通讯的工业标准。
1、官网网址
地址:https://www.openssl.org
。
2、GitHub地址
地址:https://github.com/openssl/openssl
。
二、SM4简介
SM4(国密算法)是由中国国家密码管理局(State Cryptography Administration
,SCA
)提出的分组密码算法,是一种对称加密算法。它是中国国家商用密码算法,也是 ISO/IEC
标准(ISO/IEC 18033-3:2010
)中的一部分。SM4
算法被广泛用于中国国内的商用加密应用中。
1、SM4特点
- 分组密码:
SM4
是一种分组密码,它将明文和密钥按照固定长度的分组进行加密和解密。 - 分组长度: 分组长度为 128 位(16 字节)。
- 密钥长度: 支持密钥长度为 128 位(16 字节)。
- 加密模式:
SM4
支持ECB
(Electronic Codebook
)模式,这是一种基本的分组密码加密模式。 - 代替和置换网络(Substitution-Permutation Network,SPN)结构:
SM4
算法采用SPN
结构,包括多轮的代替(Substitution
)和置换(Permutation
)操作,以增加加密的难度和安全性。 - 非线性和线性运算:
SM4
算法中的代替操作(S-Box
替代)是非线性的,而置换操作是线性的,这种结合增加了算法的安全性。
2、SM4安全性
SM4
算法的设计目标是提供高度的安全性,抵抗各种已知的攻击。它经过了广泛的密码学分析和测试,并且已经被国际上的密码学专家接受。
3、SM4使用场景
SM4
算法主要用于商用加密领域,例如金融领域的安全支付、电子身份认证、电子票据等。
SM4
算法的具体细节和设计原理可以在国家密码管理局的官方文档中找到。请注意,SM4
算法是受到专利保护的,使用 SM4
算法需要遵循相关的法律法规和标准。
4、加密模式
- ECB(Electronic Codebook)模式:
ECB
模式将明文分成固定大小的块,然后每个块单独加密,加密后的块串联起来形成密文。在ECB
模式下,相同的明文块会被加密成相同的密文块,因此ECB
模式不提供重放攻击的保护,而且不适用于对长文本的加密,因为长文本可能会有相同的块,导致相同的密文块 - CBC 模式(Cipher Block Chaining): 在
CBC
模式中,每个明文块在加密之前会与前一个密文块进行异或运算。这样可以使相同的明文块在加密时得到不同的密文块,增加了安全性。 - CFB 模式(Cipher Feedback):
CFB
模式中,密文块被反馈到加密算法的输入,然后再与明文进行异或运算,生成密文。CFB
模式可以实现分位加密,适用于流数据的加密。 - OFB 模式(Output Feedback):
OFB
模式中,密文块被反馈到加密算法的输入,生成一个密钥流(keystream
),然后将密钥流与明文进行异或运算,生成密文。OFB
模式可以实现分位加密,且具有自同步性质。 - CTR 模式(Counter):
CTR
模式中,每个明文块都与一个唯一的计数器值进行加密,计数器的值可以是明文块的块序号。CTR
模式不需要反馈,可以并行加密,适用于高速加密需求。
三、获取预编译版OpenSSL
1、下载
如果自己编译OpenSSL
比较麻烦,则可以下载预先编译好的OpenSSL
库。地址如下:
https://slproweb.com/products/Win32OpenSSL.html
点击下载对应版本 MSI
即可。
下载后如下:
2、安装
直接双击安装即可。
安装后目录结构如下:
四、OpenSSL+VS2019+SM4加解密
1、C++代码
#include <iostream>
#include <string>
#include <chrono>
#include <openssl/evp.h>
#include <openssl/rand.h>
std::string strKey = "a56egSpSWd5e67c3";
// 加密函数
void SM4Encrypt(const unsigned char* plaintext, int plaintextLength, const unsigned char* key, unsigned char* ciphertext, int* ciphertextLength)
{
EVP_CIPHER_CTX* ctx;
int len;
// 创建并初始化上下文
ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_sm4_ecb(), NULL, key, NULL);
// 加密数据
EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLength);
*ciphertextLength = len;
// 结束加密过程
EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
*ciphertextLength += len;
// 释放上下文
EVP_CIPHER_CTX_free(ctx);
}
// 解密函数
void SM4Decrypt(const unsigned char* ciphertext, int ciphertextLength, const unsigned char* key, unsigned char* plaintext, int* plaintextLength)
{
EVP_CIPHER_CTX* ctx;
int len;
// 创建并初始化上下文
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_sm4_ecb(), NULL, key, NULL);
// 解密数据
EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLength);
*plaintextLength = len;
// 结束解密过程
EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
*plaintextLength += len;
// 释放上下文
EVP_CIPHER_CTX_free(ctx);
}
int main()
{
char buff[4096];
FILE* fp = fopen("testxml.xml", "rt");
if (fp)
{
std::string strRaw;
while (fgets(buff,sizeof(buff),fp))
{
strRaw += std::string(buff);
}
printf("【原始串《长度=%d》】=%s.\n",strRaw.length(), strRaw.c_str());
auto start = std::chrono::steady_clock::now();
// 加密
unsigned char ciphertext[50280];
int ciphertextLength = 0;
SM4Encrypt((unsigned char*)strRaw.c_str(), strRaw.length() - 1, (unsigned char*)strKey.c_str(), ciphertext, &ciphertextLength);
printf("\n【加密串】= ");
for (int i = 0; i < ciphertextLength; ++i)
{
printf("%02X", ciphertext[i]);
}
printf("\n");
auto end = std::chrono::steady_clock::now();
// 计算时间间隔
std::chrono::duration<double> duration =
std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
// 输出时间间隔
std::cout << "耗时: " << duration.count() << " seconds\n";
}
return 0;
}
2、头文件包含
3、库目录包含
添加 "libcrypto.lib" "libssl.lib"
文件。
4、运行效果
五、OpenSSL+VC6.0+SM4加解密
1、代码适配
将Visual Studio
的代码移植到 VC6.0
时,会报错。解决方法如下:
①、修改openssl/macros.h文件:
注释箭头指向的 "__declspec(restrict)"
内容:
②、修改openssl/bio.h文件:
VC6.0
没有 "uintptr_t"
,需要增加 "typedef unsigned int uintptr_t"
,如第一个箭头指向处: