使用OpenSSL进行RSA加密和解密(非对称)

2023-10-27

1. RSA加密和解密基础概念

    RSA是一种非对称加密。

    RSA秘钥:私钥和公钥,一对私钥和公钥就像夫妻一样是唯一的,用私钥加密后必须用对应的公钥才能解密,用公钥加密后必须用对应的私钥才能解密。

    加密和解密方式:公钥加密-私钥解密,私钥加密-公钥解密

2. 使用OpenSSL库进行RSA加密和解密的基础过程

加密基础过程
       1) 调用OpenSSL库生成秘钥(非必要步骤,如果已经有秘钥对了,就不需要进行这步了)

       2) 调用OpenSSL库对明文进行加密

       2) 对加密后密文进行BASE64转码(非必要步骤,一般开发过程中,为了传输or存贮方便,都会对密文进行BASE64编码)

       注意:OpenSSL的RSA加密接口,每次加密数据的最大长度是有限制的,所以对“较大数据”进行加密,需要循环对“较大数据”分段加密

解密基础过程
       1)对BASE64内容进行BASE64解码

       2) 调用OpenSSL库对密文进行解密

       注意:OpenSSL的RSA解密接口,每次解密数据的最大长度是有限制的,所以对“较大数据”进行解密,需要循环对“较大数据”分段解密

3. 直接上代码

私钥和公钥格式
   说明:C++ OpenSSL中RSA秘钥(公钥和私钥)是有起止标识的,并且每64个字节会有一个换行符(\n),这个可能和其他编程语言(例如Java)是不同的。据我所知Java中秘钥是没有起止标识,只有秘钥内容的,也没有换行符(\n)(个人开发中遇到的情形,也不排除有其他情形的)

   1)私钥格式

        (据我所知)私钥的起止标识只有一种。

        起始标识:-----BEGIN RSA PRIVATE KEY-----
        结束标识:-----END RSA PRIVATE KEY-----

   2)·公钥格式

     (据我所知)公钥的起止标识有两种。

第一种
        起始标识:-----BEGIN RSA PUBLIC KEY-----
        结束标识:-----END RSA PUBLIC KEY-----

第二种
        起始标识:-----BEGIN PUBLIC KEY-----
        结束标识:-----END PUBLIC KEY-----

生成秘钥对(公钥和私钥)

#include <fstream>
#include "openssl/rsa.h"
 
#define KEY_LENGTH  2048             // 密钥长度
#define PUB_KEY_FILE "pubkey.pem"    // 公钥路径
#define PRI_KEY_FILE "prikey.pem"    // 私钥路径
 
/*
制造密钥对:私钥和公钥
**/
void GenerateRSAKey(std::string & out_pub_key, std::string & out_pri_key)
{
    size_t pri_len = 0; // 私钥长度
    size_t pub_len = 0; // 公钥长度
    char *pri_key = nullptr; // 私钥
    char *pub_key = nullptr; // 公钥
 
    // 生成密钥对
    RSA *keypair = RSA_generate_key(KEY_LENGTH, RSA_3, NULL, NULL);
 
    BIO *pri = BIO_new(BIO_s_mem());
    BIO *pub = BIO_new(BIO_s_mem());
 
    // 生成私钥
    PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
    // 注意------生成第1种格式的公钥
    //PEM_write_bio_RSAPublicKey(pub, keypair);
    // 注意------生成第2种格式的公钥(此处代码中使用这种)
    PEM_write_bio_RSA_PUBKEY(pub, keypair);
 
    // 获取长度  
    pri_len = BIO_pending(pri);
    pub_len = BIO_pending(pub);
 
    // 密钥对读取到字符串  
    pri_key = (char *)malloc(pri_len + 1);
    pub_key = (char *)malloc(pub_len + 1);
 
    BIO_read(pri, pri_key, pri_len);
    BIO_read(pub, pub_key, pub_len);
 
    pri_key[pri_len] = '\0';
    pub_key[pub_len] = '\0';
 
    out_pub_key = pub_key;
    out_pri_key = pri_key;
 
    // 将公钥写入文件
    std::ofstream pub_file(PUB_KEY_FILE, std::ios::out);
    if (!pub_file.is_open())
    {
        perror("pub key file open fail:");
        return;
    }
    pub_file << pub_key;
    pub_file.close();
 
    // 将私钥写入文件
    std::ofstream pri_file(PRI_KEY_FILE, std::ios::out);
    if (!pri_file.is_open())
    {
        perror("pri key file open fail:");
        return;
    }
    pri_file << pri_key;
    pri_file.close();
 
    // 释放内存
    RSA_free(keypair);
    BIO_free_all(pub);
    BIO_free_all(pri);
 
    free(pri_key);
    free(pub_key);
}


