Openssl数据安全传输平台009:加密理论基础:哈希/非对称加密RSA/对称加密AES
文章目录
- 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 64int 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
- A对原始数据进行
- 校验签名
- 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 -> 密文分组链接
- 需要一个初始化向量 -> 数组 -> 存储一个随机字符串 -> 分组长度相同
- 加密的和解密的时候都需要这个初始化向量
- 加解密的时候初始化向量的值必须相同
- 最常用: 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_KEYAES_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;
}
相关文章:

Openssl数据安全传输平台009:加密理论基础:哈希/非对称加密RSA/对称加密AES
文章目录 0. 代码仓库代码编译时候可能出现的错误 1. 哈希1.1 哈希算法的种类:1.2 使用的头文件1.3 哈希算法API1.3.1 详解md5 API1.3.2 sha1/sha224/sha256/sha384/sha512常用API 1.5 sha1代码测试1.4 在VS中添加预处理器定义1.5 哈希算法C代码封装的思路 2. 非对称加密RSA2.1…...

iPhone开发--Xcode15下载iOS 17.0.1 Simulator Runtime失败解决方案
爆句粗口,升级后公司网络下载iOS 17.0.1 Simulator Runtime一直出错,每次出错后都得重新开始下载,oh,f**k。上一次在在家里的网络升级成功。 解决办法一: 进入网址:https://developer.apple.com/download…...

Galaxy生信云平台|Maftools高效地汇总、分析、注释和可视化肿瘤基因突变MAF文件...
2023-10-25,Galaxy中国镜像站 UseGalaxy.cn 平台新增 5 个工具。 MAF Tools Maftools-突变景观图: 绘制肿瘤基因突变景观图Maftools-突变汇总: 汇总MAF文件中的突变信息Maftools-共突变与互斥突变: 计算共突变和互斥突变Maftools-队列比较:比较两个队列之…...

JS三种常见的存储机制
1.localStorage localStorage是HTML5引入的一种持久化存储机制,用于在浏览器中长期保存数据。localStorage中存储的数据没有过期时间,除非被显式清除或代码删除。存储在localStorage中的数据对于同一个域名下的所有页面都是共享的。localStorage可以存储…...

【Python机器学习】零基础掌握BaggingClassifier集成学习
何提高分类模型的稳定性和准确性? 在金融风控、医疗诊断或者社交媒体推荐等场景中,分类问题是常见的难题。但是,单一的分类模型(如SVM)在处理复杂或不均衡的数据集时可能会表现不佳。那么,有没有一种方法能够提高模型的稳定性和准确性呢? 假设一家银行想要通过机器学习…...

