假设您获得了一个二进制私有文件,如下所示: ,您知道它是DER编码的。
此外,二进制文件受密码保护,假设密码为“密码”
您想要对已经散列的字符串进行签名(RSA),换句话说,您要签名的数据实际上是一个SHA256散列,看起来像这样:
78/YCOiMFRP66tXunCviIi/GaDvgBWFudaWnfcSkU4M=
您想在C ++中使用OpenSSL进行此操作。
我们如何做到这一点?
答案 0 :(得分:0)
由于我不拥有网页,因此我将在此处发布,我想大多数人都会来这里寻求答案。
首先,我们希望能够打开受密码保护的二进制私钥并能够读取其内容。使用OpenSSL命令行,我们可以使用以下代码行:
openssl pkcs8 -in binPrivateKey.key -inform DER -passin pass:password -out private.key
这会将私钥输出到如下所示的文件private.key中:
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCN0peKpgfOL75i
YRv1fqq+oVYsLPVUR/GibYmGKc9InHFy5lYF6OTYjnIIvmkOdRobbGlCUxORX/tL
qY9/8F5mUvVgkcczsIgGdvf9vMQPSf3jjCiKj7j6ucxl1+FwJWmbvgNmiaUR/0q4
....
h0lBer2hP8NFqBPBBTNDwQ==
-----END PRIVATE KEY-----
在OpenSSL中,有一个名为d2i_PKCS8PrivateKey_fp的函数,该函数从字面上接受DER二进制文件指针,并输出一个内部密钥类型(对于OpenSSL内部),该类型可以是ESSL_PKEY,这是OpenSSL签名过程所必需的。该功能实际上可以使用密码来处理二进制文件。但是,必须通过回调函数输入密码,如果我们不处理GUI或命令行程序,则只需稍作调整即可。
以下函数采用一个字符串,该字符串包含二进制私钥的路由,一个char指针包含密码。
EVP_PKEY * PKCS8_DERtoEVP(std::string der_key_file, char * pwd){
FILE * fp = NULL;
EVP_PKEY * evp_pvt = NULL;
OpenSSL_add_all_algorithms(); //This is necessary for some reason I do not fully understand.
fp = fopen(der_key_file.c_str(),"rb"); //Usual file pointer of C++.
if( fp ){
evp_pvt = d2i_PKCS8PrivateKey_fp(fp, &evp_pvt, pwd_cb, pwd); //pwd_cb is a function callback I'll describe later.
if (evp_pvt == NULL) {
//ERROR HANDLING...
fclose(fp);
return NULL ;
}
}
fclose(fp);
return evp_pvt;
}
我提到的调整是为了防止以下情况发生,我正在使用OpenSSL的标准密码回调:
int pwd_cb(char *buf, int size, int rwflag, void *u){
char * pwd;
if (u == NULL){
pwd = "1234";
} else {
pwd = (char*)u;
}
size_t len = strlen(pwd);
if (len > size) len = size;
memcpy(buf,pwd,len);
return len;
}
这样,我们可以“欺骗”系统,以为用户输入是存储在char
数组中的密码。
现在,我们几乎已经准备好签名哈希了。要签名的核心功能如下:
unsigned char* signHash_DER(std::string hash_str, std::string privateKey_file, char * passphrase = NULL){
EVP_PKEY * pkey = PKCS8_DERtoEVP(privateKey_file,passphrase);
EVP_PKEY_CTX *ctx; //Context used in signature.
unsigned char *sig; //The container of the signature.
size_t siglen;
size_t mdlen;
if (pkey == NULL) {
//Error handling...
return NULL;
}
//Here our 'clear message' is actually our hash str and we copy it from the parameters.
unsigned char clear_message[hash_str.length()];
strcpy((char*) clear_message,hash_str.c_str());
//In my implementation we require this decoding, it might not be the case for everyone.
unsigned char * md = base64_decode(clear_message, strlen((char*) clear_message), &mdlen);
//Initiate the context and stablish we're using default RSA.
ctx = EVP_PKEY_CTX_new(pkey, ENGINE_get_default_RSA() /* no engine */);
if(!ctx) {
LOGF_ERROR("Error CTX_new");
return NULL;
}
if (EVP_PKEY_sign_init(ctx) <= 0){
//Error handling
return NULL;
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0){
//Error handling
return NULL;
}
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0){
//Error handling
return NULL;
}
/* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, &siglen, md, mdlen) <= 0){
//Error handling
return NULL;
}
sig = (unsigned char*)OPENSSL_malloc(siglen);
if (!sig){
//Error handling
return NULL;
}
if (EVP_PKEY_sign(ctx, sig, &siglen, md, mdlen) <= 0){
std::cout << "Error sign";
return NULL;
}
/* Signature is siglen bytes written to buffer sig */
// encode to base64 again .
size_t signed_string_len;
unsigned char * signed_string = base64_encode(sig, siglen, &signed_string_len);
return signed_string;
}
这已经足够了,大部分最新的功能代码是从OpenSSL文档中获得的,这很糟糕,但是耐心地完成了工作。
通话示例:
unsigned char* signature_decoded = signHash_DER(hashed_string, "privateBinary.key","dont4get");
如果您有任何疑问,请告诉我,希望这对您有所帮助!