前言
openssl API中有两套接口进行签名(sign)和校验(verify)操作, 一套是老接口 EVP_Sign* 一套是新接口EVP_DigestSign*和EVP_DigestVerify*.后续推荐使用新的接口
下面的例子使用新的接口来进行签名和校验,
概览
一般来说,签名一个消息分为3个步骤:
- 初始化(init)一个context,传入摘要/哈希函数和一个EVP_PKEY类型的私钥
- 传入(Update)要被签名的消息数据(如果数据量较大,这一步可以重复多次,分段将数据传入,避免内存不够的问题)
- Finalize context 得到签名数据
初始化时,有两点需要注意:
- 我们需要选取一个摘要算法,这个体现在传入的摘要函数上
- 我们为摘要算法提供一个包含私钥的EVP_PKEY指针对象
初始化调用EVP_DigestSignInit.
传入被签名的消息调用EVP_DigestSignUpdate (支持循环调用,分段传入)
最终Finalize调用EVP_DigestSignFinal.
一般来说,校验者手上有的是公钥,校验的步骤是类似的,不同点在于使用EVP_DigestVerify*这套接口并且EVP_PKEY是包含的公钥。
如果校验者手上有私钥,也可以这么校验。使用私钥使用EVP_DigestSign*接口进行签名得到签名数据,最后再和传入的签名数据进行对比来判断。
代码示例
签名代码
void signText(){
#define PRIVATEKEYPATH "/home/gateway/Eden/Test/openssl/rsa_key.private"
EVP_MD_CTX *mdctx = NULL;
int ret = 0;
unsigned char *sig = NULL;
size_t slen, len ,len1;
FILE* fd;
const char *msg = (const char*)readFile("/home/gateway/Eden/Test/openssl/config", "r", len);
std::unique_ptr<BIO, decltype(&BIO_free_all)> keybio(BIO_new_file(PRIVATEKEYPATH, "r"), BIO_free_all);
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> keyevp(PEM_read_bio_PrivateKey(keybio.get(), NULL,
NULL,
/*&cb_data*/ NULL), EVP_PKEY_free);
size_t siglen = EVP_PKEY_size(keyevp.get());
cout << "siglen =" << siglen << endl;
/* Create the Message Digest Context */
if(!(mdctx = EVP_MD_CTX_create())) goto err;
ret = 1;
/* Initialise the DigestSign operation - SHA-256 has been selected as the message digest function in this example */
if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, keyevp.get())) goto err;
ret = 2;
/* Call update with the message */
if(1 != EVP_DigestSignUpdate(mdctx, msg, len)) goto err;
ret = 3;
/* Finalise the DigestSign operation */
/* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the
* signature. Length is returned in slen */
if(1 != EVP_DigestSignFinal(mdctx, NULL, &slen)) goto err;
++ret;
/* Allocate memory for the signature based on size in slen */
if(!(sig = (unsigned char*)OPENSSL_malloc(sizeof(unsigned char) * slen))) goto err;
++ret;
/* Obtain the signature */
if(1 != EVP_DigestSignFinal(mdctx, sig, &slen)) goto err;
++ret;
/* Success */
err:
if(ret != 6)
{
cout << "ret=" << ret << endl;
/* Do some error handling */
}
/* Clean up */
if(sig && !ret) OPENSSL_free(sig);
if(mdctx) EVP_MD_CTX_destroy(mdctx);
}
sig就是对应的签名数据。
验证代码
bool VerifySign(){
#define PUBLICKEYPATH "/home/gateway/Eden/Test/openssl/rsa_key.public"
size_t siglen, len ;
int r;
const char *msg = (const char*)readFile("/home/gateway/Eden/Test/openssl/config", "r", len);
unsigned char *sig = NULL;
std::unique_ptr<BIO, decltype(&BIO_free_all)> bmd(BIO_new(BIO_f_md()), BIO_free_all);
std::unique_ptr<BIO, decltype(&BIO_free_all)> keybio(BIO_new_file(PUBLICKEYPATH, "r"), BIO_free_all);
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> keyevp(PEM_read_bio_PUBKEY(keybio.get(), NULL,
NULL,
/*&cb_data*/ NULL), EVP_PKEY_free);
std::unique_ptr<BIO, decltype(&BIO_free_all)> sigbio(BIO_new_file("/home/gateway/Eden/Test/openssl/sgn2.sign", "rb"), BIO_free_all);
siglen = EVP_PKEY_size(keyevp.get());
if(1 != EVP_DigestSignInit(mdctx.get(), NULL, EVP_sha256(), NULL, keyevp.get())){
//DEBUG(Debug::WARN, "Error EVP_DigestSignInit\n");
return false;
}
if(1 != EVP_DigestVerifyUpdate(mdctx.get(), msg, len)){
//DEBUG(Debug::WARN, "Error EVP_DigestSignUpdate\n");
return false;
}
sig = (unsigned char *)OPENSSL_malloc(siglen);
siglen = BIO_read(sigbio.get(), sig, siglen);
r=EVP_DigestVerifyFinal(mdctx.get(), sig, siglen);
OPENSSL_free(sig);
if(1 == r){
DEBUG(Debug::NOTICE, "VerifySign true\n");
return true;
}
else{
DEBUG(Debug::WARN, "VerifySign false\n");
return false;
}
}
如果签名数据和公钥都已经存在于内存中,使用如下示例:
bool A71CH::VerifySign(char* publickeyPath, const char* msg, size_t msglen, const unsigned char* sig, size_t siglen){
if(publickeyPath == NULL || msg == NULL || sig == NULL){
DEBUG(Debug::WARN, "VerifySign paramters null");
return false;
}
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_free);
std::unique_ptr<BIO, decltype(&BIO_free_all)> keybio(BIO_new_file(publickeyPath, "r"), BIO_free_all);
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> keyevp(PEM_read_bio_PUBKEY(keybio.get(), NULL,NULL,NULL), EVP_PKEY_free);
if(1 != EVP_DigestVerifyInit(mdctx.get(), NULL, EVP_sha256(), NULL, keyevp.get())){
DEBUG(Debug::WARN, "VerifySign failed");
return false;
}
if(1 != EVP_DigestVerifyUpdate(mdctx.get(), msg, msglen)){
DEBUG(Debug::WARN, "VerifySign failed");
return false;
}
if(1 == EVP_DigestVerifyFinal(mdctx.get(), sig, siglen)){
DEBUG(Debug::NOTICE, "VerifySign OK");
return true;
}
else{
DEBUG(Debug::WARN, "VerifySign failed");
return false;
}
}
参考:https://wiki.openssl.org/index.php/EVP_Signing_and_Verifying