私钥加密-公钥解密
   1)对长度较短的数据加密和解密(数据长度小于RSA单次处理的最大长度)

/*
@brief : 私钥加密
@para  : clear_text  -[i] 需要进行加密的明文
         pri_key     -[i] 私钥
@return: 加密后的数据
**/
std::string RsaPriEncrypt(const std::string &clear_text, std::string &pri_key)
{
    std::string encrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);
    RSA* rsa = RSA_new();
    rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    if (!rsa)
    {
        BIO_free_all(keybio);
        return std::string("");
    }
 
    // 获取RSA单次可以处理的数据的最大长度
    int len = RSA_size(rsa);
 
    // 申请内存:存贮加密后的密文数据
    char *text = new char[len + 1];
    memset(text, 0, len + 1);
 
    // 对数据进行私钥加密(返回值是加密后数据的长度)
    int ret = RSA_private_encrypt(clear_text.length(), (const unsigned char*)clear_text.c_str(), (unsigned char*)text, rsa, RSA_PKCS1_PADDING);
    if (ret >= 0) {
        encrypt_text = std::string(text, ret);
    }
 
    // 释放内存  
    free(text);
    BIO_free_all(keybio);
    RSA_free(rsa);
 
    return encrypt_text;
}
/*
@brief : 公钥解密
@para  : cipher_text -[i] 加密的密文
         pub_key     -[i] 公钥
@return: 解密后的数据
**/
std::string RsaPubDecrypt(const std::string & cipher_text, const std::string & pub_key)
{
    std::string decrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);
    RSA *rsa = RSA_new();
    
    // 注意--------使用第1种格式的公钥进行解密
    //rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
    // 注意--------使用第2种格式的公钥进行解密(我们使用这种格式作为示例)
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
    if (!rsa)
    {
        unsigned long err= ERR_get_error(); //获取错误号
        char err_msg[1024] = { 0 };
        ERR_error_string(err, err_msg); // 格式:error:errId:库:函数:原因
        printf("err msg: err:%ld, msg:%s\n", err, err_msg);
        BIO_free_all(keybio);
        return decrypt_text;
    }
 
    int len = RSA_size(rsa);
    char *text = new char[len + 1];
    memset(text, 0, len + 1);
    // 对密文进行解密
    int ret = RSA_public_decrypt(cipher_text.length(), (const unsigned char*)cipher_text.c_str(), (unsigned char*)text, rsa, RSA_PKCS1_PADDING);
    if (ret >= 0) {
        decrypt_text.append(std::string(text, ret));
    }
 
    // 释放内存  
    delete text;
    BIO_free_all(keybio);
    RSA_free(rsa);
 
    return decrypt_text;
}


 2)对长度较长的数据加密和解密(数据长度大于RSA单次处理数据块的最大长度)

