美文网首页
RSA数字签名算法-Cpp-OpenSSL

RSA数字签名算法-Cpp-OpenSSL

作者: Replay923 | 来源:发表于2018-10-31 11:35 被阅读0次

    前言

    RSA加密与签名是很使用率非常高的一套算法。这次工作中有需求制作一个C++版的RSA加密与签名的Demo。这里记录一下踩过的坑吧。
    如果你正好也需要Cpp的RSA算法,希望能给你提供点帮助。

    进入正题

    本次笔者使用的是OpenSSL工具包中的RSA算法。

    适用场景:两台服务器之间数据传输交互验证。

    Demo工程GitHub链接

    1、公钥,私钥注意事项

    • 本示例使用 emersonfxbx.openssl.v140.desktop.x86 NuGet库

      与其他高级语言不同的是,这里的RSA算法是基于OpenSSL 1.0.1e版本。所以需要使用密钥格式为 PKCS#1 的密钥对(只对私钥有要求,公钥PKCS#1与PKCS#8通用)。
      推荐一个密钥对生成网站程默的博客.

    • 密钥字符串需要注意格式,如代码所示(每64位一次换行):

    //来自另外一台服务器的公钥,用于验证签名,此值不变。                                                     -------公钥使用 pkcs#8  
    const std::string & _publicKey =
        "-----BEGIN PUBLIC KEY-----\n"\
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOlUoW84bA+8b23PBM7OWWO3+o\n"\
        "wISx42v7bWDH8c6rUa0zeeh2bh43sJrmoNdwMWkRNKgeHxw8BITXg/IV2j8x9Z2/\n"\
        "+uROYoFLgcIDnxotgumylfxDZo/RxFyhpl8Rx4/zlj6quSOXoMNvkXpWKLhty8+g\n"\
        "Wlsv2othCFU9Ym4cQQIDAQAB\n"\
        "-----END PUBLIC KEY-----\n";
    
    //开发者私钥,由开发者自行生成,用于解密回调数据,请将此私钥修改为开发者自已的私钥。 --------私钥使用 pkcs#1
    const std::string & _privateKey =
        "-----BEGIN RSA PRIVATE KEY-----\n"\
        "MIICXAIBAAKBgQCLF/ZGuuNwGpS+YRFPr1kMEz+hO7HesleruuyoepVnex4cLP1V\n"\
        "PDVUN6l1VgO6bJk3ToHWGme+MYIyduQwVafm6mJcL3gj5k6UymXFyZCp/+n1B1u3\n"\
        "6NQQLP4/HvW/HhGBwFXhhGTdkzLUhrZE2Xfz6NbwsMdEkDq9nhcXxAC96wIDAQAB\n"\
        "AoGAPUd0R9sEYppDV9CZ+NpOx+QfD2CmT2+Q8maq5tsCwZFbRZyIi6m38P+I19nq\n"\
        "UJKRue0LhJEjjYZwTt1UUPsbuhTYGNpVHzCsie1QVkBX19tlRrhjETisCQF8QSiT\n"\
        "DXnhmaqNXAUGchnCntp85viCXPxHmj0m0ymU5/ctr4CBXIkCQQDq64bdPet2Ky+1\n"\
        "FoFNm/mYk6UlUnkBz+z1akvWYQn3H9fmhOf1gPQknPoHo7A6LZGaL+K/qPTW/7ts\n"\
        "3+qFDuotAkEAl5Mnwd+grqqn0nIW/A6TxZqMBJlqxpbjcfqL+TvEprVXJMFI+ufb\n"\
        "NVrpuljDVfHSZk2NYYtCVy0cK4tLlcZPdwJAIbOsY20QrKFBdN9HqZSo2CTGWnZc\n"\
        "edAUlJitTJIbVeKxnJaQmH3piJ8kl5f6Hj6PVulrxEc+6OFDSDlPcctT+QJAdz53\n"\
        "mpg5qu/q0y6aUnWNX3m0CbJARDdUe8il8c9JZ/Vlty6wIWPiGlmJYuaN1cFGyuDc\n"\
        "Bw8tg7OjY8ZUEmJPBQJBAOao0jpWLKD/9HMM79k5v8Yhm+v5M58B1qtoguhED+Id\n"\
        "UKM5FzeT04lfQtOx10DhkOaS4Rb7/h05hz+in/AQXSo=\n"\
        "-----END RSA PRIVATE KEY-----\n";
    

    2、RSA算法核心部分

    RSACrypto.h

    #pragma once
    
    #include <string>
    
    
    class RSACrypto
    {
    public:
        RSACrypto(
            const std::string& public_key,
            const std::string& private_key) :
            m_strPublicKey(public_key),
            m_strPrivateKey(private_key),
            m_PublickeyRsa(nullptr), m_PrivatekeyRsa(nullptr)
        {
    
        }
    
        virtual ~RSACrypto();
    
        bool InitRsa();
    
        void Release();
    
    
        bool EncryptByPublicKey(const std::string& src, std::string& encrypted);
        bool DecryptByPublicKey(const std::string& encrypted, std::string& decrypted);
    
    
        bool EncryptByPrivateKey(const std::string& src, std::string& encrypted);
        bool DecryptByPrivateKey(const std::string& encrypted, std::string& decrypted);
    
    
        bool SignByPrivateKey(const std::string &src, std::string& sign);
        bool VerifyByPublicKey(const std::string &src, const std::string& sign);
    
    private:
        bool InitPrivateRSA();
        bool InitPublicRSA();
    private:
    
        const std::string & m_strPublicKey;
        const std::string & m_strPrivateKey;
    
        RSA *m_PublickeyRsa;
        RSA *m_PrivatekeyRsa;
    };
    

    RSACrypto.Cpp

    #include <openssl/rsa.h>
    #include <openssl/pem.h>
    #include <openssl/bio.h>
    #include <vector>
    #include <algorithm>
    
    #include "RSACrypto.h"
    
    RSACrypto::~RSACrypto()
    {
        Release();
    }
    
    bool  RSACrypto::InitPrivateRSA()
    {
        BIO* keybio = nullptr;
    
        keybio = BIO_new_mem_buf((char*)m_strPrivateKey.data(), -1);
        if (keybio == NULL)
        {
            printf("Failed to create private key BIO\n");
            return false;
        }
    
        m_PrivatekeyRsa = PEM_read_bio_RSAPrivateKey(keybio, &m_PrivatekeyRsa, NULL, NULL);
        if (!m_PrivatekeyRsa)
        {
            printf("Failed to create private RSA\n");
            BIO_set_close(keybio, BIO_CLOSE);
            BIO_free(keybio);
            return false;
        }
    
        BIO_set_close(keybio, BIO_CLOSE);
        BIO_free(keybio);
        return true;
    }
    
    bool  RSACrypto::InitPublicRSA()
    {
        BIO* keybio = nullptr;
    
        keybio = BIO_new_mem_buf((char*)m_strPublicKey.data(), -1);
        if (keybio == NULL)
        {
            printf("Failed to create public key BIO\n");
            return false;
        }
        m_PublickeyRsa = PEM_read_bio_RSA_PUBKEY(keybio, &m_PublickeyRsa, NULL, NULL);
        if (!m_PublickeyRsa)
        {
            printf("Failed to create public RSA\n");
            BIO_set_close(keybio, BIO_CLOSE);
            BIO_free(keybio);
            return false;
        }
    
        BIO_set_close(keybio, BIO_CLOSE);
        BIO_free(keybio);
        return true;
    }
    
    bool RSACrypto::InitRsa()
    {
        if (!InitPrivateRSA())
        {
            return false;
        }
    
        if (!InitPublicRSA())
        {
            return false;
        }
    
        return true;
    }
    
    void RSACrypto::Release()
    {
        if (m_PublickeyRsa != nullptr)
        {
            RSA_free(m_PublickeyRsa); m_PublickeyRsa = nullptr;
        }
    
        if (m_PrivatekeyRsa != nullptr)
        {
            RSA_free(m_PrivatekeyRsa); m_PrivatekeyRsa = nullptr;
        }
    }
    
    bool RSACrypto::EncryptByPublicKey(const std::string & src, std::string & encrypted)
    {
        std::string result;
        const int keysize = RSA_size(m_PublickeyRsa);
        std::vector<unsigned char> block(keysize);
        const int chunksize = keysize - RSA_PKCS1_PADDING_SIZE;
        int inputlen = src.length();
    
        for (int i = 0; i < inputlen; i += chunksize)
        {
            auto resultsize = RSA_public_encrypt(std::min(chunksize, inputlen - i), (uint8_t*)&src[i], &block[0], (RSA*)m_PublickeyRsa, RSA_PKCS1_PADDING);
            if (resultsize == -1)
            {
                return false;
            }
            encrypted.append((char*)block.data(), resultsize);
        }
    
        return true;
    }
    
    bool RSACrypto::DecryptByPublicKey(const std::string & encrypted, std::string & decrypted)
    {
        const int keysize = RSA_size(m_PublickeyRsa);
        std::vector<unsigned char> block(keysize);
    
        int inputlen = encrypted.length();
    
        for (int i = 0; i < (int)encrypted.length(); i += keysize)
        {
            int flen = std::min(keysize, inputlen - i);
    
            auto resultsize = RSA_public_decrypt(flen, (uint8_t*)&encrypted[i], &block[0], m_PublickeyRsa, RSA_PKCS1_PADDING);
    
            if (resultsize == -1)
            {
                return false;
            }
    
            decrypted.append((char*)block.data(), resultsize);
        }
        return true;
    }
    
    bool RSACrypto::EncryptByPrivateKey(const std::string & src, std::string & encrypted)
    {
        std::string result;
        const int keysize = RSA_size(m_PrivatekeyRsa);
        std::vector<unsigned char> block(keysize);
        const int chunksize = keysize - RSA_PKCS1_PADDING_SIZE;
        int inputlen = src.length();
    
        for (int i = 0; i < (int)src.length(); i += chunksize)
        {
            int flen = std::min<int>(chunksize, inputlen - i);
    
            std::fill(block.begin(), block.end(), 0);
    
            auto resultsize = RSA_private_encrypt(flen, (uint8_t*)&src[i], &block[0], m_PrivatekeyRsa, RSA_PKCS1_PADDING);
            if (resultsize == -1)
            {
                return false;
            }
    
            encrypted.append((char*)block.data(), resultsize);
        }
        return true;
    }
    
    bool RSACrypto::DecryptByPrivateKey(const std::string & encrypted, std::string & decrypted)
    {
        const int keysize = RSA_size(m_PrivatekeyRsa);
        std::vector<unsigned char> block(keysize);
    
        for (int i = 0; i < (int)encrypted.length(); i += keysize)
        {
            auto resultsize = RSA_private_decrypt(std::min<int>(keysize, encrypted.length() - i), (uint8_t*)&encrypted[i], &block[0], m_PrivatekeyRsa, RSA_PKCS1_PADDING);
            if (resultsize == -1)
            {
                return false;
            }
            decrypted.append((char*)block.data(), resultsize);
        }
    
        return true;
    }
    
    
    
    bool RSACrypto::SignByPrivateKey(const std::string &src, std::string& sign)
    {
        EVP_MD_CTX* rsa_sign_ctx = EVP_MD_CTX_create();
        EVP_PKEY* pri_key = EVP_PKEY_new();
    
        auto clean = [pri_key, rsa_sign_ctx] {
            EVP_PKEY_free(pri_key);
            EVP_MD_CTX_cleanup(rsa_sign_ctx);
        };
    
        //EVP_PKEY_assign_RSA(pri_key, m_PrivatekeyRsa);
        EVP_PKEY_set1_RSA(pri_key, m_PrivatekeyRsa);
        if (EVP_DigestSignInit(rsa_sign_ctx, NULL, EVP_sha1(), NULL, pri_key) <= 0)
        {
            clean();
            return false;
        }
    
        if (EVP_DigestSignUpdate(rsa_sign_ctx, src.data(), src.length()) <= 0)
        {
            clean();
            return false;
        }
    
        size_t sign_len;
        if (EVP_DigestSignFinal(rsa_sign_ctx, NULL, &sign_len) <= 0)
        {
            clean();
            return false;
        }
    
        sign.resize(sign_len);
        if (EVP_DigestSignFinal(rsa_sign_ctx, (unsigned char*)sign.data(), &sign_len) <= 0)
        {
            clean();
            return false;
        }
    
        sign.resize(sign_len);
        clean();
        return true;
    }
    
    bool RSACrypto::VerifyByPublicKey(const std::string &src, const std::string& sign)
    {
        EVP_PKEY* pub_key = EVP_PKEY_new();
        //EVP_PKEY_assign_RSA(pub_key, m_PublickeyRsa);
        EVP_PKEY_set1_RSA(pub_key, m_PublickeyRsa);
        EVP_MD_CTX* rsa_verify_ctx = EVP_MD_CTX_create();
    
        auto clean = [pub_key, rsa_verify_ctx] {
            EVP_PKEY_free(pub_key);
            EVP_MD_CTX_destroy(rsa_verify_ctx);
        };
    
        if (EVP_DigestVerifyInit(rsa_verify_ctx, NULL, EVP_sha1(), NULL, pub_key) <= 0)
        {
            clean();
            return false;
        }
    
        if (EVP_DigestVerifyUpdate(rsa_verify_ctx, src.data(), src.length()) <= 0)
        {
            clean();
            return false;
        }
    
        if (EVP_DigestVerifyFinal(rsa_verify_ctx, (unsigned char*)sign.data(), sign.length()) <= 0)
        {
            clean();
            return false;
        }
    
        clean();
        return true;
    }
    

    3、RSA加密与签名示例

    ...
    
    //要加密的数据源
    std::string _message = "hello,world";
    
    //初始化RSA算法
    RSACrypto* _RSACrypto = new RSACrypto(_publicKey, _privateKey);
    _RSACrypto->InitRsa();
    std::string _utf8Message;
    std::string _encrypted;
    std::string _encryptedBase64;
    std::string verify_data;
    
    //加密示例******************************************************************************************************
    
    //公钥加密
    //===========================================================================================
    Encoder _encoder = Encoder();
    //1、 src =  UTF8.encode编码(消息体)
    _utf8Message = _encoder.AnsiStringToUTF8String(_message);
    //2、 _encrypted =  RSA公钥加密(src)
    bool success = _RSACrypto->EncryptByPublicKey(_utf8Message, _encrypted);
    //3、 base64Str =   base64.encode编码(_encrypted )
    _encryptedBase64 = OpenSSL_Base64Encode(_encrypted.data(), _encrypted.length(), false);
    //4、 最终verify_data = UrlEncode编码(base64Str )
    verify_data = _encoder.UrlEncode(_encryptedBase64);
    //===========================================================================================
    
    //私钥签名
    //===========================================================================================
    std::string _sign;
    //1、 _sign = RSA私钥签名(消息体)
    bool success2 = _RSACrypto->SignByPrivateKey(_message, _sign);
    //2、 base64Str =   base64编码(_sign )
    std::string _signBase64 = OpenSSL_Base64Encode(_sign.data(), _sign.length(), false);
    //3、 最终verify_sign = UrlEncode编码(_signBase64 )
    auto verify_sign = _encoder.UrlEncode(_signBase64);
    //===========================================================================================
    
    //得到 最终的 验证登录状态 url  : finalStr
    std::string finalStr = "https://api.mguwp.com/user/verifySignin?verify_data=" + verify_data + "&verify_sign=" + verify_sign + "\n";
    cout << "Url:\n" << finalStr << endl;
    OutputDebugStringA(LPCSTR(finalStr.data()));
    
    //加密示例******************************************************************************************************
    
    ...
    

    4、RSA解密与签名验证示例

    ...
    
    //回调数据示例,两个参数$param_data,$sign
    std::string param_data = "etRzkAmBx5dbb%2BzggET0Z5rJ6FG8Jsh2hFFHD173og3vr%2F7bwPoueJFytrDFT7g13AkXmpz3vr9sEXFymsueG9UMMPELgJQW0gKLkhj96pA%2BNQLAN53H2%2F%2Fi8Z1Rderj%2Fes59T%2FK7bNQnHS%2Fc1LK3rduAkRFmSdm%2FLACNxHIUXpzXPZtyKRxRb7BuxJX%2B31ytaVrrDRseB9NPD9DFK4TuAMJPZqSZqNFWfzZXcyqTg2a9YVYsW6NCIFoBYv5G7%2F%2FQAFcqNQpXjCUb9ISdLHRffq1fvcqYwwdDAU8FkcGXdOQ9wNjixPj3x67GkJLaYAtwzYnCDLGMIl3T%2F%2B%2FM%2B66qw%3D%3D";
    std::string sign = "cPniGrltgyofwkEeQBVdADTOuOV5rNGi55VM%2FAwd%2BPRySCMqrU0DrE90gi36Q14O00A7x8DbYl2mD9wOu%2F2ZxsaSoIf5CHHjIEEL0xhuFqAA05zP3qvD9D5m8f3ru%2F3oGgRfCbjAOr%2BHHbhZcwcuBwSlGSbJEuVGd6Ia%2ByqbvWQ%3D";
    
    RSACrypto* _RSACrypto = new RSACrypto(_publicKey, _privateKey);
    _RSACrypto->InitRsa();
    std::string _urlDecodeStr;
    std::string _decryptedBase64;
    std::string _decrypted;
    Encoder _encoder = Encoder();
    
    //解密示例******************************************************************************************************
    //使用开发者私钥解密
    //===========================================================================================
    //1、_urlDecodeStr = UrlDecode解码(param_data)
    _urlDecodeStr = _encoder.UrlDecode(param_data);
    //2、_decryptedBase64 = Base64Decode解码(_urlDecodeStr)
    _decryptedBase64 = OpenSSL_Base64Decode(const_cast<char*>(_urlDecodeStr.c_str()), _urlDecodeStr.length(), false);
    OutputDebugStringW(LPCWSTR(_decryptedBase64.data()));
    //3、_decrypted = 使用开发者私钥进行RSA解密
    bool success = _RSACrypto->DecryptByPrivateKey(_decryptedBase64, _decrypted);
    //4、 得到解密后的 param_data 数据 _decrypted
    cout << "param_data = \n" << _decrypted << endl;
    //===========================================================================================
    
    //使用公钥验证签名
    //===========================================================================================
    std::string _sign;
    std::string _signDeBase64;
    //1、 _sign = UrlDecode解码(sign)
    _sign = _encoder.UTF8UrlDecode(sign);
    OutputDebugStringA(LPCSTR(_sign.data()));
    //2、 _signBase64 = Base64Decode解码(_sign)
    _signDeBase64 = OpenSSL_Base64Decode(const_cast<char*>(_sign.c_str()), _sign.length(), false);
    OutputDebugStringA(LPCSTR("\n"));
    OutputDebugStringW(LPCWSTR(_signDeBase64.data()));
    //3、 使用公钥验证签名。 参数1:私钥解密后的数据,参数2:转码后的 sign
    bool success2 = _RSACrypto->VerifyByPublicKey(_decrypted, _signDeBase64);
    //3、 最终验证结果 = success2
    cout << "验证结果:0为失败,1为成功 = \n" << success2 << endl;
    //===========================================================================================
    //解密示例******************************************************************************************************
    
    ...
    

    Thanks

    程默的博客

    来自上海鼹鼠网络的一位大佬

    RSA公钥文件解密密文的原理分析

    用OpenSSL 做Base64 编解码(C++)

    相关文章

      网友评论

          本文标题:RSA数字签名算法-Cpp-OpenSSL

          本文链接:https://www.haomeiwen.com/subject/lczotqtx.html