一、对称加密
对称加密也被称为共享密钥加密,是一种将相同密钥用于加密和解密的加密方式。在对称加密中,加密和解密使用的是相同的密钥,因此需要确保密钥的安全性。
1.工作原理
对称加密使用一个密钥来加密和解密数据。发送方使用该密钥对数据进行加密,接收方使用相同的密钥对数据进行解密。这种加密方式非常快速,因为它只需要一个密钥来加密和解密数据。
以下是对称加密的基本过程:
发送方使用密钥对明文进行加密。
发送方将加密后的数据发送给接收方。
接收方使用相同的密钥对加密后的数据进行解密。
2. 优缺点
优点:
加密和解密速度快。
加密强度高,因为使用的是相同的密钥。
适合用于大量数据的加密。
缺点:
密钥管理困难,需要确保密钥的安全性。
不适合用于跨网络的通信,因为需要将密钥传输给接收方。
3. 应用场景
对称加密广泛应用于保护本地数据的安全,例如:
文件和文件夹加密
数据库加密
本地存储设备加密
EVP_CipherInit_ex()、EVP_CipherUpdate() 和 EVP_CipherFinal_ex() 是可用于解密或加密的函数。执行的操作取决于enc参数的值。加密时应设置为 1,解密时设置为 0,保持值不变为 -1。
// 创建密码上下文
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
// 清除密码上下文中的所有信息并释放与其关联的任何已分配内存,包括ctx本身。
// 应在使用密码的所有操作完成后调用此函数,以便敏感信息不会保留在内存中。
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
/**
函数作用:初始化密码上下文ctx
ctx : 由 EVP_CIPHER_CTX_new() 创建
type : 使用的算法类型,例如:EVP_aes_256_cbc()、EVP_aes_128_cbc()
impl :密码类型,如果impl为 NULL,则使用默认实现。一般都设置为NULL
key : 加密密钥
iv : 偏移量
enc : 1 - 加密;0 - 解密
**/
int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc);
/**
输入 in 缓冲区中的 inl 字节的数据并将加/解密数据写入 out。可以多次调用此函数来加/解密连续的数据块。
**/
int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl);
/**
输出 缓冲区中剩余的数据。必须在 EVP_CipherUpdate() 之后调用。
outm : 为输出缓冲区中剩余部分
**/
int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);
/**
启用或禁用填充。在使用 EVP_EncryptInit_ex()、EVP_DecryptInit_ex() 或 EVP_CipherInit_ex() 为加密或解密设置上下文后,应调用此函数。
默认情况下,加密操作使用标准块填充进行填充,并且在解密时检查并删除填充。
如果填充参数 padding 设置为零,则不执行填充,此时加密或解密的数据总量必须是块大小的倍数,否则将发生错误。
默认情况下填充是启用的。padding 为0 则禁用填充,否则启用填充
**/
int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *x, int padding);
#include
#include
#include
#include
unsigned char key[] = "0123456789abcdeF";
unsigned char iv[] = "1234567887654321";
/**
inBuf : 输入数据
inl : 输入数据长度
out : 输出缓冲区,由调用者确定其大小
cipher:算法类型。例如:EVP_aes_128_cbc()、EVP_aes_256_cbc()
enc: operator type , 1 is enc and 0 is dec
**/
int do_crypt(const char *inBuf, int inl, char *out, const EVP_CIPHER *cipher, int enc)
{
if(NULL == inBuf || NULL == out)
{
return -1;
}
int templ, total;
// 创建上下文
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
// 初始化.设置算法类型和加解密类型以及加密密钥和偏移量
EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, enc);
// 设置是否启用填充.默认是启用的
// 如果填充参数为零,则不执行填充,此时加密或解密的数据总量必须是块大小的倍数,否则将发生错误。
EVP_CIPHER_CTX_set_padding(ctx, 1);
// key 和 iv 长度断言检查。断言长度随着 cipher 的不同而不同
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
templ= 0;
total = 0;
if (!EVP_CipherUpdate(ctx, out, &templ, inBuf, inl))
{
printf("EVP_CipherUpdate fail...");
goto err;
}
total += templ;
if (!EVP_CipherFinal_ex(ctx, out + total, &templ))
{
printf("EVP_CipherFinal_ex fail...");
goto err;
}
total += templ;
EVP_CIPHER_CTX_free(ctx);
return 0;
err:
EVP_CIPHER_CTX_free(ctx);
return -1;
}
int main(){
const char plaintext[] = "Hello World!";
char ciphertext[128];
char decryptedtext[128];
printf("plain text: %s\n",plaintext);
do_crypt(plaintext, strlen((const char*)plaintext),ciphertext,EVP_aes_128_cbc(),1);
printf("enc text: %s\n",ciphertext);
do_crypt(ciphertext, strlen((const char*)ciphertext),decryptedtext,EVP_aes_128_cbc(),0);
printf("dec text: %s\n",decryptedtext);
}
EVP_Encryp 和 EVP_Decryp 系列:
/**
设置密码上下文ctx以使用来自 ENGINE impl 的密码类型进行加密。ctx必须在调用此函数之前创建。类型通常由诸如 EVP_aes_256_cbc() 之类的函数提供。如果impl为 NULL,则使用默认实现。key是要使用的对称密钥,iv是要使用的 IV(如有必要),用于密钥和 IV 的实际字节数取决于密码。可以在初始调用中将除type之外的所有参数设置为 NULL ,并在后续调用中提供其余参数.
**/
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
ENGINE *impl, const unsigned char *key, const unsigned char *iv);
/**
加密inl缓冲区中的inl字节in并将加密版本写入out。可以多次调用此函数来加密连续的数据块。
写入的数据量取决于加密数据的块对齐。对于大多数密码和模式,写入的数据量可以是从零字节到 (inl + cipher_block_size - 1) 字节的任何内容。对于包装密码模式,写入的数据量可以是从零字节到 (inl + cipher_block_size) 字节的任何内容。
对于流密码,写入的数据量可以是从零字节到 inl 字节的任何内容。因此,out应该为正在执行的操作包含足够的空间。实际写入的字节数放在outl中
**/
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl);
/**
必须在 EVP_EncryptUpdate 之后调用,用来加密原文剩余部分。
**/
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
EVP_DecryptInit_ex()、EVP_DecryptUpdate()和EVP_DecryptFinal_ex()是对应的解密操作。如果启用了填充并且最终块的格式不正确,则 EVP_DecryptFinal() 将返回错误代码。
#include
#include
#include
using namespace std;
void symmetric_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
unsigned char *iv, unsigned char *ciphertext) {
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len);
ciphertext_len = len;
EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
ciphertext_len += len;
EVP_CIPHER_CTX_free(ctx);
}
void symmetric_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv, unsigned char *plaintext) {
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
}
int main() {
unsigned char key[] = "01234567890123456789012345678901";
unsigned char iv[] = "0123456789012345";
unsigned char plaintext[] = "Hello World!";
unsigned char ciphertext[128];
unsigned char decryptedtext[128];
symmetric_encrypt(plaintext, strlen((char *) plaintext), key, iv, ciphertext);
symmetric_decrypt(ciphertext, strlen((char *) ciphertext), key, iv, decryptedtext);
cout << "Plaintext: " << plaintext << endl;
cout << "Ciphertext: " << ciphertext << endl;
cout << "Decrypted text: " << decryptedtext << endl;
return 0;
}