/*
@brief : 私钥加密
@para  : clear_text  -[i] 需要进行加密的明文
         pri_key     -[i] 私钥
@return: 加密后的数据
**/
std::string RsaPriEncrypt(const std::string &clear_text, std::string &pri_key)
{
    std::string encrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);
    RSA* rsa = RSA_new();
    rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    if (!rsa)
    {
        BIO_free_all(keybio);
        return std::string("");
    }
 
    // 获取RSA单次可以处理的数据块的最大长度
    int key_len = RSA_size(rsa);
    int block_len = key_len - 11;    // 因为填充方式为RSA_PKCS1_PADDING, 所以要在key_len基础上减去11
 
    // 申请内存:存贮加密后的密文数据
    char *sub_text = new char[key_len + 1];
    memset(sub_text, 0, key_len + 1);
    int ret = 0;
    int pos = 0;
    std::string sub_str;
    // 对数据进行分段加密(返回值是加密后数据的长度)
    while (pos < clear_text.length()) {
        sub_str = clear_text.substr(pos, block_len);
        memset(sub_text, 0, key_len + 1);
        ret = RSA_private_encrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            encrypt_text.append(std::string(sub_text, ret));
        }
        pos += block_len;
    }
    
    // 释放内存  
    delete sub_text;
    BIO_free_all(keybio);
    RSA_free(rsa);
 
    return encrypt_text;
}
/*
@brief : 公钥解密
@para  : cipher_text -[i] 加密的密文
         pub_key     -[i] 公钥
@return: 解密后的数据
**/
std::string RsaPubDecrypt(const std::string & cipher_text, const std::string & pub_key)
{
    std::string decrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);
    RSA* rsa = RSA_new();
    
    // 注意-------使用第1种格式的公钥进行解密
    //rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
    // 注意-------使用第2种格式的公钥进行解密(我们使用这种格式作为示例)
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
    if (!rsa)
    {
        unsigned long err = ERR_get_error(); //获取错误号
        char err_msg[1024] = { 0 };
        ERR_error_string(ulErr, szErrMsg); // 格式:error:errId:库:函数:原因
        printf("err msg: err:%ld, msg:%s\n", err , err_msg);
        BIO_free_all(keybio);
        
        return decrypt_text;
    }
 
    // 获取RSA单次处理的最大长度
    int len = RSA_size(rsa);
    char *sub_text = new char[len + 1];
    memset(sub_text, 0, len + 1);
    int ret = 0;
    std::string sub_str;
    int pos = 0;
    // 对密文进行分段解密
    while (pos < cipher_text.length()) {
        sub_str = cipher_text.substr(pos, len);
        memset(sub_text, 0, len + 1);
        ret = RSA_public_decrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            decrypt_text.append(std::string(sub_text, ret));
            printf("pos:%d, sub: %s\n", pos, sub_text);
            pos += len;
        }
    }
 
    // 释放内存  
    delete sub_text;
    BIO_free_all(keybio);
    RSA_free(rsa);
 
    return decrypt_text;
}


公钥加密-私钥解密
   该部分只对数据块较长的数据的处理为例

/*
@brief : 公钥加密
@para  : clear_text  -[i] 需要进行加密的明文
         pri_key     -[i] 私钥
@return: 加密后的数据
**/
std::string RsaPubEncrypt(const std::string &clear_text, const std::string &pub_key)
{
    std::string encrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *)pub_key.c_str(), -1);
    RSA* rsa = RSA_new();
    // 注意-----第1种格式的公钥
    //rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
    // 注意-----第2种格式的公钥(这里以第二种格式为例)
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
 
    // 获取RSA单次可以处理的数据块的最大长度
    int key_len = RSA_size(rsa);
    int block_len = key_len - 11;    // 因为填充方式为RSA_PKCS1_PADDING, 所以要在key_len基础上减去11
 
    // 申请内存:存贮加密后的密文数据
    char *sub_text = new char[key_len + 1];
    memset(sub_text, 0, key_len + 1);
    int ret = 0;
    int pos = 0;
    std::string sub_str;
    // 对数据进行分段加密(返回值是加密后数据的长度)
    while (pos < clear_text.length()) {
        sub_str = clear_text.substr(pos, block_len);
        memset(sub_text, 0, key_len + 1);
        ret = RSA_public_encrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            encrypt_text.append(std::string(sub_text, ret));
        }
        pos += block_len;
    }
    
    // 释放内存  
    BIO_free_all(keybio);
    RSA_free(rsa);
    delete[] sub_text;
 
    return encrypt_text;
}
/*
@brief : 私钥解密
@para  : cipher_text -[i] 加密的密文
         pub_key     -[i] 公钥
@return: 解密后的数据
**/
std::string RsaPriDecrypt(const std::string &cipher_text, const std::string &pri_key)
{
    std::string decrypt_text;
    RSA *rsa = RSA_new();
    BIO *keybio;
    keybio = BIO_new_mem_buf((unsigned char *)pri_key.c_str(), -1);
 
    rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    if (rsa == nullptr) {
        unsigned long err = ERR_get_error(); //获取错误号
        char err_msg[1024] = { 0 };
        ERR_error_string(err, err_msg); // 格式:error:errId:库:函数:原因
        printf("err msg: err:%ld, msg:%s\n", err, err_msg);
        return std::string();
    }
 
    // 获取RSA单次处理的最大长度
    int key_len = RSA_size(rsa);
    char *sub_text = new char[key_len + 1];
    memset(sub_text, 0, key_len + 1);
    int ret = 0;
    std::string sub_str;
    int pos = 0;
    // 对密文进行分段解密
    while (pos < cipher_text.length()) {
        sub_str = cipher_text.substr(pos, key_len);
        memset(sub_text, 0, key_len + 1);
        ret = RSA_private_decrypt(sub_str.length(), (const unsigned char*)sub_str.c_str(), (unsigned char*)sub_text, rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            decrypt_text.append(std::string(sub_text, ret));
            printf("pos:%d, sub: %s\n", pos, sub_text);
            pos += key_len;
        }
    }
    // 释放内存  
    delete[] sub_text;
    BIO_free_all(keybio);
    RSA_free(rsa);
 
    return decrypt_text;
}


