Openssl数据安全传输平台009:加密理论基础:哈希/非对称加密RSA/对称加密AES

news2024/9/20 8:09:40

文章目录

  • 0. 代码仓库
    • 代码编译时候可能出现的错误
  • 1. 哈希
    • 1.1 哈希算法的种类:
    • 1.2 使用的头文件
    • 1.3 哈希算法API
      • 1.3.1 详解md5 API
      • 1.3.2 sha1/sha224/sha256/sha384/sha512常用API
    • 1.5 sha1代码测试
    • 1.4 在VS中添加预处理器定义
    • 1.5 哈希算法C++代码封装的思路
  • 2. 非对称加密RSA
    • 2.1 特点
    • 2.2 应用场景:
    • 2.3 生成RSA密钥对:常用的API
    • 2.4 生成密钥对:测试代码
      • 2.4.1 将密钥对写入磁盘 - 代码主要部分
      • 2.4.2 使用bio方式将秘钥写入磁盘
    • 2.5 加密和解密
      • 2.5.1 加密解密常用API
      • 2.5.2 加密解密:测试代码
    • 2.6 RSA签名和校验签名
      • 2.6.1 签名和校验签名API
      • 2.6.2 签名和校验:测试代码
    • 2.7 RSA封装成C++类
  • 3.对称加密AES
    • 3.1 AES 加解密的API:
      • 3.1.1 生成加密/解密的Key
      • 3.1.2 CBC方式加密 - 密码分组链接模式
    • 3.2 AES代码测试

0. 代码仓库

Openssl_Test:项目中要单独添加main.cpp或者test.cpp

代码编译时候可能出现的错误

  • OPENSSL_Uplink no OPENSSL_Applink 错误

    Applink()函数不属于openssl的dll内部函数的一部分(通过dll分析器看出这个函数不存在), 所以必须把applink.c文件应用程序的一部分编译.

  • 解决方案

    extern "C"
    {
    #include <openssl/applink.c>
    };
    

在这里插入图片描述

1. 哈希

1.1 哈希算法的种类:

  • md5 - 散列值: 16byte
  • sha1 - 散列值: 20byte
  • sha224- 散列值: 28byte
  • sha256- 散列值: 32byte
  • sha384- 散列值: 48byte
  • sha512- 散列值: 64byte

以上说的散列值长度是二进制数据长度, 一般散列值使用 16 进制格式的数字串表示的, 看到的字符串长度是原来的2倍长.

1.2 使用的头文件

#include <openssl/md5.h>
#include <openssl/sha.h>

1.3 哈希算法API

1.3.1 详解md5 API

# 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地址

1.3.2 sha1/sha224/sha256/sha384/sha512常用API

# 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);

1.5 sha1代码测试

void sha1Test()
{
	// 1. 初始化
	SHA_CTX ctx;
	SHA1_Init(&ctx);
	// 2. 添加数据
	SHA1_Update(&ctx, "hello", strlen("hello"));
	SHA1_Update(&ctx, ", world", strlen(", world"));
	// 3. 哈希计算
	unsigned char* md = new unsigned char[SHA_DIGEST_LENGTH];
	char* res = new char[SHA_DIGEST_LENGTH*2 + 1];
	SHA1_Final(md, &ctx);
	// 4. 格式转换
	for (int i = 0; i < SHA_DIGEST_LENGTH; ++i)
	{
		sprintf(&res[i * 2], "%02x", md[i]);
	}
	cout << "sha1: " << res << endl;
}

在这里插入图片描述

1.4 在VS中添加预处理器定义

_CONSOLE
_DEBUG
_SCL_SECURE_NO_WARNINGS
PROTOBUF_USE_DLLS
_CRT_SECURE_NO_WARNINGS

在这里插入图片描述

1.5 哈希算法C++代码封装的思路

c++中不建议使用宏,因为容易出bug,而且这种bug不好找,可以使用 常量/枚举/内联->用空间换时间。


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;
}

2. 非对称加密RSA

  • RSA 算法密钥长度越长,安全性越好,加密解密所需时间越长。

  • 密钥长度增长1倍,公钥操作所需时间增加约4倍,私钥操作所需时间增加约8倍,公私钥生成时间约增长16倍