[晕事]今天做了件晕事26;gcc对strcmp/strncmp的优化
今天做了一件晕事,写了一个测试小程序,开头的程序例如下面片段。在后续又写了一些代码,进行编译,使用gdb查看可执行文件,怎么都得不到想要的结果,非常的纳闷,非常的奇怪。 int main() {char a[3]="ab";int b = strncmp(0, a, 1<...

【深度学习】使用Pytorch实现的用于时间序列预测的各种深度学习模型类
深度学习模型类 简介按滑动时间窗口切割数据集模型类CNNGRULSTMMLPRNNTCNTransformer 简介 本文所定义模型类的输入数据的形状shape统一为 [batch_size, time_step,n_features],batch_size为批次大小,time_step为时间步长,n_feat…...

ts | js | 爬虫小公举分享
Curl转Code 快速将curl转为各种语言的代码; 便于提取请求头之类, 或者微改直接使用 https://curlconverter.com/node-axios/ (有点慢, 但是很全)https://www.lddgo.net/convert/curl-to-code (没有axios, 我喜欢用axios) 使用… 抓取地址, 使用浏览器或者其他抓包工具都可, 这…...

实现el-table打印功能,样式对齐,去除滚动条
实现el-table打印功能,样式对齐,去除滚动条 // 整个页面打印 function printTable(id) {// let domId #js_index// if (id) {// domId #${ id };// }// let wpt document.querySelector(domId);// let newContent wpt.innerHTML;// let oldContent document.…...

2022年09月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 表达式len(“学史明理增信 ,读史终生受益”) > len(" reading history will benefit you ")的…...

【面试经典150 | 链表】两数相加
文章目录 写在前面Tag题目来源题目解读解题思路方法一:模拟 其他语言python3 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带一些对于本题涉及到…...

vue路径中“@/“代表什么
举例: <img src"/../static/imgNew/adv/tupian.jpg"/>其中,/是webpack设置的路径别名,代表什么路径,要看webpack的build文件夹下webpack.base.conf.js里面对于是如何配置: 上图中代表src,上述代码就…...

java springboot2.7 写一个本地 pdf 预览的接口
依赖方面 创建的是 接口web项目就好了 然后包管理工具打开需要这些 import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; imp…...

RustDay06------Exercise[81-90]
81.宏函数里面的不同的匹配规则需要使用分号隔开 // macros4.rs // // Execute rustlings hint macros4 or use the hint watch subcommand for a // hint.// I AM NOT DONE#[rustfmt::skip] macro_rules! my_macro {() > {println!("Check out my macro!");};($…...

【Docker从入门到入土 6】Consul详解+Docker https安全认证(附证书申请方式)
Part 6 一、服务注册与发现的概念1.1 cmp问题1.2 服务注册与发现 二、Consul ----- 服务自动发现和注册2.1 简介2.2 为什么要用consul?2.3 consul的架构2.3 Consul-template 三、consul架构部署3.1 Consul服务器Step1 建立 Consul 服务Step2 查看集群信息Step3 通过…...

PCIe架构的处理器系统介绍
不同的处理器系统中,PCIe体系结构的实现方式不尽相同。PCIe体系结构以Intel的x86处理器为蓝本实现,已被深深地烙下x86处理器的印记。在PCIe总线规范中,有许多内容是x86处理器独有的,也仅在x86处理器的Chipset中存在。在PCIe总线规…...

国产内存强势崛起,光威龙武挑战D5内存24×2新标杆
今年国产内存的表现非常亮眼,出现了很多高质量的普惠产品,像是最近光威推出的一款内存条龙武ddr5 242就很有竞争力,加上之前神策新加入的ddr5 242版本,都是备受瞩目的新品,凭实力把DIY主机的内存配置拉高到了48GB。 龙…...

矩阵的运算
目标:实现一个能进行稀疏矩阵基本运算(包括加、减、乘)的运算器。 (1)以三元组顺序表表示稀疏矩阵,实现两个矩阵相加、相减、相乘的运算 (2)稀疏矩阵的输入形式为三元组表示,运算结果则以通常…...

Android 获取SIM卡号码权限申请
1.添加权限 在AndroidManifest.xml中添加如下权限 <uses-permission android:name"android.permission.READ_PHONE_STATE"/> 2.获取权限 如果你只在清单文件中添加权限却没有在代码中获取权限,代码还是会报错的。 报错原因: android…...

Linux CentOS 本地yum配置
本地开发用虚拟机,因为有光盘镜像在手,并不需要联网安装组件,只需要设置一下就可以让yum从本地获取源。 将安装盘挂载在合适位置 mount /dev/cdrom /mnt 进入目录 cd /etc/yum.repos.d/ 修改CentOS-Media.repo里面的挂载路径 …...

【c++速通】入门级攻略:什么是内联函数?函数重载又是什么?
🎥 屿小夏 : 个人主页 🔥个人专栏 : C入门到进阶 🌄 莫道桑榆晚,为霞尚满天! 文章目录 📑前言🌤️函数重载☁️函数重载的概念☁️函数重载的作用☁️C支持函数重载的原理…...

ubuntu 安装串口工具和添加虚拟串口
目录 一、串口工具安装 二、使用Windows本身虚拟的串口 (一)添加串口 1、保证虚拟机是关闭状态,打开“虚拟机设置”,点击“添加”。 2、选中“串行端口”,点击“完成”。 3、选中刚添加的串口,下拉选…...

【数据结构】数组和字符串(四):特殊矩阵的压缩存储:稀疏矩阵——三元组表
文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表结构体初始化元素设置打印矩阵主函数输出结果代码整合 4.2.1 矩阵的数组表示 【数据结构】数组和字符串(一ÿ…...

为什么POST请求经常发送两次?
大多数初级前端程序员,在通过浏览器F12的调试工具调试网络请求时,可能都会有一个发现,在进行POST请求时,明明代码里只请求了一次,为什么network里发送了两次呢,难道我代码出bug了?带着疑问点开第…...

打破总分行数据协作壁垒,DataOps在头部股份制银行的实践|案例研究
从银行开始建设数据仓库至今已近20年,当前各银行机构在数据能力建设中面临诸多困扰:如何保证数据使用时的准确性?如何让数据敏捷响应业务变化?如何让更多的业务人员使用数据? 这些问题极大影响了经营指标的达成与业务…...

测试用例的设计方法(全):边界值分析方法
一.方法简介 1.定义:边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。 2.与等价划分的区别 1)边界值分析不是从某等价类中随便挑…...

酷开科技 | 酷开系统沉浸式大屏游戏更解压!
随着家庭娱乐需求日益旺盛,越来越多的家庭消费者和游戏玩家开始追求大屏游戏带来的沉浸感。玩家在玩游戏的时候用大屏能获得更广阔的视野和更出色的视觉包围感,因此用大屏玩游戏已经成为了一种潮流。用酷开系统玩大屏游戏,过瘾又刺激…...

读高性能MySQL(第4版)笔记20_Performance Schema和其他
1. 线程 1.1. MySQL服务端是多线程软件。它的每个组件都使用线程 1.2. 每个线程至少有两个唯一标识符 1.2.1. 操作系统线程ID 1.2.2. MySQL内部线程ID 2. 对象类型 2.1. OBJECT_TYPE列 2.2. EVENT 2.3. FUNCTION 2.4. PROCEDURE 2.5. TABLE 2.6. TRIGGER 3. Perfor…...

spring cloud Eureka集群模式搭建(IDEA中运行)《二》
上一篇集群配置文件完善 上一篇博客,想必大家都学会了Eureka集群模式的搭建和运行,针对上一篇的配置文件进行了优化,在这里分享给大家。上一篇主要有3个配置文件,分别对应3个不同的服务,这种形式配置文件分别写在了不…...

大模型(LLM)在电商推荐系统的探索与实践
本文对LLM推荐的结合范式进行了梳理和讨论,并尝试将LLM涌现的能力迁移应用在推荐系统之中,利用LLM的通用知识来辅助推荐,改善推荐效果和用户体验。 背景 电商推荐系统(Recommend System,RecSys)是一种基于…...