测试代码

int main()
{
    // 原始明文  
    std::string src_text = "test begin\n this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! this is an rsa test example!!! \ntest end";
    //src_text = "rsa test";
 
    std::string encrypt_text;
    std::string decrypt_text;
 
    // 生成密钥对
    std::string pub_key;
    std::string pri_key;
    OpensslTool::GenerateRSAKey(pub_key, pri_key);
    printf("public key:\n");
    printf("%s\n", pub_key.c_str());
    printf("private key:\n");
    printf("%s\n", pri_key.c_str());
 
    // 私钥加密-公钥解密
    encrypt_text = OpensslTool::RsaPriEncrypt(src_text, pri_key);
    printf("encrypt: len=%d\n", encrypt_text.length());
    decrypt_text = OpensslTool::RsaPubDecrypt(encrypt_text, pub_key);
    printf("decrypt: len=%d\n", decrypt_text.length());
    printf("decrypt: %s\n", decrypt_text.c_str());
 
    // 公钥加密-私钥解密
    encrypt_text = OpensslTool::RsaPubEncrypt(src_text, pub_key);
    printf("encrypt: len=%d\n", encrypt_text.length());
    decrypt_text = OpensslTool::RsaPriDecrypt(encrypt_text, pri_key);
    printf("decrypt: len=%d\n", decrypt_text.length());
    printf("decrypt: %s\n", decrypt_text.c_str());
 
    return 0;
}


4. 总结
1)单次加密数据的最大长度(block_len),由RSA秘钥模长RSA_size()和填充模式有关

      A. 填充模式:RSA_PKCS1_PADDING, block_len=RSA_size() - 11

      B. 填充模式:RSA_PKCS1_OAEP_PADDING,block_len=RSA_size() - 41

      C. 填充模式:RSA_NO_PADDING(不填充),block_len=RSA_size()

      调用加密接口时,如果传入的加密数据的长度大于block_len,则加密接口将返回错误

2)公钥的使用(公钥和公钥加密解密接口),公钥的类型 和 生成公钥RSA对象指针的接口必须是一一对应的,否则将操作失败

      A. 第1种格式的公钥(-----BEGIN RSA PUBLIC KEY----- / -----END RSA PUBLIC KEY-----),对应的接口如下:

          生成公钥:PEM_write_bio_RSAPublicKey()

          生成公钥RSA对象指针:PEM_read_bio_RSAPublicKey()

      B. 第2种格式的公钥(-----BEGIN PUBLIC KEY----- / -----END PUBLIC KEY-----),对应的接口如下:

          生成公钥:PEM_write_bio_RSA_PUBKEY()

          生成公钥RSA对象指针:PEM_read_bio_RSA_PUBKEY()

