• 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
    
    • 1

    单步调试, 发现命令行实现用的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 
    #include 
    
    #include 
    #include 
    #include 
    
    #include "CMemHookRec.h"
    
    #include "ecc_priv_key_s.h"
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239

    END

  • 相关阅读:
    ADC架构种类说明_笔记
    跨域问题及前端的解决方法
    【Linux】—— 常见指令の图解总结(万字详解)
    浅尝 ECDHE 协议流程
    MST2100是一款用于摩托车单相磁电机 调压器的控制IC
    MySQL---MGR保姆版
    leecode337. 打家劫舍 III
    YOLOv5 分类模型 Top 1和Top 5 指标实现
    科研论文作图小技巧
    丝绸之路网络安全论坛成功举办,开源网安受邀分享软件供应链安全落地经验
  • 原文地址:https://blog.csdn.net/LostSpeed/article/details/136676445