2.1 特点

  • 秘钥是一个密钥对: 公钥, 私钥
    • 公钥加密, 必须私钥解密
    • 私钥加密, 必须公钥解密
  • 加密强度比较高, 效率低
    • 不会使用非对称加密, 加密特别大的数据

2.2 应用场景:

  • 2.2.1 秘钥分发 -> 对称加密
    • 核心思想: 加密的时候, 公钥加密, 私钥解密
    • 分发步骤:
      • 假设A, B两端
      • A端生成了一个密钥对, 分发公钥, B端有了公钥
      • B端生成一个对称加密的秘钥, 使用公钥加密 -> 密文
      • B将密文发送给A
      • A接收数据 -> 密文, 使用私钥对密文解密 -> 对称加密的秘钥
  • 2.2.2 签名 -> 验证数据是否被篡改, 验证数据的所有者
    • 核心思想: 私钥加密, 公钥解密
    • A, B两端, 假设A要发送数据
      • A端生成一个密钥对, 将公钥进行分发, 自己留私钥
    • 签名
      • A对原始数据进行哈希运算 -> 哈希值
      • A使用私钥对哈希值加密 -> 密文
      • 将原始数据+密文发送给B
    • 校验签名
      • B接收数据: 密文 + 收到的原始数据
      • 使用公钥对密文解密 -> 哈希值old
      • 使用has算法对收到的数据进行哈希运算 -> 哈希值new
      • 比较这两个哈希值
        • 相同: 校验成功
        • 不同: 失败

2.3 生成RSA密钥对:常用的API

#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


////////////////////////////////////////////////////////////////////
2.4.2 使用bio方式将秘钥写入磁盘
// 创建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);

2.5 加密-读取秘钥
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);

2.4.1 将密钥对写入磁盘
// 写入文件中的公钥私钥数据不是原始数据, 写入的编码之后的数据
// 是一种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

///////////////////////////////////////////////////////////

2.5 单独生成公钥或者私钥
// rsa公钥私钥类型是一样的: RSA类型
// 将参数rsa中的公钥提取出来
RSA *RSAPublicKey_dup(RSA *rsa);
    - rsa参数: 秘钥信息
    - 返回值: rsa公钥

// 将参数rsa中的私钥提取出来
RSA *RSAPrivateKey_dup(RSA *rsa);
    - rsa参数: 秘钥信息
    - 返回值: rsa私钥

2.4 生成密钥对:测试代码

2.4.1 将密钥对写入磁盘 - 代码主要部分

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);
	// 3. 将密钥对写入到磁盘
	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);
}

运行成功之后生成公钥和私钥
在这里插入图片描述

2.4.2 使用bio方式将秘钥写入磁盘

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);

在这里插入图片描述

2.5 加密和解密

2.5.1 加密解密常用API

以块的方式进行加密的, 加密的数据长度, 不能大于秘钥长度

  • 假设: 秘钥长度: 1024bit = 128byte
// 公钥加密
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字节

2.5.2 加密解密:测试代码

注意解密时候的返回值,容易出错

return string(buf, len); //注意这里

// 公钥加密
string encryptPublicKey()
{
	// 1. 准备要加密数据
	string text = "让编程改变世界...";
	// 2. 准备秘钥 -> 公钥
	// 从磁盘文件读秘钥
	// 使用bio的方式
	BIO* bio = BIO_new_file("public-1.pem", "r");
	RSA* pubKey = RSA_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];
	// 返回值就是密文长度
	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();
	if (PEM_read_bio_RSAPrivateKey(bio, &priKey, NULL, NULL) == NULL)
	{
		cout << "读私钥失败..." << endl;
		return string();
	}
	BIO_free(bio);
	// 解密 -> 明文
	// 数据被加密之后, 长度和秘钥长度相同
	// 通过函数计算秘钥长度
	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); //注意这里
}

在这里插入图片描述

2.6 RSA签名和校验签名

2.6.1 签名和校验签名API

int RSA_sign(int type, const unsigned char *m, unsigned int m_length,
             unsigned char *sigret, unsigned int *siglen, RSA *rsa);
    参数:
        - type: 使用的哈希算法
            - NID_MD5
            - NID_SHA1
            - NID_SHA224
            - .....
        - m: 要进行签名的数据
        - m_length: 要签名的数据长度
            - 0 < m_length <= 秘钥长度-11
        - sigret: 传出, 存储了签名之后的数据 -> 密文
        - siglen: sigret密文长度
        - rsa: 私钥
     返回值: 判断函数状态