3) 在使用其他编程语言生成的RSA秘钥对时,可能(同时)遇到以下问题(本人在使用java生成的公钥时全部遇到了,血的经验……):

      A. 秘钥对只有“秘钥内容”,而没有“秘钥起止标识”;(公钥+私钥都可能遇到)

      B. 秘钥内容没有换行符(\n);(公钥可能会遇到)

      遇到这个问题,请不要紧张,自己写个函数对“秘钥内容”包装一下,加上“起止标识”和换行符(\n)。对于“起止标识”,一种格式不行就换另一种试试,本人遇到的就是需要使用第二种“起止标识”的,代码请参考:

#define RSA_KEYSUB_LEN    64
 
std::string iifs::IIRSA::FormatPubKey(const std::string & key)
{
    std::string pub_key = "-----BEGIN PUBLIC KEY-----\n";
    
    auto pos = key.length();
    pos = 0;
    while (pos < key.length()) {
        pub_key.append(key.substr(pos, RSA_KEYSUB_LEN));
        pub_key.append("\n");
        pos += RSA_KEYSUB_LEN;
    }
 
    pub_key.append("-----END PUBLIC KEY-----");
    return pub_key;
}


5. 贴上一对秘钥(起止标识结束位置有换行,每64字节有换行)
/* 公钥 */
-----BEGIN RSA PUBLIC KEY-----
MIIBCAKCAQEAwDpa2EOEu7vCx88mVXzLHxdw1Yn0Hm7gkUEdnzdXzPenbL4NVLoj
6lxtX2UQ10wZLQfHuv5elaBn9jkCxpZgb9zkD3hKqmVLJI6HXRi2OECEnOp0Edus
crCr3N/FXvf7ALT4i9LeUlfmUmTB+kIR7VpyPY2Pg88DvA5n7QXQRRqRu8d3NBXF
ZhB9nZd8Zb8XyTStKLwwd11e7JV4vUTiA3heoNnSsBEeLN1DWdQNjCqq35AFP2yd
M1pncM4Zjhbyv0z0l776vCJyS4/iS78qund4i1dxp2l4gDoDuTd4Nck6oN/HC9Ba
nuIqrv/RP0Sw01Dij7g59nVxYA1aN8cvxQIBAw==
-----END RSA PUBLIC KEY-----
/* 私钥 */
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwDpa2EOEu7vCx88mVXzLHxdw1Yn0Hm7gkUEdnzdXzPenbL4N
VLoj6lxtX2UQ10wZLQfHuv5elaBn9jkCxpZgb9zkD3hKqmVLJI6HXRi2OECEnOp0
EduscrCr3N/FXvf7ALT4i9LeUlfmUmTB+kIR7VpyPY2Pg88DvA5n7QXQRRqRu8d3
NBXFZhB9nZd8Zb8XyTStKLwwd11e7JV4vUTiA3heoNnSsBEeLN1DWdQNjCqq35AF
P2ydM1pncM4Zjhbyv0z0l776vCJyS4/iS78qund4i1dxp2l4gDoDuTd4Nck6oN/H
C9BanuIqrv/RP0Sw01Dij7g59nVxYA1aN8cvxQIBAwKCAQEAgCbnOtet0n0shTTE
OP3cv2T147FNaZ9AYNYTv3o6iKUaSH6zjdFtRuhI6kNgj4gQyK/afKmUY8BFTtCs
hGRASpNCtPrccZjcwwmvk2XO0CsDE0b4C+fITHXH6JUuP0/8qyNQXTc+4Y/u4ZiB
UYFhSOb207O1AooCfV7v81k1g2XkBlZqUGXBCYoo7ec5X1PoCImTHrlbwTLOaA6h
GIr0HgrsvLMVjNNvzx8p7v18jBRzolkDa3Ch3dp61QHQ9Z3lehpXByTplLHxCNDp
fa8KtmdmQVZSKsosjBoMvtxHtEEpAhhAAL+6HtlQ67Y4LHsfLtIDk/PKjMNKyeht
mDKV8wKBgQDgWdxBbLw30cos87Tk98JlWYmkuo6YtVQL0qyj4J6jFnH2LTMSiJPP
2ETcUXt2rkd5Zs6NCNQEhHifgbmvIOILqD7txNGRIDksVf5UKRC5X3+90RbQff/x
XLLzNqKIEhIrdgNQzptoTpJyj5Llzw5Sj1f0MtnKAomW4l3zmuf2BwKBgQDbWGmW
TsDsBfcTRQfBXv7WYtyrwBeOID0dfdLjN9XQv/YFWJof1EAmnemoIdxcC8SEBTvz
FW+l4hoPr5Gw/MgO3+aESDYLPN5caFgv5ifhSVyhWD8l6TpEUV/9ZEqElVVRp7gW
PBVbIgm+vduXLX2vfb3o/vDAIMbqTtLCOJNY0wKBgQCVkT2A8yglNobIoniYpSxD
kQZt0bRlzjgH4chtQGnCDvakHiIMWw01OtiS4Pz5yYT7md8IsI1YWFBqVnvKFewH
xX9JLeELatDIOVQ4G2B7lP/T4LngU//2PcyiJGxatrbHpAI13xJFibb3CmHuigmM
X4/4IeaGrFu57D6iZ0VOrwKBgQCSOvEO3ytIA/oM2K/WP1SO7JMdKrpewCi+U+Hs
z+Pgf/lY5bwVOCrEaUZwFpLoB9hYA31MuPUZQWa1H7Z1/dq0lURYMCQHfemS8DrK
mW/rhj3A5X9um3wti5VTmDGtuOOLxSVkKA48wVvUfpJkyP50/n6bVKCAFdnxieHW
0GI7NwKBgCNVncsGnj/sIwoTJ62udRTxKW5VxtmUmPcDlG05qfjCB/itrJmPC5nv
Pmzq2doZXlu9SCqTN/tEgeyJ8PGBGJFDS03T42VnjuNu4Eravbmgm4AJYLdxip4O
oCF6GkBGNYJaCCdcPHQnouW5cvTILlAvJVYn99w0Vei/VmajwCIZ
-----END RSA PRIVATE KEY-----

