文章目录
- openssl3.2 - exp - export ecc pubkey from ecc priv key
- 概述
- 笔记
- END
openssl3.2 - exp - export ecc pubkey from ecc priv key
概述
前面实验已经生成了ECC私钥, 现在做从ECC私钥(内容为公私钥对, 里面既有私钥信息, 也有公钥信息)导出ECC公钥.
实验对应的命令行为
openssl ec -in ecc_priv_key.pem -pubout -out ecc_pub_key_s.pem
单步调试, 发现命令行实现用的OSSL_STORE_open_ex(), 这个很难受, 没办法移植. 里面没有openssl API可用.
还好运气不错, 前面实验有从buffer load key的实现, 改了一下, 可以正常从buffer载入到私钥的EVP_PKEY*
然后就可以按照openssl命令行的实验进行移植了, 机智:P
如果没有前面做的那些实验, 直接从命令行实现迁移实现, 那就难了(关键调用. e.g. 官方从文件载入私钥(用的API内部完全看不出载入buffer时的openssl API调用), 我是从buffer载入私钥, 没有交集啊). 只能说运气还不错.
最后程序导出的公钥和命令行导出的公钥, 只有回车的区别, 用起来是一样的.
笔记
/*!
* \file exp021_export_pubkey_from_ecc_priv_key.cpp
* \note openssl3.2 - exp - export ecc pubkey from ecc priv key
* 对应的命令 openssl ec -in ecc_priv_key.pem -pubout -out ecc_pub_key_s.pem
* 现在的实现和命令行实现并不一致, 无法从命令行实现中完全移植, 主要是从buffer中载入私钥这块
* 命令行实现是 OSSL_STORE_open_ex(), 里面没用可以参考的openssl API调用
* 换成了前面实验得到的load_priv_key(), 用OSSL_DECODER_from_bio()得到EVP_PKEY*, 后续就一致了
*/#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>#include <stdlib.h>
#include <stdio.h>
#include <assert.h>#include "CMemHookRec.h"#include "ecc_priv_key_s.h"
#include <openssl/decoder.h>
#include <openssl/encoder.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>void my_openssl_app();
bool export_ecc_pub_key_from_ecc_priv_key(const UCHAR* pBuf, int lenBuf, unsigned char*& pdata, size_t& pdata_len);
EVP_PKEY* load_priv_key(bool isPrivkeyBuffer, const char* key_type, OSSL_LIB_CTX* libctx, const char* pBufPrivKey, int lenPrivKey, const char* passphrase);int main(int argc, char** argv)
{setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞mem_hook();my_openssl_app();mem_unhook();return 0;
}void my_openssl_app()
{unsigned char* pdata = NULL;size_t pdata_len = 0;FILE* fp = NULL;size_t sz_wr = 0;do {if (!export_ecc_pub_key_from_ecc_priv_key((const UCHAR*)ucAry_ecc_priv_key_s, sizeof(ucAry_ecc_priv_key_s), pdata, pdata_len)){break;}// 实际应用中, 就可以那取到的公钥数据干活了(e.g. 转成公钥的EVP_PKEY*)// 现在将公钥数据保存成文件, 然后和用命令行从同一个私钥导出的公钥比对一下, 看看是否一样fp = fopen("ecc_pub_key_export_by_app.pem", "wb");if (NULL != fp){sz_wr = fwrite(pdata, sizeof(char), pdata_len, fp);assert(sz_wr == pdata_len);fclose(fp);fp = NULL;// fc /B ecc_pub_key_s.pem ecc_pub_key_export_by_app.pem// 除了回车, 内容一摸一样// openssl.exe 导出的公钥.pem 回车是\r\n// 用 openssl API导出的公钥.pem 回车是 \n}} while (false);if (NULL != pdata){OPENSSL_free(pdata);pdata = NULL;}
}bool export_ecc_pub_key_from_ecc_priv_key(const UCHAR* pBuf, int lenBuf, unsigned char*& pdata, size_t& pdata_len)
{bool b_rc = false;int i_rc = 0;EVP_PKEY* priv_key = NULL;int selection = 0;const char* output_structure = NULL;OSSL_ENCODER_CTX* _ossl_encoder_ctx = NULL;do {// 从buffer载入私钥用了 exp018_from_PrivKeyDat_export_PubKeyDat_ecc 的实现// openssl命令行实现是从文件中拿的, 还不是直接拿, 是用了OSSL_STORE_open_ex(), 实在没办法用, 里面都是内部函数// 看官方实现, 载入私钥和载入公钥是2个实现priv_key = load_priv_key(true, "EC", NULL, (const char*)pBuf, lenBuf, NULL);if (NULL == priv_key){break;}i_rc = EVP_PKEY_set_int_param(priv_key, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 1);if (1 != i_rc){break;}selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS | OSSL_KEYMGMT_SELECT_PUBLIC_KEY;output_structure = "SubjectPublicKeyInfo";_ossl_encoder_ctx = OSSL_ENCODER_CTX_new_for_pkey(priv_key, selection,"PEM", output_structure,NULL);if (NULL == _ossl_encoder_ctx){break;}// 从私钥载入公钥成功, _ossl_encoder_ctx中包含公钥if (1 != OSSL_ENCODER_to_data(_ossl_encoder_ctx, &pdata, &pdata_len)){break;}b_rc = true;} while (false);if (NULL != priv_key){EVP_PKEY_free(priv_key);priv_key = NULL;}if (NULL != _ossl_encoder_ctx){OSSL_ENCODER_CTX_free(_ossl_encoder_ctx);_ossl_encoder_ctx = NULL;}return b_rc;
}EVP_PKEY* load_priv_key(bool isPrivkeyBuffer, const char* key_type, OSSL_LIB_CTX* libctx, const char* pBufPrivKey, int lenPrivKey, const char* passphrase)
{int ret = 0;EVP_PKEY* pkey = NULL;OSSL_DECODER_CTX* dctx = NULL;int selection = 0;int i_tmp = 0;BIO* bio_privKey = NULL;if (NULL == key_type){goto cleanup;}bio_privKey = BIO_new(BIO_s_mem());if (NULL == bio_privKey){goto cleanup;}i_tmp = BIO_write(bio_privKey, pBufPrivKey, lenPrivKey);if (i_tmp != lenPrivKey){goto cleanup;}/** Create PEM decoder context expecting an RSA key.** For raw (non-PEM-encoded) keys, change "PEM" to "DER".** The selection argument here specifies whether we are willing to accept a* public key, private key, or either. If it is set to zero, either will be* accepted. If set to EVP_PKEY_KEYPAIR, a private key will be required, and* if set to EVP_PKEY_PUBLIC_KEY, a public key will be required.*/// 在执行 OSSL_DECODER_CTX_new_for_pkey() 之前, 要选择啥要定好, 否则后面从pkey中取不到东西(公私钥对)// selection = (isPrivkeyBuffer ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY);// EVP_PKEY_KEY_PARAMETERS// selection = EVP_PKEY_PUBLIC_KEY; // 如果载入的是纯公钥数据, 好使// selection = EVP_PKEY_PRIVATE_KEY; // 如果载入的是openssl.exe生成的私钥, 去私钥不好使, OSSL_DECODER_from_bio()就失败selection = EVP_PKEY_KEYPAIR; //如果载入私钥, 就是一个公私钥对dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, key_type,selection,libctx, NULL);if (dctx == NULL) {// fprintf(stderr, "OSSL_DECODER_CTX_new_for_pkey() failed\n");goto cleanup;}/** Set passphrase if provided; needed to decrypt encrypted PEM files.* If the input is not encrypted, any passphrase provided is ignored.** Alternative methods for specifying passphrases exist, such as a callback* (see OSSL_DECODER_CTX_set_passphrase_cb(3)), which may be more useful for* interactive applications which do not know if a passphrase should be* prompted for in advance, or for GUI applications.*/if (passphrase != NULL) {if (OSSL_DECODER_CTX_set_passphrase(dctx,(const unsigned char*)passphrase,strlen(passphrase)) == 0) {// fprintf(stderr, "OSSL_DECODER_CTX_set_passphrase() failed\n");goto cleanup;}}/* Do the decode, reading from file. */if (OSSL_DECODER_from_bio(dctx, bio_privKey) == 0) { // 如果f是stdin, 就需要自己输入私钥内容, 所以函数入参的f必须是一个实际文件的FILE*// fprintf(stderr, "OSSL_DECODER_from_fp() failed\n");goto cleanup;}ret = 1;
cleanup:if (NULL != dctx){OSSL_DECODER_CTX_free(dctx);dctx = NULL;}/** pkey is created by OSSL_DECODER_CTX_new_for_pkey, but we* might fail subsequently, so ensure it's properly freed* in this case.*/if (ret == 0) {EVP_PKEY_free(pkey);pkey = NULL;}if (NULL != bio_privKey){BIO_free(bio_privKey);bio_privKey = NULL;}return pkey;
}