openssl3.2 - exp - export ecc pubkey from ecc priv key

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;
}

END

相关推荐

  1. openssl3.2 - exp - RAND_bytes_ex

    2024-03-17 11:48:01       29 阅读
  2. openssl+EVP详解

    2024-03-17 11:48:01       28 阅读
  3. openssl3.2 - exp - get openssl version info

    2024-03-17 11:48:01       9 阅读
  4. openssl3.2 - exp - PEM <==> DER

    2024-03-17 11:48:01       26 阅读
  5. openssl3.2 - exp - AES-256-GCM

    2024-03-17 11:48:01       16 阅读
  6. openssl3.2 - exp - generate prime

    2024-03-17 11:48:01       20 阅读
  7. openssl3.2 - exp - aes-128-cbc

    2024-03-17 11:48:01       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-17 11:48:01       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-17 11:48:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-17 11:48:01       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-17 11:48:01       20 阅读

热门阅读

  1. 【Flutter】IOS运行工程二次启动崩溃问题。

    2024-03-17 11:48:01       22 阅读
  2. element-plus中的表单校验

    2024-03-17 11:48:01       21 阅读
  3. Vue中nextTick一文详解

    2024-03-17 11:48:01       21 阅读
  4. docker运行sqlserver服务

    2024-03-17 11:48:01       17 阅读
  5. 码云使用 创建项目

    2024-03-17 11:48:01       24 阅读
  6. 比特币,区块链及相关概念简介(三)

    2024-03-17 11:48:01       23 阅读