int RSA_verify(int type, const unsigned char *m, unsigned int m_length,
               const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
    参数:
        - type: 使用的哈希算法, 和签名使用的哈希算法一致
            - NID_MD5
            - NID_SHA1
            - NID_SHA224
            - .....
        - m: 进行签名的原始数据 -> 接收到的
        - m_length: m参数字符串的长度
        - sigbuf: 接收到的签名数据
        - siglen: sigbuf接收到的签名数据的长度
        - rsa: 公钥
    返回值:
        如果!=1: 失败
        如果==1: 成功

2.6.2 签名和校验:测试代码

void rsaSigAndVerfiy()
{
	// 1. 签名数据
	string text = "让编程改变世界...";
	// 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(), text.size(), out, &outLen, priKey);
	
	// 要给到用户的数据
	string sigbuf((char*)out, outLen);

	// 4. 验证签名
	int ret = RSA_verify(NID_sha1, (const unsigned char*)text.data(), text.size(), (const unsigned char*)sigbuf.data(), sigbuf.size(), pubKey);
	
	cout << "ret : " << ret << endl;
}

在这里插入图片描述

2.7 RSA封装成C++类

class MyRSA
{
public:
    MyRSA();
    ~MyRSA;
    // 生成密钥对
    // 公钥加密
    // 私钥解密
    // 数据签名
    // 验证签名
private:
    RSA* pubkey;
    RSA* pirKey;
}

在这里插入图片描述

3.对称加密AES

分组加密: 每组长度 -> 16byte, 128bit
秘钥长度: 16byte, 24byte, 32byte
每组明文和加密之后的密文长度相同

  • 分组加密有不同的加密方式
    • 五种密码分组模式
      • 最常用: cbc -> 密文分组链接
        • 需要一个初始化向量 -> 数组 -> 存储一个随机字符串 -> 分组长度相同
        • 加密的和解密的时候都需要这个初始化向量
        • 加解密的时候初始化向量的值必须相同

AES是一套对称密钥的密码术,目前已广泛使用,用于替代已经不够安全的DES算法。所谓对称密钥,就是说加密和解密用的是同一个密钥,消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同,这里的密钥是双方保密的,不会让任何第三方知道。

对称密钥加密法主要==基于块加密选取固定长度的密钥加密明文中固定长度的块,生成的密文块与明文块长度一样显然密钥长度十分重要,块的长度也很重要。如果太短,则很容易枚举出所有的明文-密文映射;如果太长,性能则会急剧下降。AES中规定块长度为128 bit,而密钥长度可以选择128, 192或256 bit== 。暴力破解密钥需要万亿年,这保证了AES的安全性。

3.1 AES 加解密的API:

3.1.1 生成加密/解密的Key

  #include <openssl/aes.h>
  # define AES_BLOCK_SIZE 16    // 明文分组的大小
  
  // 加密的时候调用
  // aes中的秘钥格式 AES_KEY
    AES_KEY key;
 
  // 封装加密时候使用的秘钥
  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对称加密的秘钥-> 字符串, 长度: 16, 24, 32byte
bites指定秘钥的长度: 单位->bit
key传出参数

3.1.2 CBC方式加密 - 密码分组链接模式

由最后一个参数决定是加密还是解密。
重点:length是16的整数倍

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的长度
           - (len = (字符串长度 + \0) % 16) == 0
           - 如果不是在函数内部会自动填充
               - 实际长度: ((len / 16) + 1 ) * 16
       - key: 初始化之后的秘钥
       - ivec: 初始化向量, 字符串 ==> 长度和分组长度相同
       - enc: 指定数据要解密还是解密
           - # define AES_ENCRYPT     1 -> 加密
           - # define AES_DECRYPT     0 -> 解密

3.2 AES代码测试

void aesCBCCrypto()
{
	// 1. 准备数据
	const char* pt = "AES是一套对称密钥的密码术,目前已广泛使用,用于替代已经不够安全的DES算法。所谓对称密钥,就是说加密和解密用的是同一个密钥,消息的发送方和接收方在消息传递前需要享有这个密钥。和非对称密钥体系不同,这里的密钥是双方保密的,不会让任何第三方知道。对称密钥加密法主要基于块加密,选取固定长度的密钥,去加密明文中固定长度的块,生成的密文块与明文块长度一样。显然密钥长度十分重要,块的长度也很重要。如果太短,则很容易枚举出所有的明文 - 密文映射;如果太长,性能则会急剧下降。AES中规定块长度为128 bit,而密钥长度可以选择128, 192或256 bit 。暴力破解密钥需要万亿年,这保证了AES的安全性。";
	// 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];
	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;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1126173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp 单选框以及多选框样式更改

radio以及checkbox默认样式不符合自身需求时&#xff0c;根据自身需求更改样式&#xff0c;以下是自身的示例&#xff1a; 单选&#xff1a; 多选&#xff1a; 由于uniapp自身包含了一套默认的样式&#xff0c;所以如果不想全局更改只想在某个单据页面使用的话&#xff0c;就…

Redis3.2.12版本服务器迁移

1.新机器更新yum源 yum -y update 2.新机器安装redis数据库 yum install redis 3.新机器下载fedora的epel仓库 systemctl enable redis 4.将旧机器上的/etc/redis.conf拷贝到新机器的/config目录下 scp -r -P22 redis.config root162.32.196.57:/config/redis.config 5.新机器启…

CentOS 编译安装TinyXml2

安装 TinyXml2 Git 源码下载地址:https://github.com/leethomason/tinyxml2 步骤1&#xff1a;首先&#xff0c;你需要下载tinyxml2的源代码。你可以从Github或者源代码官方网站下载。并上传至/usr/local/source_code/ 步骤2&#xff1a;下载完成后&#xff0c;需要将源代码解…

LrC 13 ACR 16:点颜色

Adobe Lightroom Classic 13&#xff08; 2023 年 10 月版&#xff09;及 Adobe Camera Raw 16 新增的点颜色 Point Color功能可以方便、精准地调整图像上的颜色。 LrC&#xff1a;修改照片/混色器/点颜色 Develop/Color Mixer/Point Color ACR&#xff1a;编辑/混色器/点颜色 …

希捷推出Exos系列24TB硬盘:配备增强型缓存 性能提高三倍

希捷推出了全新的Exos 24TB硬盘。其基于传统的CMR构建&#xff0c;为3.5英寸规格&#xff0c;转速为7200 RPM。 同时&#xff0c;Exos系列24TB硬盘拥有10片磁盘&#xff0c;每片磁盘的容量为2.4TB&#xff0c;是希捷存储密度最高的硬盘&#xff0c;适用于超大规模企业和数据中心…

最新Jn建站系统2.0 已集成各类源码 【附视频安装教程】

附视频安装教程|已集成各类源码 目前已集成的网站&#xff1a; 1.发卡网(最新) 2.代刷网(无需授权) 3. 博客网(自带模板) 4.易支付(稳定版) 5.个人导航网(简洁) 6.代理查询网 7.留言网 8.匿名网 9.表白墙(最新) 10.抽奖网 11.源码站 12.z-blog博客程序 13.织梦CM…

Ubuntu Linux下如何搭建并安装EDK2

本教程全程手动下载安装&#xff1a; 1、官网下载EDK2安装包(这里下载当前最新版) ---- 2023.10.23 网址&#xff1a;https://sourceforge.net/projects/edk2/files/UDK2015_Releases/UDK2015/UDK2015.Complete.MyWorkSpace.zip 2、解压 unzip UDK2015.Complete.MyWorkSpace…

Android 实现资源国际化(多语言)

目录 一、介绍 二、字符串资源 三、图片资源 四、日期和时间格式 五、其他 六、应用内切换语言 七、资源文件命名规则 一、介绍 Android国际化(多语言)是一种开发技术&#xff0c;旨在使Android应用程序能够在「不同语言和文化环境」下运行&#xff0c;并为用户提供本地…

vue3中使用vue3-pdf-app和使用浏览器内置的PDF插件浏览器PDF文件

文章目录 先准备一个PDF使用浏览器内置的PDF插件预览PDF在HTML中使用浏览器插件预览PDFVscode使用插件发布服务后直接通过URL地址访问PDF可使用的浏览器 在vue3项目中预览PDF文件vue3项目也是可以通过URL地址访问文件的vue3中使用浏览器内置的PDF插件预览PDF代码如下所示&#…

单窗口单IP适合炉石传说游戏么?

游戏道具制作在炉石传说中是一个很有挑战的任务&#xff0c;但与此同时&#xff0c;它也是一个充满机遇的领域。在这篇文章中&#xff0c;我们将向您展示如何在炉石传说游戏中使用动态包机、多窗口IP工具和动态IP进行游戏道具制作。 作者与主题的关系&#xff1a;作为一名热爱炉…

【分布式】大模型分布式训练入门与实践 - 04

大模型分布式训练 数据并行-Distributed Data Parallel1.1 背景1.2 PyTorch DDP1&#xff09; DDP训练流程2&#xff09;DistributedSampler3&#xff09;DataLoader: Parallelizing data loading4&#xff09;Data-parallel&#xff08;DP&#xff09;5&#xff09;DDP原理解析…

nodejs+vue 视频网站的设计与实现

该设计划分为七大模块&#xff0c;包括用户的系统管理、其他管理和视频管理三部分&#xff0c; 和管理管理员的视频、其他管理、系统管理和用户管理四部分。目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关…

【虚幻引擎UE】UE4/UE5 基于2D屏幕坐标获取场景3D坐标 射线检测(蓝图/C++)

UE4/UE5 基于2D屏幕坐标获取场景3D坐标 一、射线检测1&#xff09;定义1&#xff09;射线与3D场景中的物体交互的流程2&#xff09;射线检测蓝图函数3&#xff09;蓝图实现根据鼠标点击位置获取场景中的坐标值4&#xff09;根据相机中心点获取场景中的坐标值5&#xff09;射线检…

迅为RK3588开发板Android12双摄同时显示

要支持双摄同时显示需对源码做如下修改&#xff0c;修改文件 hardware/rockchip/camera/Camera3HALModule.cpp 注释掉下面函数中的部分代码即可。 测试 双摄同时显示需要双摄 app&#xff0c;在网盘资料下载测试 APK&#xff0c;然后使用 adb 安装测试 APK&#xff0c; 启动…

hive窗口函数记录

记录工作中和学习中的窗口函数&#xff0c;方便以后使用&#xff0c;本记持续更新和完善&#xff0c;版本&#xff1a;231019 文章目录 1.什么是窗口函数2.窗口函数的表达式3.窗口函数的类型1&#xff09; 排名函数2&#xff09; 聚合函数3&#xff09; 跨行取值函数 4.[frame…

中英文双语言海外微盘交易源码 微盘交易源码 虚拟币微盘交易系统源码 支持产品数据

Rockefeller微交易源码/双语言海外微盘完整源码/带单控 测试环境&#xff1a;宝塔、Linux、PHP7.3、MySQL5.6 根目录 public&#xff0c;伪静态 larvael5&#xff0c;默认文档&#xff1a;index.html修改为第一个&#xff0c;不然会报404 k线对接的是蜜蜂查&#xff1a;http…

平衡二叉树(AVL)【java实现+图解】

目录 一、平衡二叉树(AVL) 二、平衡二叉树的四种旋转 1.右旋转 2.左旋转 3. 左右旋转 4. 右左旋转 三、基于二叉搜索树之平衡二叉树的代码实现 1.具体方法思路 2.java代码实现 一、平衡二叉树(AVL) 一种自平衡二叉搜索树&#xff0c;它是在每个节点上增加一个平衡因子…

Day 1 Vue 页面框架

现在前端框架越来越像后端了&#xff0c;特别是TypeScript这样的语言出现后&#xff0c;开发前端的体验跟后端渐渐接近了。当然&#xff0c;作为一个后端&#xff0c;直接上手前端&#xff0c;还是有很多坑要填的。 本次开发&#xff0c;前端页面框架直接选择Vue。原因很简单&…

HTML中文本框\单选框\按钮\多选框

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <h1>登录注册</h1> <form action"第一个网页.html" method"post&quo…

字节码进阶之java反射的底层实现原理

文章目录 1. 前言1. 类加载机制2. 字节码操作 2. 反射方法源码分析反射的inflation机制 3. 为什么反射性能差4. 反射的限制与安全性考虑1. **性能开销**2. **安全限制**3. **破坏抽象**4. **版本兼容性问题** 参考文档 java 反射的底层实现原理 1. 前言 Java反射的底层实现原理…