[英]Sign a SHA256 hash using a DER binary private key
因为我没有网页,所以我会在这里发布这个,我想大多数人来这里是为了寻求答案。
首先,我们希望能够打开受密码保护的二进制私钥并能够读取其内容。 使用 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 内部),它可以是 EVP_PKEY,这是 OpenSSL 中的签名过程所必需的。 这个函数实际上可以带密码来处理二进制文件。 但是,密码必须通过回调函数输入,如果我们不处理 GUI 或命令行程序,只需稍作调整即可。
以下函数采用包含二进制私钥路径的字符串和包含密码的字符指针。
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");
如果您有任何疑问,请告诉我,希望这对某人有所帮助!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.