其他链接:https://www.jianshu.com/p/011303dc9429

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用OpenSSL进行RSA加密和解密(非对称) 的相关文章

随机推荐

  • Mysql高可用高性能存储应用系列4 - 分库分表、中间件

    概述 为什么要分库的原因 1 很多时候接口性能慢都是数据库造成的 2 并发量比较大时 大量的数据库请求 会带来磁盘I O的性能瓶颈 3 来越多 导致sql查询数据 即使走了索引也比较慢 分库分表的场景 分库和分表是不同的两个概念 解决的问题
  • mac os操作系统如何降级

    降级方法 哔哩哔哩视频有相关讲解方法 一种是U盘启动方法 去app store下载相应版本操作系统 速度快 或者百度云 速度慢 做U盘启动盘 重启 键盘按住option键 抹掉硬盘数据 然后执行安装系统 过半个小时以上 完成安装 还有一种是
  • Linux jq 命令讲解与实战操作(json字符串解析工具)

    文章目录 一 概述 二 jq 命令安装 三 jq 命令语法与示例详解 1 基本用法 2 常用选项 3 查询和过滤 1 选择字段 2 过滤 3 遍历数组 4 组合操作 4 修改和创建 1 修改字段值 2 创建新字段 3 组合操作 4 条件修改
  • redis设计与实现读书笔记-数据结构

    简单动态字符串 数据结构 SDS与C字符串的区别 C语言使用长度为N 1的字符数组来表示长度为N的字符串 并且字符数组的最后一个元素总是空字符 0 C字符串并不记录自身的长度信息 所以为了获取一个C字符串的长度 程序必须遍历整个字符串 和C
  • write(byte b[], int off, int len)与write(byte b[])

    在进行写文件的时候有时候返现 通过write byte b 方式写文件比原来的文件大一些 流程代码 public static void main String args throws Exception long t1 System cu
  • 安防摄像头已分别接入乐橙云、萤石云,如何实现私有云平台的统一管理与向上级联?

    一 背景分析 科技创新对社会发展的重要性不言而喻 对于安防行业来说 人工智能和5G等新技术的应用 已经成为推动智能安防发展的一块关键踏板 从技术维度来看 人工智能 云计算 大数据 物联网等技术在安防视频监控领域产生了较多的交汇与融合 新技术
  • 冲刺必会代码100题(1-31)

    说在前面 链表章节 22题之前 多处用到了二级指针 有些地方可以选择使用一级指针也可 题目目录 1 顺序表01 2023 08 19 1 题目描述 2 算法思想 3 代码示例 4 总结 2 顺序表02 2023 08 19 1 题目描述 2
  • matlab如何输入数值,如何将数据输入到训练的神经网络算法 - MATLAB

    这是很基本的 但我似乎无法在网上找到答案 如何将数据输入到训练的神经网络算法 MATLAB 我已经开发了使用MATLAB进行分类的神经网络 但是 我想以预期的方式向训练算法提供用于预测的新数据集 我似乎无法弄清函数myNeuralNetwo
  • protected 权限方法调用.

    在同一个包中 子类可以调用父类的protected方法 子类的对象也可以调用父类的protected 方法 在不同的包中 在子类的内部可以调用父类 超类的protected 方法 但是子类的对象无法调用父类 超类的protected方法
  • MD5 算法流程

    先通过下面的命令对 md5算法有个感性的认识 md5sum tmp 1 txt 1dc792fcaf345a07b10248a387cc2718 tmp 1 txt md5sum 从键盘输入 ctrl d 结束输入 hello world
  • Unity 运行时到处fbx-Autodesk FBX SDK for Unity

    参考链接
  • VMware 虚拟机快照、克隆、磁盘扩容

    1 快照 快照是虚拟机某个时间点上完整系统的镜像 可以在虚拟机内部通过快照文件恢复系统到之前的节点 拍摄快照 恢复快照 2 克隆 克隆是原始虚拟机全部状态的一个拷贝 是脱离原始虚拟机独立存在的 可以在宿主机或其他机器上通过克隆文件创建一个完
  • Python初级到高级调试

    Python初级到高级调试 什么是Python调试 为什么调试很重要 如何在python中执行调试 需要调试的编码错误是什么 常见的调试技术有哪些 什么是Python调试器 Python模块 参考 这篇博客通过示例介绍调试的一些基本和常见用
  • JavaWeb详讲

    JavaWeb内容 一 Servlet简介 Servlet 就是 Sun 公司开发动态 Web 的一门技术 Sun 在这些 API Application Programming Interface 应用程序接口 中提供一个接口叫做 Ser
  • 区块链 - 分布式账本技术的应用优势

    区块链技术正在改变世界各地的工业 它将组织 政府 金融机构和支付平台引入一个新的数字时代 它改变了我们周围的一切 但很多人仍然不知道区块链是什么或区块链技术如何工作 今天 我们为您具体解释什么是区块链和区块链技术的核心内容 什么是区块链 区
  • 如何写好一篇拆书稿?

    前段时间报名参加了知乎写作课 其中有一节课程 介绍如何写好拆书稿 之后不久 看到有人在知乎提问相关问题 便简单整理课程笔记 增加了一些自己的理解 作为回答 算是现学现卖吧 虽然拆书稿不是写作变现的最好渠道 但是刻意练习拆解图书内容 有助于提
  • 万字攻略全面了解selenium_selenium教程

    今天带大家一起学习下python爬虫4小分队 scrapy beautifulsoup selenium以及pyppeteer 之一的Selenium库 主要用于模拟浏览器运行 是一个用于web应用测试的工具 Selenium直接运行在浏览
  • 小程序评论回复功能

  • 超级详细!!!node.js中读写文件方法总结

    nodejs中所有与文件相关的操作都在fs模块中 而读写操作又是我们会经常用到的操作 nodejs的fs模块针对读操作为我们提供了readFile read createReadStream三个方法 针对写操作为我们提供了writeFile
  • 使用OpenSSL进行RSA加密和解密(非对称)

    1 RSA加密和解密基础概念 RSA是一种非对称加密 RSA秘钥 私钥和公钥 一对私钥和公钥就像夫妻一样是唯一的 用私钥加密后必须用对应的公钥才能解密 用公钥加密后必须用对应的私钥才能解密 加密和解密方式 公钥加密 私钥解密 私钥加密 公钥