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

文章目录

    • [openssl3.2 - exp - export ecc pubkey from ecc priv key](#openssl3.2 - exp - export ecc pubkey from ecc priv key)
    • 概述
    • 笔记
    • END

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

概述

前面实验已经生成了ECC私钥, 现在做从ECC私钥(内容为公私钥对, 里面既有私钥信息, 也有公钥信息)导出ECC公钥.

实验对应的命令行为

bash 复制代码
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载入私钥, 没有交集啊). 只能说运气还不错.

最后程序导出的公钥和命令行导出的公钥, 只有回车的区别, 用起来是一样的.

笔记

c 复制代码
/*!
* \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

相关推荐
Lazy Dave9 天前
gmssl私钥文件格式
网络安全·ssl·openssl
沉在嵌入式的鱼1 个月前
RK3588移植Openssl库
linux·rk3588·openssl
黑屋里的马1 个月前
ssl相关命令生成证书
服务器·网络·ssl·openssl·gmssl
fangeqin1 个月前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
API开发2 个月前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
码农不惑2 个月前
Rust使用tokio(二)HTTPS相关
https·rust·web·openssl
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法并通过OPENSSL加密验证算法正确性。
linux·服务器·c++·算法·安全·加密·openssl
liulilittle2 个月前
OpenSSL 的 AES-NI 支持机制
linux·运维·服务器·算法·加密·openssl·解密
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法。
linux·服务器·c++·算法·安全·加密·openssl
花花少年2 个月前
Ubuntu系统下交叉编译openssl
openssl·交叉编译