因为最近用到AES,RSA,SHA之类的密码学算法,用的是crypto++这个库,所以在这里记录一下
现在我觉得是openssl库好用,真香!
crypto++
https://www.cryptopp.com/
Crypto ++ Library是一个加密方案的免费C ++类库。
一.安装依赖库
首先需要下载这个库
sudo apt-get install libcrypto++
我用的是clion
在cmakelist.txt的最后添加依赖
target_link_libraries(your_program_name crypto++)
之后就能用了
#include "crypto++/files.h"
#include "crypto++/rsa.h"
#include "crypto++/randpool.h"
#include "crypto++/hex.h"
#include "crypto++/base64.h"
#include "crypto++/sha.h"
using namespace CryptoPP;
使用时注意使用命名空间
aes https://blog.csdn.net/csdn49532/article/details/50686222
二.RSA
网上的例子有很多,但是没有用自己已有的公钥去加密的,我尝试用自己另外的公钥去加密,一直在报错,都是格式问题
下面这段是参考别人的
//------------------------
// Generate RSA Key
//------------------------
void CKeyController::GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed)
{
RandomPool randPool;
randPool.Put((byte *)seed, strlen(seed));
RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength);
Base64Encoder privFile(new FileSink(privFilename));
priv.DEREncode(privFile);
privFile.MessageEnd();
RSAES_OAEP_SHA_Encryptor pub(priv);
Base64Encoder pubFile(new FileSink(pubFilename));
pub.DEREncode(pubFile);
pubFile.MessageEnd();
}
//------------------------
// RSA encrypt choose Base64Encoder/HexEncoder/null
//------------------------
string CKeyController::RSAEncryptString(const char *pubFilename, const char *seed, const char *message){
FileSource pubFile(pubFilename, true, new HexDecoder);
RSAES_OAEP_SHA_Encryptor pub(pubFile);
RandomPool randPool;
randPool.Put((byte *)seed, strlen(seed));
string result;
StringSource(message, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result))));//16进制编码器
return result;
}
//------------------------
// RSA decrypt
//------------------------
string CKeyController::RSADecryptString(const char *privFilename, const char *ciphertext){
FileSource privFile(privFilename, true, new HexDecoder);
RSAES_OAEP_SHA_Decryptor priv(privFile);
string result;
StringSource(ciphertext, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result))));//Base64 Hex
return result;
}
//------------------------
// define gobal rng
//------------------------
RandomPool & CKeyController::GlobalRNG()
{
static RandomPool randomPool;
return randomPool;
}
替换成自己的公钥去加密会报BER decode error
这里的密钥内部保存是ASN.1 DER的编码方式
StringSource参数:(原始字符串, 长度, 变换方式filter)
terminate called after throwing an instance of 'CryptoPP::InputRejecting::InputRejected'
what(): BufferedTransformation: this object doesn't allow input
这个问题就没解决
https://blog.csdn.net/rain_qingtian/article/details/43267793
我发现好像搞反了,RSA公钥算法的话是公钥加密私钥解密,但是用在签名上的话是私钥签名用公钥验证,好气
签名的官方文档,好好看这篇
https://www.cryptopp.com/wiki/Rsa_signature_schemes
尝试了好几天了,没法替换私钥,总是格式问题
把自己的私钥放进去,然后就会无法解析,报错BER
记录我的各种尝试
string CKeyController::SignMessage_by( const std::string& privateKeyFileName, const std::string& message )
{
std::string signedMessage = "";
////////////////////////////////////////
string encString;
string prikey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCGzWIxRW4gO5V6npTs2X/VuqjVDMntifmSwOTQ68emYZjYnSx85NovrmjtWPhgWzCGu2CV09ihe5nwX2JXWXNbDbxgK3J+5spSEcGmH5SQgiWB2TfdrbXOqGZ6ogZ2zIKju36Cp1oFY8gnUYoSCOpYqb9CoVlqG0A1n9z6Qi4u7s256pZi7fSzTwMrLJgHCI2nbqTw7deQCqRPeulwQNh6n1ftmJStqfztf7XJUc211oRPgun+e7DUQt1Ork3CwYREdjsXsCcbm8KRsb5LNYzNnsA/wtFUb4Fn5F/JtyveU3i4uZcvZQf6r3eUpaEMTbbrj7gx0YMKR6ShnIM9+4+JAgMBAAECggEARwpyFxlKAj6kdiygu3mcPxzR4QWOXVr7ZfW2IMxBOVPx4yZhzaglvD8kGQFsVAt6tWaGbi9o2SF0bRM3lmUTwAarIduDyZEDSuEEtvGpr8aG0bmSEYd8at6gszw0P+Njbbpf36cmb/S4BNCZ/QspxXQf0ZOKtbqKyjfnkxPRlvWLK2JokD+aYSa7VuP83D9MXmn28LP6VALcFYGuiljXMsBcT1hWV3nS8SqzW5+gHNP5EBiSzmQU9wChrRiwqLNVkZq574uTYXQGEBdDkeB6fNL+mzSCia692SZTOO13g2YcfL/8BJ1tI8YnitLxDZ6CJvKswHGnbShK86Pz6SlmKQKBgQC9X8E/0XytzHDuxjXmG+c21C1TGnULlkwnsAcETMdz0LN24uyXkI5VTSQ5jImd5044elQttzxWNsakAyiTrCrQey1C3gASJtkpEXDfZnLNTx6OgN2vHQtcFZMc6nsQm3KpF6j/ZLUtcrRj9m9RdCOkYDjSBlVe5FLU7KZUx66BOwKBgQC2Ooti2kIK0ERg0PLQGVJIxiIaxos3NtsNbGZUneHzuIbMqRgD7yqb/zDVAK1bV5yTnHuEtbndyXT8CxBV4Xz+uxD/JXEHB0t9htrtQ8ER93Smg8wcut9xS/EUJslN4TBrCkx2X5nEbyP7V+1CBqrlmefSvearUxklEL8oPhPmCwKBgB/JyA2nY49lBvv9x6RlwK0idq1Bfq5AP25WDQd6v+w/9zAYrXBPsqS8+zyxy5P29NlkJ2c5H+a08Sks4o3PU6yFULatv0NMxB0xsUm+z7yI8G8pUI0JihOeB4DOF3RVM60QrKyaQhNr8r3o6wy4Oxdnk4Il0ATKvODDDLSkhDVrAoGBAKH2FrydT+VchemmzKi4WLHlp5o0rLc+QiMEHU9Ho/OCJgzwnS+h+jT78uLd3zY4ACoSApZJDhFZZGg/TR2HdBjTGqArHvIoQagnm50Vgxv82+jDaz59uOpxnKtUbcaSRv5cAOmUvo4gWdKiq3TriCjZSXm83q+XA9o1sDCMPBNRAoGAKk8DZjOpjFvN2PNdPNRgexhy7mVsIFtzlt/0CuAJYuqelg1H22cUnnALs1LLZ2qEF5uzyZgChA7z19cGSKsAR2hWg9QXSpxmqZnPSCgm2yc71ok49p+JlZ6DtjKn4az4nF2koyfpSuh1FRYhLT4h8cMXDna385V/RZzhOrYN8ho=";
//StringSource privString( prikey.c_str(), true, new Base64Decoder(new StringSink(encString)));
StringSource privString( prikey.c_str(), true, new FileSink("prikey"));
RSAES_OAEP_SHA_Decryptor decryptor;
Base64Encoder decFile(new StringSink(prikey));
decryptor.DEREncode(decFile);
decFile.MessageEnd();
FileSource privFile( "prikey", true, new Base64Decoder(new StringSink(encString)));
RSASSA_PKCS1v15_SHA_Signer priv;
//
/* HexEncoder pubFile(new FileSink("pubkey", true));
pubFile.Put((const byte*)encString.c_str(),encString.size());
decryptor.DEREncode(pubFile);
pubFile.MessageEnd();*/
/* HexDecoder decoder;
decoder.Put( (byte*)encString.c_str(), encString.size() );
decoder.MessageEnd();*/
// priv.AccessKey().Load(decoder);
AutoSeededRandomPool rng;
// StringSource s1(message, true, new SignerFilter(rng, priv, new Base64Encoder(new StringSink(signedMessage))));
return encString;
}
还是gg,转openssl库看下
https://www.linuxidc.com/Linux/2017-09/147117.htm
貌似这里的链接要注意
cmake_minimum_required(VERSION 3.10)
project(untitled_3)
set(CMAKE_CXX_STANDARD 11)
set(INC_DIR /home/parallels/Downloads/openssl-1.1.1/include)
set(LINK_DIR /home/parallels/Downloads/openssl-1.1.1/lib)
include_directories(${INC_DIR})
link_directories(${LINK_DIR})
link_libraries(ssl crypto)
add_executable(untitled_3 main.cpp common_tool.cpp common_tool.h
Base64.cpp
Base64.h)
target_link_libraries(untitled_3 openssl)
问题一:
报错 EVP_MD_CTX的问题,是由于函数变动,调用方式也需要改动
/home/parallels/CLionProjects/untitled_3/common_tool.cpp: In static member function ‘static bool common_tool::verify_rsa(RSA*, const string&, const string&)’:
/home/parallels/CLionProjects/untitled_3/common_tool.cpp:57:16: error: aggregate ‘EVP_MD_CTX ctx’ has incomplete type and cannot be defined
EVP_MD_CTX ctx;
^
/home/parallels/CLionProjects/untitled_3/common_tool.cpp:99:28: error: ‘EVP_MD_CTX_cleanup’ was not declared in this scope
EVP_MD_CTX_cleanup(&ctx);
^
解决方案:https://blog.csdn.net/uniom/article/details/54143201
问题二:解决问题一之后的问题
代码中是调用的EVP_MD_CTX_create,EVP_MD_CTX_init方法的,但是查看evp.h是有定义过的
考虑版本问题吧
我用的最新版是1.1.1现在打算用1.0.1u,经过测试,就是这个问题,解决
换库很快的,make之后在cmakelist里面改一个库的路径就行
问题三:
在运行的时候又报错了,然后把问题一的时候修改的内容换回来,就可以了
是在EVP_MD_CTX_init(&ctx);的时候有信号量问题,之前是改成指针所以有问题,改回来了
urlencode
https://www.codeguru.com/cpp/cpp/algorithms/strings/article.php/c12759/URI-Encoding-and-Decoding.htm
参考的是与java对接的c++,https://blog.csdn.net/richerg85/article/details/51723124
贴一下完整的代码,是我运行,需要的依赖和问题前面都介绍了
这是结构
这里的pem密钥是openssl生成的,然后拷贝到目录下的
OpenSSL> genrsa -out rsa_private_key.pem 1024 #生成私钥
OpenSSL> pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out rsa_private_key_pkcs8.pem #Java开发者需要将私钥转换成PKCS8格式
OpenSSL> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥
OpenSSL> exit #退出OpenSSL程序
这里看一下pem文件的内部,就是多加了前缀和后缀,中间的内容可以替换,我就是替换的自己的密钥
-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvuKK4Hd+R39cNPHPC6a2MMpUIkfYiBX0+3iFePiffZueU94weBfgK3XE5RV2B8WQsXA3LTRvgS7uluU2niCF/zcQ5uYi7CFM07iXRktr93z2fmgUbeSqL//NBBX8jouROdmB/mG6hXX9xjcoO4SHkJ/jLQftcv3ZIuJdkW3b5epHF1S+he+Cf8DssZskUNzhW5pVonMK/HoNJngwEboReAQhTQM8GRugKCPryff3JntORKlUhuj8edK0bHJCakdeu+m5cG0FriByVIyD/y00u2+30TxYY5gXwlAhI4Fq8rfQ9Kpk4iiVp8fjtyGO3Wl8GuUbSCaAynsbp6pWmp1/7AgMBAAECggEAJOhN3QgBPj1aiOAqnETCe9Df9pXZsZmfjcXQEmBRtZ2ChIpd9qxgRb/vGQ1Vr4ZI+Pw4tXuzoC+n8Q3znn18M8C/NzSd4FLGTza54MPMlUf/RHdwHS4c3pzatcx2wMBk3YoFiKc2jVP7AZm7V9a3bJlsqxxntDpOmMWUnrKLUaiwARbFIaR4UXe6smSW/haC3cB5W579CRW7Qynpy+6xEs6pNfRNFm2P8tPfsMwHdb/btLKZnDfO9qCoV3rHK+8pu4teu4rehpz/wOEPOXLidDNZ1NR98gQ+MPk3kaur+IQZLEsA/OSPIxF1yx3BF1DSGLSxFG9fq+9i45uXVLB+2QKBgQDxRvowG9u6BPJ6ztV0JxI19Zc/Boxd+tgYxP4xcNgEoKrv4k3j/9e6LIuRn0R2WprvqR5THEG2b6IeaXCjPFD+e4jeJ+9ypT6c8M0cW3raosQWNQgCntUgYi4WXJNOfStjVsEhFId+xG5QdjxI7suVUCIqEBNPqPgRuVPxiejefwKBgQC6cZlvu+bS0rWptgOSSWr+eDwa7IAUkjuC4hzenXkO8+OM+5YwLVj8II6kXGYQ81oVQUFcCd7pdU5mvkKYYZN8LgLYSnV0P+hWHXeybgGhorF19MJY2yl/cyUL/9fGtQKeSIM431+7nQECrLVelw1DuWcKb7a+uXceePsp8AA4hQKBgErwE5bLoEQ3cySHhy6LUBGoVLCDlSc/WMzgql6qwL03NcBJ1o1hEhzF5xd0kyv7WjHjogKIPTSvmXe+ofhhLHrN3aW6W5mND1RpShtrok2zzhljoThHIsTrGYtid3IiWE6DTHq8yJSr5+NfxbuVR2/5spLD6WuNfHbsy9CyV7x7AoGBAJZ9iU8SCU+UesPAwgM7NZyvKjzFNuDj5BtSLeFWlFeIbtzmWiULBW+FS/3JAy2db9IGOR50bTRd1mnCGvjqkeRDM1KoT0QFLaEM6gfhGXoBW2KNrK2SQV25cGbOHLoscy+V8i1ex+Qk56LkCwWNwUwwA8b9wzERfN/g4ULJkTQNAoGAVnbKNCeRMqu1cj0qFYh/aTlu2uu7DJQDvylhnccuDOgpwA+GZAJdb+Q6ACNBGGObtJMK1hwxH2Sg0qH96Pgj7PhmyVIX8051VWXudeJn52l0Dml5/xwGhE/JAA199kXd7KgE8D+mU33Bi3PUctqBNlndYohk5CRTCxX3hPzt450=
-----END RSA PRIVATE KEY-----
main.cpp
#include
#include
#include "common_tool.h"
static void ERR(const char* string);
static std::string sign(const char *private_key,const std::string &content) {
BIO *bufio = NULL;
RSA *rsa = NULL;
EVP_PKEY *evpKey = NULL;
bool verify = false;
EVP_MD_CTX ctx ;
int result = 0;
unsigned int size = 0;
char *sign = NULL;
std::string signStr = "";
/* bufio = BIO_new_mem_buf((void*)private_key, -1);
if (bufio == NULL) {
ERR("BIO_new_mem_buf failed");
goto safe_exit;
}*/
bufio = BIO_new(BIO_s_file());
BIO_read_filename(bufio, "rsa_private_key_pkcs8.pem");
// BIO_read_filename(bufio, "rsa_private_key.pem");
rsa = PEM_read_bio_RSAPrivateKey(bufio, NULL, NULL, NULL);
if (rsa == NULL) {
ERR("PEM_read_bio_RSAPrivateKey failed");
goto safe_exit;
}
evpKey = EVP_PKEY_new();
if (evpKey == NULL) {
ERR("EVP_PKEY_new failed");
goto safe_exit;
}
if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {
ERR("EVP_PKEY_set1_RSA failed");
goto safe_exit;
}
EVP_MD_CTX_init(&ctx);
if (result == 1 && (result = EVP_SignInit_ex(&ctx, EVP_md5(), NULL)) != 1) {
ERR("EVP_SignInit_ex failed");
}
if (result == 1 && (result = EVP_SignUpdate(&ctx, content.c_str(), content.size())) != 1) {
ERR("EVP_SignUpdate failed");
}
size = EVP_PKEY_size(evpKey);
sign = (char*)malloc(size+1);
memset(sign, 0, size+1);
if (result == 1 && (result = EVP_SignFinal(&ctx, (unsigned char*)sign, &size, evpKey)) != 1) {
ERR("EVP_SignFinal failed");
}
if (result == 1) {
verify = true;
} else {
ERR("verify failed");
}
signStr = base64_encode((const unsigned char*)sign, size);
EVP_MD_CTX_cleanup(&ctx);
//EVP_MD_CTX_destroy(ctx);
free(sign);
safe_exit:
if (rsa != NULL) {
RSA_free(rsa);
rsa = NULL;
}
if (evpKey != NULL) {
EVP_PKEY_free(evpKey);
evpKey = NULL;
}
if (bufio != NULL) {
BIO_free_all(bufio);
bufio = NULL;
}
return signStr;
//return sign;
}
static void ERR(const char* string) {
std::cout<<"error "<<string<<std::endl;
}
int main() {
std::cout << "Hello, World!" << std::endl;
std::string message="shadkfkaslkdfj";
std::string prikey="";
std::string signtxt=sign("",message);
std::cout<<signtxt<<std::endl;
BIO *bufio = NULL;
RSA *rsa = NULL;
bufio = BIO_new(BIO_s_file());
BIO_read_filename(bufio, "rsa_public_key.pem");
rsa = PEM_read_bio_RSA_PUBKEY(bufio, NULL, NULL, NULL);
common_tool ct;
std::cout<<ct.verify_rsa(rsa,message,signtxt)<<std::endl;
return 0;
}
common_tool.h
//
// Created by parallels on 9/13/18.
//
#ifndef UNTITLED_3_COMMON_TOOL_H
#define UNTITLED_3_COMMON_TOOL_H
#include
#include "openssl/sha.h"
#include "openssl/rsa.h"
#include "openssl/rand.h"
#include "openssl/objects.h"
#include "openssl/pem.h"
#include "Base64.h"
class common_tool {
public:
static std::string url_encode(const std::string& szToEncode);
static std::string url_decode(const std::string& szToDecode);
static bool verify_rsa(RSA *rsa ,const std::string &content, const std::string &sign);
};
#endif //UNTITLED_3_COMMON_TOOL_H
common_tool.cpp
//
// Created by parallels on 9/13/18.
//
#include "common_tool.h"
std::string common_tool::url_encode(const std::string& szToEncode)
{
std::string src = szToEncode;
char hex[] = "0123456789ABCDEF";
std::string dst;
for (size_t i = 0; i < src.size(); ++i)
{
unsigned char cc = src[i];
if (isascii(cc))
{
if (cc == ' ')
{
dst += "%20";
}
else
dst += cc;
}
else
{
unsigned char c = static_cast<unsigned char>(src[i]);
dst += '%';
dst += hex[c / 16];
dst += hex[c % 16];
}
}
return dst;
}
std::string common_tool::url_decode(const std::string &SRC) {
std::string ret;
char ch;
int i, ii;
for (i=0; i<SRC.length(); i++) {
if (int(SRC[i])==37) {
sscanf(SRC.substr(i+1,2).c_str(), "%x", &ii);
ch=static_cast<char>(ii);
ret+=ch;
i=i+2;
} else {
ret+=SRC[i];
}
}
return (ret);
}
bool common_tool::verify_rsa(/*const char *public_key*/RSA *rsa , const std::string &content, const std::string &sign) {
BIO *bufio = NULL;
EVP_PKEY *evpKey = NULL;
bool verify = false;
EVP_MD_CTX ctx;
int result = 0;
std::string decodedSign = base64_decode(sign);
char *chDecodedSign = const_cast<char*>(decodedSign.c_str());
if (rsa == NULL) {
printf("PEM_read_bio_RSA_PUBKEY failed");
goto safe_exit;
}
evpKey = EVP_PKEY_new();
if (evpKey == NULL) {
printf("EVP_PKEY_new failed");
goto safe_exit;
}
if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) {
printf("EVP_PKEY_set1_RSA failed");
goto safe_exit;
}
EVP_MD_CTX_init(&ctx);
if (result == 1 && (result = EVP_VerifyInit_ex(&ctx,
EVP_md5(), NULL)) != 1) {
printf("EVP_VerifyInit_ex failed");
}
if (result == 1 && (result = EVP_VerifyUpdate(&ctx,
content.c_str(), content.size())) != 1) {
printf("EVP_VerifyUpdate failed");
}
if (result == 1 && (result = EVP_VerifyFinal(&ctx, (unsigned char*)chDecodedSign, decodedSign.size(), evpKey)) != 1) {
printf("EVP_VerifyFinal failed");
}
if (result == 1) {
verify = true;
} else {
printf("verify failed");
}
EVP_MD_CTX_cleanup(&ctx);
//EVP_MD_CTX_destroy(&ctx);
safe_exit:
if (rsa != NULL) {
RSA_free(rsa);
rsa = NULL;
}
if (evpKey != NULL) {
EVP_PKEY_free(evpKey);
evpKey = NULL;
}
if (bufio != NULL) {
BIO_free_all(bufio);
bufio = NULL;
}
return verify;
}
base64.h
//
// Created by parallels on 9/11/18.
//
#ifndef UNTITLED1_BASE64_H
#define UNTITLED1_BASE64_H
#include
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
#endif //UNTITLED1_BASE64_H
这个实现自己找的
上面的代码在和java对接的时候那个url编码有问题,上面的代码编码前后无差,需要更换
unsigned char ToHex(unsigned char x)
{
return x > 9 ? x + 55 : x + 48;
}
unsigned char FromHex(unsigned char x)
{
unsigned char y;
if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
else if (x >= '0' && x <= '9') y = x - '0';
else assert(0);
return y;
}
std::string UrlEncode(const std::string& str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (isalnum((unsigned char)str[i]) ||
(str[i] == '-') ||
(str[i] == '_') ||
(str[i] == '.') ||
(str[i] == '~'))
strTemp += str[i];
else if (str[i] == ' ')
strTemp += "+";
else
{
strTemp += '%';
strTemp += ToHex((unsigned char)str[i] >> 4);
strTemp += ToHex((unsigned char)str[i] % 16);
}
}
return strTemp;
}
std::string UrlDecode(const std::string& str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (str[i] == '+') strTemp += ' ';
else if (str[i] == '%')
{
assert(i + 2 < length);
unsigned char high = FromHex((unsigned char)str[++i]);
unsigned char low = FromHex((unsigned char)str[++i]);
strTemp += high*16 + low;
}
else strTemp += str[i];
}
return strTemp;
}
实测可用