👹
Carlos's Tech Blog
  • 🧔ECUs
    • ZYNQ_Documents
      • [ZYNQ] 构建ZYNQ的BSP工程
      • [ZYNQ] 启动流程
      • [ZYNQ] Secure Boot Flow
      • [ZYNQ] Provisioning Guideline
      • [ZYNQ] Decrypting Partition by the Decrypt Agent Using PUF key
      • [ZYNQ] enabling the cryptsetup on ramdisk
      • [ZYNQ] Encrypt external files based on file system using PUF key
      • [ZYNQ] Loading an Encrypted Linux kernel at U-Boot with a KUP Key
      • [ZYNQ] cross-compile the cryptsetup on Xilinx ZYNQ aarch64 platform
      • [ZYNQ] Linux Linaro系统镜像制作SD卡启动
    • S32G_Documents
      • [S32G] Going through the s32g hard/soft platform
      • [S32G] S32g247's Secure Boot using HSE firmware
        • S32g2 HSE key config
        • How S32g verify secure boot image
        • S32g secure boot signature generation
        • How to download and build S32g Secure boot image
        • [S32G] OTA with Secure Boot
    • RT117x_Documents
      • [RT-117x]IMX RT1170 Provisioning Guideline
      • [RT-117x] Going through the MX-RT1170 hard/soft platform
      • [RT-117x] i.MX-RT1170's Secure Boot
        • [RT-117x]Signing image with the HSM (SignServer)
    • LS104x_Documents
      • [LS104x] bsp project
      • [LS104x] boot flow
      • [LS104x] secure boot
      • [LS104x] Application Note, Using the PKCS#11 in TCU platform
      • [LS104x] 使用ostree更新rootfs
      • [LS104x] ostree的移植
      • [LS104x] Starting with Yocto
      • [LS104x] 使用FIT的kernel格式和initramfs
    • IMX6/8_Documents
      • [IMX6] Defining A U-Boot Command
      • NXP IMX6 嵌入式板子一些笔记
      • NXP-imx6 initialization
    • Vehicle_Apps
      • [SecOC] Tree
        • [SecOC] SecOC Freshness and MAC Truncation
  • 😾TECH
    • Rust Arm OS
      • ARMv7m_Using_The_RUST_Cross_Compiler
    • ARM
      • ARM-v7-M
        • 01_ARMv7-M_处理器架构技术综述
        • 02_ARMv7-M_编程模型与模式
        • 03_ARMv7-M_存储系统结构
        • 04_ARMv7-M_异常处理及中断处理
      • ARM-v8-A
        • 02_ARMv8_基本概念
        • 03_ARMv8_指令集介绍_加载指令集和存储指令集
        • 04_ARMv8_指令集_运算指令集
        • 05_ARMv8_指令集_跳转_比较与返回指令
        • 06_ARMv8_指令集_一些重要的指令
        • 0X_ARMv8_指令集_基于汇编的UART驱动
        • 07_ARMv8_汇编器Using as
        • 08_ARMv8_链接器和链接脚本
        • 09_ARMv8_内嵌汇编(内联汇编)Inline assembly
        • 10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表
        • 11_ARMv8_异常处理(二)- Legacy 中断处理
        • 12_ARMv8_异常处理(三)- GICv1/v2中断处理
        • 13_ARMv8_内存管理(一)-内存管理要素
        • 14_ARMv8_内存管理(二)-ARM的MMU设计
        • 15_ARMv8_内存管理(三)-MMU恒等映射及Linux实现
        • 16_ARMv8_高速缓存(一)cache要素
        • 17_ARMv8_高速缓存(二)ARM cache设计
        • 18_ARMv8_高速缓存(三)多核与一致性要素
        • 19_ARMv8_TLB管理(Translation Lookaside buffer)
        • 20_ARMv8_barrier(一)流水线和一致性模型
        • 21_ARMv8_barrier(二)内存屏障案例
      • ARM Boot Flow
        • 01_Embedded_ARMv7/v8 non-secure Boot Flow
        • 02_Embedded_ARMv8 ATF Secure Boot Flow (BL1/BL2/BL31)
        • 03_Embedded_ARMv8 BL33 Uboot Booting Flow
      • ARM Compiler
        • Compiler optimization and the volatile keyword
      • ARM Development
        • 在MACBOOK上搭建ARMv8架构的ARM开发环境
        • Starting with JLink debugger or QEMU
    • Linux
      • Kernel
        • 0x01_LinuxKernel_内核的启动(一)之启动前准备
        • 0x02_LinuxKernel_内核的启动(二)SMP多核处理器启动过程分析
        • 0x21_LinuxKernel_内核活动(一)之系统调用
        • 0x22_LinuxKernel_内核活动(二)中断体系结构(中断上文)
        • 0x23_LinuxKernel_内核活动(三)中断体系结构(中断下文)
        • 0x24_LinuxKernel_进程(一)进程的管理(生命周期、进程表示)
        • 0x25_LinuxKernel_进程(二)进程的调度器的实现
        • 0x26_LinuxKernel_设备驱动(一)综述与文件系统关联
        • 0x27_LinuxKernel_设备驱动(二)字符设备操作
        • 0x28_LinuxKernel_设备驱动(三)块设备操作
        • 0x29_LinuxKernel_设备驱动(四)资源与总线系统
        • 0x30_LinuxKernel_设备驱动(五)模块
        • 0x31_LinuxKernel_内存管理(一)物理页面、伙伴系统和slab分配器
        • 0x32_LinuxKernel_内存管理(二)虚拟内存管理、缺页与调试工具
        • 0x33_LinuxKernel_同步管理_原子操作_内存屏障_锁机制等
        • 01_LinuxDebug_调试理论和基础综述
      • Userspace
        • Linux-用户空间-多线程与同步
        • Linux进程之间的通信-管道(上)
        • Linux进程之间的通信-管道(下)
        • Linux进程之间的通信-信号量(System V)
        • Linux进程之间的通信-内存共享(System V)
        • Linux进程之间的通信-消息队列(System V)
        • Linux应用调试(一)方法、技巧和工具 - 综述
        • Linux应用调试(二)工具之coredump
        • Linux应用调试(三)工具之Valgrind
        • Linux机制之内存池
        • Linux机制之对象管理和引用计数(kobject/ktype/kset)
        • Linux机制copy_{to, from}_user
        • Linux设备树 - DTS语法、节点、设备树解析等
        • Linux System : Managing Linux Services - inittab & init.d
        • Linux System : Managing Linux Services - initramfs
      • Kernel Examples
        • Linux Driver - GPIO键盘驱动开发记录_OMAPL138
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
        • 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ
        • Linux内核调用SPI驱动_实现OLED显示功能
        • Linux内核调用I2C驱动_驱动嵌套驱动方法MPU6050
    • OPTEE
      • 01_OPTEE-OS_基础之(一)功能综述、简要介绍
      • 02_OPTEE-OS_基础之(二)TrustZone和ATF功能综述、简要介绍
      • 03_OPTEE-OS_系统集成之(一)编译、实例、在QEMU上执行
      • 05_OPTEE-OS_系统集成之(三)ATF启动过程
      • 06_OPTEE-OS_系统集成之(四)OPTEE镜像启动过程
      • 07_OPTEE-OS_系统集成之(五)REE侧上层软件
      • 08_OPTEE-OS_系统集成之(六)TEE的驱动
      • 09_OPTEE-OS_内核之(一)ARM核安全态和非安全态的切换
      • 10_OPTEE-OS_内核之(二)对安全监控模式的调用的处理
      • 11_OPTEE-OS_内核之(三)中断与异常的处理
      • 12_OPTEE-OS_内核之(四)对TA请求的处理
      • 13_OPTEE-OS_内核之(五)内存和cache管理
      • 14_OPTEE-OS_内核之(六)线程管理与并发
      • 15_OPTEE-OS_内核之(七)系统调用及IPC机制
      • 16_OPTEE-OS_应用之(一)TA镜像的签名和加载
      • 17_OPTEE-OS_应用之(二)密码学算法和安全存储
      • 18_OPTEE-OS_应用之(三)可信应用的开发
      • 19_OPTEE-OS_应用之(四)安全驱动开发
      • 20_OPTEE-OS_应用之(五)终端密钥在线下发系统
    • Binary
      • 01_ELF文件_目标文件格式
      • 02_ELF文件结构_浅析内部文件结构
      • 03_ELF文件_静态链接
      • 04_ELF文件_加载进程虚拟地址空间
      • 05_ELF文件_动态链接
      • 06_Linux的动态共享库
      • 07_ELF文件_堆和栈调用惯例以ARMv8为例
      • 08_ELF文件_运行库(入口、库、多线程)
      • 09_ELF文件_基于ARMv7的Linux系统调用原理
      • 10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)
    • Build
      • 01_Script_makefile_summary
    • Rust
      • 02_SYS_RUST_文件IO
    • Security
      • Crypto
        • 1.0_Security_计算机安全概述及安全需求
        • 2.0_Security_随机数(伪随机数)
        • 3.0_Security_对称密钥算法加解密
        • 3.1_Security_对称密钥算法之AES
        • 3.2_Security_对称密钥算法之MAC(CMAC/HMAC)
        • 3.3_Security_对称密钥算法之AEAD
        • 8.0_Security_pkcs7(CMS)_embedded
        • 9.0_Security_pkcs11(HSM)_embedded
      • Tools
        • Openssl EVP to implement RSA and SM2 en/dec sign/verify
        • 基于Mac Silicon M1 的OpenSSL 编译
        • How to compile mbedtls library on Linux/Mac/Windows
    • Embedded
      • eMMC启动介质
  • 😃Design
    • Secure Boot
      • JY Secure Boot Desgin
    • FOTA
      • [FOTA] Module of ECUs' FOTA unit design
        • [FOTA] Tech key point: OSTree Deployment
        • [FOTA] Tech key point: repositories role for onboard
        • [FOTA] Tech key point: metadata management
        • [FOTA] Tech key point: ECU verifying and Decrpting
        • [FOTA] Tech key point: time server
      • [FOTA] Local-OTA for Embedded Linux System
    • Provisioning
      • [X-Shield] Module of the Embedded Boards initialization
    • Report
由 GitBook 提供支持
在本页
  • Using the EVP interface in openssl to implement RSA and SM2 encrypt decrypt sign and verify (C lauguage)
  • 0. Abstract
  • 1. RSA
  • 2. SM2
  • Code Share
  • Reference List
  1. TECH
  2. Security
  3. Tools

Openssl EVP to implement RSA and SM2 en/dec sign/verify

https://github.com/carloscn/blog/issues/44

Using the EVP interface in openssl to implement RSA and SM2 encrypt decrypt sign and verify (C lauguage)

0. Abstract

Openssl provides a series of interfaces that name is EVP structure. Using the interfaces, it is pretty convenient to implement these algorithms of asymmetric RSA or SM2 encryption decryption signature and verification. This paper sorted out the usage of OPENSSL EVP C-lauguage interface, and implement the SM2 and RSA encrypt decrypt signature and verify. Combining with the code, this paper introduced the related functions of RSA and SM2 algorithm. Finally, ran the program to test module functions.

1. RSA

1.1 What is RSA.

RSA is a algorithm of asymmetric en/decryption which name comes from the first letter of the three men's name who are Rivest Adi and Shamir. There are two types of public key and private key. Using the RSA algorithm to encrypt plaintext, you need to assign the public key string and plaintext body to RSA algorithm, and RSA algorithm return corresponding ciphertext for you finally. When you need to decrypt the ciphertext, a private key required for decryption. So we need to hide the private key information to avoid being obtained by attacker. But public key information can share to everyone who even include the attacker. In genaral, the public key owner should be a person with whom you are communicating confidentially.

RSA cannot only encrypt and decrypt information, but also have the signature and verified function. When we want to sign the information, we need to assign the private key string and information to be signed to algorithm of RSA signature, and RSA signature function will return the signature string that the data length is about one hundred bytes to us. When we want to verify the signature information, use public key to do it. The verified result is bool value, it's either true or false. The signature and verified function can help us to avoid the information being modifed by anyone else. It's one of the security system principles - authentication principle.

1.2 RSA gen .pem file(key pairs)

One of the RSA algorithm key points is the key that includes the public key and private key which are called key pairs collectively. The key is irregular string in nature. You can assign the string in your code directly, also you can save it in a "pem" file that is standard key information carrier.

So the first step is gen key pairs for RSA algorithm. This paper and code base openssl EVP C-lauguage interface rather than openssl cmd mode. You should compile the openssl source code on your pc, and source code can be downloaded on https://github.com/openssl/openssl/tags website. I recommand the openssl 1.1.1 that no any letter in the version, because of sm2 supported.

The RSA pem file have different format, that are pkcs#1, pkcs#8 as follows:

1.2.1 PKCS#1 Format

The RSA public key PEM file is specific for RSA keys.

It starts with the tags:

-----BEGIN RSA PUBLIC KEY-----

and it ends with the tags:

------END RSA PUBLIC KEY-----

What's more the PEM have base64 encoded data format that still is PKCS#1

That the format with the tags:

RSAPublicKey ::= SEQUENCE {
  modulus INTEGER, -- n
  publicExponent INTEGER -- e
}

The RSA privae key PEM file is basically same with public key PEM file. It's just different from the public key PEM file is PUBLIC string to PRIVATE string.

-----BEGIN RSA PUBLIC KEY-----

...content

------END RSA PUBLIC KEY-----

1.2.2 PKCS#8 Format

Because RSA is not used exclusively inside X509 and SSL/TLS, a more generic key format is available in the form of PKCS#8, that identifies the type of public key and containsum the relevant data.

The difference between the PKCS#1 and PKCS#8 in content respect is that the PKCS#8 not existing RSA words.

-----BEGIN PUBLIC KEY-----

...content

------END PUBLIC KEY-----

The private key PEM file same as the public one.

**So, when we read key information from the PEM file, must ensure that the PKCS is matching. **

1.2.3 How to generate RSA key

How to generate the RSA key pairs using the C-language. Firstly, Some header files should be included in your project before using the openssl EVP interface.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <openssl/ssl.h>
#include <openssl/md5.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/x509.h>

And gen the PEM file need to prepared some essential information that contains the RSA key length, random seed, and path and name of the PEM file.

#define RSA_KEY_LENGTH 1024
static const char rnd_seed[] = "string to make the random number generator initialized";
#define PRIVATE_RSA_KEY_FILE "fotaprikey.pem"
#define PUBLIC_RSA_KEY_FILE "fotapubkey.pem"

There is the code that generate RSA key file. In the code, RSA structure is defined for including all the RSA algorithm information that are large prime number P-value, Q-value etc. All the PEM file operation which is took on BIO struct, using the BIO_new() BIO_write_file etc. The key information will be read from RSA structure to BIO function, finally, genarate a pairs of PEM file for public key and private key.

int generate_rsa_key_files(const char *pub_keyfile, const char *pri_keyfile,
		const unsigned char *passwd, int passwd_len)
{
	RSA *rsa = NULL;
	RAND_seed(rnd_seed, sizeof(rnd_seed));
	rsa = RSA_generate_key(RSA_KEY_LENGTH, RSA_F4, NULL, NULL);
	if(rsa == NULL) {
		printf("RSA_generate_key error!\n");
		return -1;
	}
	BIO *bp = BIO_new(BIO_s_file());
	if (NULL == bp) {
		printf("generate_key bio file new error!\n");
		return -1;
	}
	if(BIO_write_filename(bp, (void *)pub_keyfile) <= 0) {
		printf("BIO_write_filename error!\n");
		return -1;
	}
	if(PEM_write_bio_RSAPublicKey(bp, rsa) != 1) {
		printf("PEM_write_bio_RSAPublicKey error!\n");
		return -1;
	}
	printf("Create rsa public key ok!\n");
	BIO_free_all(bp);
	bp = BIO_new_file(pri_keyfile, "w+");
	if(NULL == bp){
		printf("generate_key bio file new error2!\n");
		return -1;
	}
	if(PEM_write_bio_RSAPrivateKey(bp, rsa,
			EVP_des_ede3_ofb(), (unsigned char *)passwd,
			passwd_len, NULL, NULL) != 1) {
		printf("PEM_write_bio_RSAPublicKey error!\n");
		return -1;
	}
	printf("Create rsa private key ok!\n");
	BIO_free_all(bp);
	RSA_free(rsa);

	return 0;
}

You should notice that function PEM_wirte_bio_RSAPriateKey(), private key can be assigned the password. If you set the password for private key, everyone try to read the private PEM file, the password need to be inputted as required.

And the other tips you should notice is that bio interface will gen the PKCS#1 PEM files. If you want to gen the PKCS#8 format PEM files, you can use PEM_write_bio_PKCS8PrivateKey() interface to implement it.

1.3 RSA encrypt

Using the RSA to encrypt message, I abstract it to openssl_evp_rsa_encrypt function that need user to transform plaintext, ciphertext buffer, and public key PEM file.

The flow of the function is check user input -> read public key from PEM file to EVP_PKEY structure -> using the EVP_PKEY structure do message encrypt.

/*openssl rsa cipher evp using*/
int openssl_evp_rsa_encrypt(	unsigned char *plain_text, size_t plain_len,
								unsigned char *cipher_text, size_t *cipher_len,
								unsigned char *pem_file)
{
	int ret = 0;
	RSA *rsa = NULL;
	EVP_PKEY* public_evp_key = NULL;
	FILE *fp = NULL;
	BIO *bp = NULL;
	EVP_PKEY_CTX *ctx = NULL;

	/*Check the user input.*/
	if (plain_text == NULL || plain_len == 0 || cipher_text == NULL || *cipher_len == 0) {
		printf("input parameters error, plain_text cipher_text or plain_len is NULL or 0.\n");
		ret = -1;
		goto finish;
	}
	if (NULL == pem_file) {
		printf("input pem_file name is invalid\n");
		ret = -1;
		goto finish;
	}
	fp = fopen((const char*)pem_file, "r");
	if (NULL == fp) {
		printf("input pem_file is not exit.\n");
		ret = -1;
		goto finish;
	}
	fclose(fp);
	fp = NULL;

	//OpenSSL_add_all_algorithms();
	bp = BIO_new(BIO_s_file());
	if (bp == NULL) {
		printf("BIO_new is failed.\n");
		ret = -1;
		goto finish;
	}
	/*read public key from pem file.*/
	ret = BIO_read_filename(bp, pem_file);
	rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
	if (rsa == NULL) {
		ret = -1;
		printf("open_public_key failed to PEM_read_bio_RSAPublicKey Failed, ret=%d\n", ret);
		goto finish;
	}
	public_evp_key = EVP_PKEY_new();
	if (public_evp_key == NULL) {
		ret = -1;
		printf("open_public_key EVP_PKEY_new failed\n");
		goto finish;
	}
	EVP_PKEY_assign_RSA(public_evp_key, rsa);

	/*do cipher.*/
	ctx = EVP_PKEY_CTX_new(public_evp_key, NULL);
	if (ctx == NULL) {
		ret = -1;
		printf("EVP_PKEY_CTX_new failed\n");
		goto finish;
	}
	ret = EVP_PKEY_encrypt_init(ctx);
	if (ret < 0) {
		printf("ras_pubkey_encrypt failed to EVP_PKEY_encrypt_init. ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_PKEY_CTX_set_rsa_padding(ctx, EVP_PADDING_PKCS7);
	if (ret != 1) {
		printf("EVP_PKEY_CTX_set_rsa_padding failed. ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_PKEY_encrypt(ctx, cipher_text, cipher_len, plain_text, plain_len);
	if (ret < 0) {
		printf("ras_pubkey_encrypt failed to EVP_PKEY_encrypt. ret = %d\n", ret);
		goto finish;
	}
	ret = 0;

finish:
	if (public_evp_key != NULL)
		EVP_PKEY_free(public_evp_key);
	if (bp != NULL)
		BIO_free(bp);
	if (ctx != NULL)
		EVP_PKEY_CTX_free(ctx);

	return ret;
}

1.4 RSA decrypt

Same as encryption procession, the decryption is revert procession of encryption. The funtion as follow need transform ciphertext and plaintext buffer, and private key PEM file is required by decrypting. When you generate the private key PEM file assigned a password, the password should be transformed in the function, if not, a NULL (void*0) parameter is filled in this position.

/*openssl rsa decrypt evp using*/
int openssl_evp_rsa_decrypt(unsigned char *cipher_text, size_t cipher_len,
							   unsigned char *plain_text, size_t *plain_len,
							   const unsigned char *pem_file, const unsigned char *passwd)
{
	int ret = 0;
	size_t out_len = 0;
	EVP_PKEY* private_evp_key = NULL;
	RSA *rsa = NULL;
	BIO *bp = NULL;
	FILE *fp = NULL;
	EVP_PKEY_CTX *ctx = NULL;

	/*Check the user input.*/
	if (plain_text == NULL || *plain_len == 0 || cipher_text == NULL || cipher_len == 0) {
		printf("input parameters error, plain_text cipher_text or plain_len is NULL or 0.\n");
		ret = -1;
		goto finish;
	}
	if (NULL == pem_file) {
		printf("input pem_file name is invalid\n");
		ret = -1;
		goto finish;
	}
	fp = fopen((const char*)pem_file, "r");
	if (NULL == fp) {
		printf("input pem_file is not exit.\n");
		ret = -1;
		goto finish;
	}
	fclose(fp);
	fp = NULL;

	//OpenSSL_add_all_algorithms();
	bp = BIO_new(BIO_s_file());
	if (bp == NULL) {
		printf("BIO_new is failed.\n");
		ret = -1;
		goto finish;
	}
	/*read private key from pem file.*/
	ret = BIO_read_filename(bp, pem_file);
	rsa = PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, (void*)passwd);
	if (rsa == NULL) {
		ret = -1;
		printf("open_private_key failed to PEM_read_bio_RSAPrivateKey Failed, ret=%d\n", ret);
		goto finish;
	}
	private_evp_key = EVP_PKEY_new();
	if (private_evp_key == NULL) {
		ret = -1;
		printf("open_private_key EVP_PKEY_new failed\n");
		goto finish;
	}
	EVP_PKEY_assign_RSA(private_evp_key, rsa);
	/*do cipher.*/
	ctx = EVP_PKEY_CTX_new(private_evp_key, NULL);
	if (ctx == NULL) {
		ret = -1;
		printf("EVP_PKEY_CTX_new failed\n");
		goto finish;
	}
	ret = EVP_PKEY_decrypt_init(ctx);
	if (ret != 1) {
		printf("rsa_private_key decrypt failed to EVP_PKEY_decrypt_init. ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_PKEY_CTX_set_rsa_padding(ctx, EVP_PADDING_PKCS7);
	if (ret != 1) {
		printf("EVP_PKEY_CTX_set_rsa_padding failed. ret = %d\n", ret);
		goto finish;
	}
	/* Determine buffer length */
	ret = EVP_PKEY_decrypt(ctx, NULL, &out_len, cipher_text, cipher_len);
	if (ret != 1) {
		printf("rsa_prikey_decrypt failed to EVP_PKEY_decrypt. ret = %d\n", ret);
		goto finish;
	}
	*plain_len = out_len;
	ret = EVP_PKEY_decrypt(ctx, plain_text, plain_len, cipher_text, cipher_len);
	if (ret != 1) {
		printf("rsa_prikey_decrypt failed to EVP_PKEY_decrypt. ret = %d\n", ret);
		goto finish;
	}
	ret = 0;
finish:
	if (private_evp_key != NULL)
		EVP_PKEY_free(private_evp_key);
	if (bp != NULL)
		BIO_free(bp);
	if (ctx != NULL)
		EVP_PKEY_CTX_free(ctx);

	return ret;
}

1.5 RSA En/Decryption Experiment

I designed a test case for testing the RSA En/Decryption functions. Firstly, generate a pairs of public private key, then using the encryption function to encrypt the "hello carlos!" message, finally, clear the plaintext buffer, and transform it to decryption function and check the result of decryption wether same to original plaintext.

My environment as follow:

  • System OS: MacOS Catalina 10.15.6

  • OpenSSL Version: 1.1.1

  • Compiler: clang-gcc

  • IDE: Clion - cmake

I wrote the test code as folllows:

int test_evp_rsa_encrypt_decrypt()
{
	int ret = 0, i = 0;
	unsigned char cipher_out[1024];
	unsigned char plain_in[] = "hello carlos rsa...";
	size_t out_len = 1024;
	size_t in_len = strlen(plain_in);

	ret = openssl_evp_rsa_encrypt(plain_in, in_len, cipher_out, &out_len, PUBLIC_RSA_KEY_FILE);
	if (ret != 0) {
		printf("error in encrypt %d\n", ret);
	}
	printf("rsa plain text is : %s \n", plain_in);
	printf("rsa cipher len = %d text is :\n", out_len);
	for (i = 0; i < out_len; i ++) {
		printf("%02X", cipher_out[i]);
	}
	printf("\n");
	memset(plain_in, '\0', in_len);
	in_len = 1;
	ret = openssl_evp_rsa_decrypt(cipher_out, out_len, plain_in, &in_len, PRIVATE_RSA_KEY_FILE, "12345");
	if (ret != 0) {
		printf("error in decrypt %d\n", ret);
	}
	printf("rsa decrypt len = %d  and text is : %s \n", in_len, plain_in);
	return ret;
}

int test_evp_sm2_encrypt_decrypt()
{
	int ret = 0, i = 0;
	unsigned char cipher_out[1024];
	unsigned char plain_in[] = "hello carlos sm2...";
	size_t out_len = 1024;
	size_t in_len = strlen(plain_in);

	ret = openssl_evp_sm2_encrypt(plain_in, strlen(plain_in), cipher_out, &out_len, PUBLIC_SM2_KEY_FILE);
	if (ret != 0) {
		printf("error in encrypt %d\n", ret);
		return ret;
	}
	printf("sm2 plain text is : %s \n", plain_in);
	printf("sm2 cipher len = %d text is :\n", out_len);
	for (i = 0; i < out_len; i ++) {
		printf("%02X", cipher_out[i]);
	}
	printf("\n");
	memset(plain_in, '\0', in_len);
	in_len = 1;
	ret = openssl_evp_sm2_decrypt(cipher_out, out_len, plain_in, &in_len, PRIVATE_SM2_KEY_FILE, "12345");
	if (ret != 0) {
		printf("error in decrypt %d\n", ret);
	}
	printf("sm2 decrypt len = %d  and text is : %s \n", in_len, plain_in);
	return ret;
}

1.6 RSA signature

RSA sign procession is different from encryption and decryption, that is contains other asymmetric algorithm, Message Digest and Secure Hash Algorithm. And anther difference is that finishing the signature need the private key. It's the opposite of the RSA encryption/decryption process.

// RSA_PKCS1_PADDING  RSA_OAEP_PADDING
int openssl_evp_rsa_signature(unsigned char *sign_rom, size_t sign_rom_len,
								unsigned char *result, size_t *result_len,
								const unsigned char *priv_pem_file, const unsigned char *passwd)
{
	int ret = 0;
	FILE *fp = NULL;
	EVP_PKEY* private_evp_key = NULL;
	RSA *rsa = NULL;
	BIO *bp = NULL;
	EVP_PKEY_CTX *ctx = NULL;
	EVP_MD_CTX *evp_md_ctx = NULL;

	/*Check the user input.*/
	if (sign_rom == NULL || sign_rom_len == 0 || result == NULL || *result_len == 0) {
		printf("input parameters error, content or len is NULL or 0.\n");
		ret = -1;
		goto finish;
	}
	if (NULL == priv_pem_file) {
		printf("input pem_file name is invalid\n");
		ret = -1;
		return ret;
	}
	fp = fopen((const char*)priv_pem_file, "r");
	if (NULL == fp) {
		printf("input pem_file is not exit.\n");
		ret = -1;
		goto finish;
	}
	fclose(fp);
	fp = NULL;
	/*read private key from pem file to private_evp_key*/
	//OpenSSL_add_all_algorithms();
	bp = BIO_new(BIO_s_file());
	if (bp == NULL) {
		printf("BIO_new is failed.\n");
		ret = -1;
		goto finish;
	}
	ret = BIO_read_filename(bp, priv_pem_file);
	rsa = PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, (void*)passwd);
	if (rsa == NULL) {
		ret = -1;
		printf("open_private_key failed to PEM_read_bio_RSAPrivateKey Failed, ret=%d\n", ret);
		goto finish;
	}
	private_evp_key = EVP_PKEY_new();
	if (private_evp_key == NULL) {
		ret = -1;
		printf("open_private_key EVP_PKEY_new failed\n");
		goto finish;
	}
	EVP_PKEY_assign_RSA(private_evp_key, rsa);
	/*do signature*/
	evp_md_ctx = EVP_MD_CTX_new();
	if (evp_md_ctx == NULL) {
		printf("EVP_MD_CTX_new failed.\n");
		ret = -1;
		goto finish;
	}
	EVP_MD_CTX_init(evp_md_ctx);
	ret = EVP_SignInit_ex(evp_md_ctx, EVP_md5(), NULL);
	if (ret != 1) {
		printf("EVP_SignInit_ex failed, ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_SignUpdate(evp_md_ctx, sign_rom, sign_rom_len);
	if (ret != 1) {
		printf("EVP_SignUpdate failed, ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_SignFinal(evp_md_ctx, result, (unsigned int*)result_len, private_evp_key);
	if (ret != 1) {
		printf("EVP_SignFinal failed, ret = %d\n", ret);
		goto finish;
	}
	ret = 0;
finish:
	if (private_evp_key != NULL)
		EVP_PKEY_free(private_evp_key);
	if (bp != NULL)
		BIO_free(bp);
	if (evp_md_ctx != NULL)
		EVP_MD_CTX_free(evp_md_ctx);
	if (ctx != NULL)
		EVP_PKEY_CTX_free(ctx);
	return ret;
}

The second parameter in function EVP_SignInit_ex(evp_md_ctx, EVP_md5(), NULL); is sub-algorithm of RSA sign, multiple message digest and secure hash algorithm are available applying the RSA.

1.7 RSA verify

RSA verify just return a bool result to you that it's either ture or false. If the true, it's surely verified message success, and vice versa.

int openssl_evp_rsa_verify(unsigned char *sign_rom, size_t sign_rom_len,
							unsigned char *result, size_t result_len,
							const unsigned char *pub_pem_file)
{
	int ret = 0;
	FILE *fp = NULL;
	EVP_PKEY* public_evp_key = NULL;
	RSA *rsa = NULL;
	BIO *bp = NULL;
	EVP_PKEY_CTX *ctx = NULL;
	EVP_MD_CTX *evp_md_ctx = NULL;

	/*Check the user input.*/
	if (sign_rom == NULL || sign_rom_len == 0 || result == NULL || result_len == 0) {
		printf("input parameters error, content or len is NULL or 0.\n");
		ret = -1;
		goto finish;
	}
	if (NULL == pub_pem_file) {
		printf("input pem_file name is invalid\n");
		ret = -1;
		goto finish;
	}
	fp = fopen((const char*)pub_pem_file, "r");
	if (NULL == fp) {
		printf("input pem_file is not exit.\n");
		ret = -1;
		goto finish;
	}
	fclose(fp);
	fp = NULL;
	/*read public key from pem file to private_evp_key*/
	//OpenSSL_add_all_algorithms();
	bp = BIO_new(BIO_s_file());
	if (bp == NULL) {
		printf("BIO_new is failed.\n");
		ret = -1;
		goto finish;
	}
	ret = BIO_read_filename(bp, pub_pem_file);
	rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
	if (rsa == NULL) {
		ret = -1;
		printf("open_public_key failed to PEM_read_bio_RSAPublicKey Failed, ret=%d\n", ret);
		goto finish;
	}
	public_evp_key = EVP_PKEY_new();
	if (public_evp_key == NULL) {
		ret = -1;
		goto finish;
	}
	EVP_PKEY_assign_RSA(public_evp_key, rsa);
	/*do verify*/
	evp_md_ctx = EVP_MD_CTX_new();
	if (evp_md_ctx == NULL) {
		printf("EVP_MD_CTX_new failed.\n");
		ret = -1;
		goto finish;
	}
	EVP_MD_CTX_init(evp_md_ctx);
	ret = EVP_VerifyInit_ex(evp_md_ctx, EVP_md5(), NULL);
	if (ret != 1) {
		printf("EVP_VerifyInit_ex failed, ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_VerifyUpdate(evp_md_ctx, result, result_len);
	if (ret != 1) {
		printf("EVP_VerifyUpdate failed, ret = %d\n", ret);
		goto finish;
	}
	ret = EVP_VerifyFinal(evp_md_ctx, sign_rom, (unsigned int)sign_rom_len, public_evp_key);
	if (ret != 1) {
		printf("EVP_VerifyFinal failed, ret = %d\n", ret);
		goto finish;
	}
	ret = 0;
finish:
	if (public_evp_key != NULL)
		EVP_PKEY_free(public_evp_key);
	if (bp != NULL)
		BIO_free(bp);
	if (evp_md_ctx != NULL)
		EVP_MD_CTX_free(evp_md_ctx);
	if (ctx != NULL)
		EVP_PKEY_CTX_free(ctx);

	return ret;
}

1.8 RSA Sign/Verify Experment

I have prepared test code for RSA and Verify similarly. Firstly, gave the message to add signature information using the private key. Secondly, I transformed the signature information and origin message to verify funtion. Finally, the verify funtion return me a boolean result told me the status verified.

int test_evp_rsa_signature_verify()
{
	int ret = 0, i = 0;
	unsigned char sign_out[1024];
	unsigned char plain_in[] = "hello carlos.";
	size_t out_len = 256;
	size_t in_len = strlen(plain_in);

	ret = openssl_evp_rsa_signature(plain_in, in_len, sign_out, &out_len, PRIVATE_RSA_KEY_FILE, "12345");
	if (ret != 0) {
		printf("rsa signature failed!\n");
		return ret;
	}
	printf("rsa %s openssl sign len = %d, signature result: \n", plain_in, out_len);
	for(i = 0; i < out_len; i++) {
		printf("%02X", sign_out[i]);
	}
	printf("\n");

	ret = openssl_evp_rsa_verify(sign_out, out_len, plain_in, in_len, PUBLIC_RSA_KEY_FILE);
	if (ret != 0) {
		printf("rsa verify failed!\n");
	} else {
		printf("rsa verify succeed!\n");
	}
}

2. SM2

2.1 What is SM2

SM is the first letter of abbreviations (商密:Shang Mi). It's invented by National Cryptography Department of China. SM2 is one of the algorithm of the SM series cryptography and it belongs to asymmetric cryptography. So there are public key and private key required and SM2 PEM format file of key pairs is meet PKCS#1 PKCS#8 standards.

Using the SM2 in openssl, the #include "openssl_sm2.h" should be written at head of .c file.

2.2 SM2 gen .pem file(key pairs)

SM2 algorithm belongs to the type of ellipse encryption, so when we use SM2 algorithm, we need to set the type of SM2 curve. There are many curves you can select to ellipse encryption. SM2 pem file is different from RSA. This chapter will introduce that how to generate the sm2 .pem file by giving the code examples.

int generate_sm2_key_files(const char *pub_keyfile, const char *pri_keyfile,
                           const unsigned char *passwd, int passwd_len)
{
    int ret = 0;
    EC_KEY *ec_key = NULL;
    EC_GROUP *ec_group = NULL;
#ifdef MAKE_KEY_TO_RAM
    size_t prikey_len = 0;
	size_t pubkey_len = 0;
	unsigned char *prikey_buffer = NULL;
	unsigned char *pubkey_buffer = NULL;
#endif
    BIO *pri_bio = NULL;
    BIO *pub_bio = NULL;

    ec_key = EC_KEY_new();
    if (ec_key == NULL) {
        ret = -1;
        printf("EC_KEY_new() failed return NULL.\n");
        goto finish;
    }
    ec_group = EC_GROUP_new_by_curve_name(NID_sm2);
    if (ec_group == NULL) {
        ret = -1;
        printf("EC_GROUP_new_by_curve_name() failed, return NULL.\n");
        goto finish;
    }
    ret = EC_KEY_set_group(ec_key, ec_group);
    if (ret != 1) {
        printf("EC_KEY_set_group() failed, ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    ret = EC_KEY_generate_key(ec_key);
    if (!ret) {
        printf("EC_KEY_generate_key() failed, ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    printf("Create sm2 private key ok!");
#ifdef MAKE_KEY_TO_RAM
    pri_bio = BIO_new(BIO_s_mem());
#else
    pri_bio = BIO_new(BIO_s_file());
#endif
    if (pri_bio == NULL) {
        ret = -1;
        printf("pri_bio = BIO_new(BIO_s_file()) failed, return NULL. \n");
        goto finish;
    }
    ret = BIO_write_filename(pri_bio, (void *)pri_keyfile);
    if (ret <= 0) {
        printf("BIO_write_filename error!\n");
        goto finish;
    }
    ret = PEM_write_bio_ECPrivateKey(pri_bio, ec_key, NULL, (unsigned char *)passwd, passwd_len, NULL, NULL);
    if (ret != 1) {
        printf("PEM_write_bio_ECPrivateKey error! ret = %d \n", ret);
        ret = -1;
        goto finish;
    }
#ifdef MAKE_KEY_TO_RAM
    pub_bio = BIO_new(BIO_s_mem());
#else
    pub_bio = BIO_new(BIO_s_file());
#endif
    if (pub_bio == NULL) {
        ret = -1;
        printf("pub_bio = BIO_new(BIO_s_file()) failed, return NULL. \n");
        goto finish;
    }
    ret = BIO_write_filename(pub_bio, (void *)pub_keyfile);
    if (ret <= 0) {
        printf("BIO_write_filename error!\n");
        goto finish;
    }
    ret = PEM_write_bio_EC_PUBKEY(pub_bio, ec_key);
    if (ret != 1) {
        ret = -1;
        printf("PEM_write_bio_EC_PUBKEY error!\n");
        goto finish;
    }
    printf("Create sm2 public key ok!");
#ifdef MAKE_KEY_TO_RAM
    PEM_write_bio_EC_PUBKEY(pub_bio, ec_key);
	prikey_len = BIO_pending(pri_bio);
	pubkey_len = BIO_Pending(pub_bio);
	prikey_buffer = (unsigned char*)OPENSSL_malloc((prikey_len + 1) * sizeof(unsigned char));
	if (prikey_buffer == NULL) {
		ret = -1;
		printf("prikey_buffer OPENSSL_malloc failed, return NULL. \n");
		goto finish;
	}
	pubkey_buffer = (unsigned char*)OPENSSL_malloc((pubkey_len + 1) * sizeof(unsigned char));
	if (pubkey_buffer == NULL) {
		ret = -1;
		printf("pubkey_buffer OPENSSL_malloc failed, return NULL. \n");
		goto finish;
	}
	BIO_read(pri_bio, prikey_buffer, prikey_len);
	BIO_read(pub_bio, pubkey_buffer, pubkey_len);
	prikey_buffer[prikey_len] = '\0';
	pubkey_buffer[pubkey_len] = '\0';
#endif
    finish:
    if (ec_key != NULL)
        EC_KEY_free(ec_key);
    if (ec_group != NULL)
        EC_GROUP_free(ec_group);
#ifdef MAKE_KEY_TO_RAM
    if (prikey_buffer != NULL)
		OPENSSL_free(prikey_buffer);
	if (pubkey_buffer != NULL)
		OPENSSL_free(pubkey_buffer);
#endif
    if (pub_bio != NULL)
        BIO_free_all(pub_bio);
    if (pri_bio != NULL)
        BIO_free_all(pri_bio);
    return ret;
}

2.3 SM2 encrypt

/*openssl sm2 cipher evp using*/
int openssl_evp_sm2_encrypt(	unsigned char *plain_text, size_t plain_len,
                                unsigned char *cipher_text, size_t *cipher_len,
                                unsigned char *pem_file)
{
    int ret = 0;
    size_t out_len = 512;
    unsigned char cipper[512];
    FILE *fp = NULL;
    BIO *bp = NULL;
    EC_KEY *ec_key = NULL;
    EVP_PKEY* public_evp_key = NULL;
    EVP_PKEY_CTX *ctx = NULL;

    /*Check the user input.*/
    if (plain_text == NULL || plain_len == 0 || cipher_text == NULL || *cipher_len == 0) {
        printf("input parameters error, plain_text cipher_text or plain_len is NULL or 0.\n");
        ret = -1;
        return ret;
    }
    if (NULL == pem_file) {
        printf("input pem_file name is invalid\n");
        ret = -1;
        return ret;
    }
    fp = fopen(pem_file, "r");
    if (NULL == fp) {
        printf("input pem_file is not exit.\n");
        ret = -1;
        return ret;
    }
    fclose(fp);
    fp = NULL;

    //OpenSSL_add_all_algorithms();
    bp = BIO_new(BIO_s_file());
    if (bp == NULL) {
        printf("BIO_new is failed.\n");
        ret = -1;
        return ret;
    }
    /*read public key from pem file.*/
    ret = BIO_read_filename(bp, pem_file);
    ec_key = PEM_read_bio_EC_PUBKEY(bp, NULL, NULL, NULL);
    if (ec_key == NULL) {
        ret = -1;
        printf("open_public_key failed to PEM_read_bio_EC_PUBKEY Failed, ret=%d\n", ret);
        goto finish;
    }
    public_evp_key = EVP_PKEY_new();
    if (public_evp_key == NULL) {
        ret = -1;
        printf("open_public_key EVP_PKEY_new failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set1_EC_KEY(public_evp_key, ec_key);
    if (ret != 1) {
        ret = -1;
        printf("EVP_PKEY_set1_EC_KEY failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set_alias_type(public_evp_key, EVP_PKEY_SM2);
    if (ret != 1) {
        printf("EVP_PKEY_set_alias_type to EVP_PKEY_SM2 failed! ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    /*modifying a EVP_PKEY to use a different set of algorithms than the default.*/

    /*do cipher.*/
    ctx = EVP_PKEY_CTX_new(public_evp_key, NULL);
    if (ctx == NULL) {
        ret = -1;
        printf("EVP_PKEY_CTX_new failed\n");
        goto finish;
    }
    ret = EVP_PKEY_encrypt_init(ctx);
    if (ret < 0) {
        printf("sm2_pubkey_encrypt failed to EVP_PKEY_encrypt_init. ret = %d\n", ret);
        EVP_PKEY_free(public_evp_key);
        EVP_PKEY_CTX_free(ctx);
        return ret;
    }
    ret = EVP_PKEY_encrypt(ctx, cipher_text, cipher_len, plain_text, plain_len);
    if (ret < 0) {
        printf("sm2_pubkey_encrypt failed to EVP_PKEY_encrypt. ret = %d\n", ret);
        EVP_PKEY_free(public_evp_key);
        EVP_PKEY_CTX_free(ctx);
        return ret;
    }
    ret = 0;
    finish:
    if (public_evp_key != NULL)
        EVP_PKEY_free(public_evp_key);
    if (ctx != NULL)
        EVP_PKEY_CTX_free(ctx);
    if (bp != NULL)
        BIO_free(bp);
    if (ec_key != NULL)
        EC_KEY_free(ec_key);

    return ret;
}

It's different from the RSA encryption is there is a ec_key need to set except evp_key, the ec_key need be assigned by EVP_PKEY_set1_EC_KEY and using the EVP_PKEY_set_alias_type to set which curve is your select.(SM2 is EVP_PKEY_SM2 macro define)

2.4 SM2 decrypt

/*openssl sm2 decrypt evp using*/
int openssl_evp_sm2_decrypt(unsigned char *cipher_text, size_t cipher_len,
                            unsigned char *plain_text, size_t *plain_len,
                            const unsigned char *pem_file, const unsigned char *passwd)
{
    int ret = 0;
    size_t out_len = 0;
    FILE *fp = NULL;
    BIO *bp = NULL;
    EC_KEY *ec_key = NULL;
    EVP_PKEY* private_evp_key = NULL;
    EVP_PKEY_CTX *ctx = NULL;

    /*Check the user input.*/
    if (plain_text == NULL || cipher_len == 0 || cipher_text == NULL || *plain_len == 0) {
        printf("input parameters error, plain_text cipher_text or plain_len is NULL or 0.\n");
        ret = -1;
        return ret;
    }
    if (NULL == pem_file) {
        printf("input pem_file name is invalid\n");
        ret = -1;
        return ret;
    }
    fp = fopen(pem_file, "r");
    if (NULL == fp) {
        printf("input pem_file is not exit.\n");
        ret = -1;
        return ret;
    }
    fclose(fp);
    fp = NULL;

    //OpenSSL_add_all_algorithms();
    bp = BIO_new(BIO_s_file());
    if (bp == NULL) {
        printf("BIO_new is failed.\n");
        ret = -1;
        return ret;
    }
    /*read public key from pem file.*/
    ret = BIO_read_filename(bp, pem_file);
    ec_key = PEM_read_bio_ECPrivateKey(bp, &ec_key, NULL, (void*)passwd);
    if (ec_key == NULL) {
        ret = -1;
        printf("open_private_key failed to PEM_read_bio_ECPrivateKey Failed, ret=%d\n", ret);
        goto finish;
    }
    private_evp_key = EVP_PKEY_new();
    if (private_evp_key == NULL) {
        ret = -1;
        printf("open_public_key EVP_PKEY_new failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set1_EC_KEY(private_evp_key, ec_key);
    if (ret != 1) {
        ret = -1;
        printf("EVP_PKEY_set1_EC_KEY failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set_alias_type(private_evp_key, EVP_PKEY_SM2);
    if (ret != 1) {
        printf("EVP_PKEY_set_alias_type to EVP_PKEY_SM2 failed! ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    /*modifying a EVP_PKEY to use a different set of algorithms than the default.*/

    /*do cipher.*/
    ctx = EVP_PKEY_CTX_new(private_evp_key, NULL);
    if (ctx == NULL) {
        ret = -1;
        printf("EVP_PKEY_CTX_new failed\n");
        goto finish;
    }
    ret = EVP_PKEY_decrypt_init(ctx);
    if (ret < 0) {
        printf("sm2 private_key decrypt failed to EVP_PKEY_decrypt_init. ret = %d\n", ret);
        goto finish;
    }
    /* Determine buffer length */
    ret = EVP_PKEY_decrypt(ctx, NULL, &out_len, cipher_text, cipher_len);
    if (ret < 0) {
        printf("sm2_prikey_decrypt failed to EVP_PKEY_decrypt. ret = %d\n", ret);
        goto finish;
    }
    *plain_len = out_len;
    ret = EVP_PKEY_decrypt(ctx, plain_text, plain_len, cipher_text, cipher_len);
    if (ret < 0) {
        printf("sm2_prikey_decrypt failed to EVP_PKEY_decrypt. ret = %d\n", ret);
        goto finish;
    }
    ret = 0;
    finish:
    if (private_evp_key != NULL)
        EVP_PKEY_free(private_evp_key);
    if (ctx != NULL)
        EVP_PKEY_CTX_free(ctx);
    if (bp != NULL)
        BIO_free(bp);
    if (ec_key != NULL)
        EC_KEY_free(ec_key);
    return ret;
}

2.5 SM2 signature

int openssl_evp_sm2_signature(unsigned char *sign_rom, size_t sign_rom_len,
                              unsigned char *result, size_t *result_len,
                              const unsigned char *priv_pem_file, const unsigned char *passwd)
{
    int ret = 0;
    size_t out_len = 0;
    FILE *fp = NULL;
    BIO *bp = NULL;
    EC_KEY *ec_key = NULL;
    EVP_PKEY* private_evp_key = NULL;
    EVP_PKEY_CTX *ctx = NULL;
    EVP_MD_CTX *evp_md_ctx = NULL;

    /*Check the user input.*/
    if (sign_rom == NULL || sign_rom_len == 0 || result == NULL || *result_len == 0) {
        printf("input parameters error, plain_text cipher_text or plain_len is NULL or 0.\n");
        ret = -1;
        return ret;
    }
    if (NULL == priv_pem_file) {
        printf("input pem_file name is invalid\n");
        ret = -1;
        return ret;
    }
    fp = fopen(priv_pem_file, "r");
    if (NULL == fp) {
        printf("input pem_file is not exit.\n");
        ret = -1;
        return ret;
    }
    fclose(fp);
    fp = NULL;

    //OpenSSL_add_all_algorithms();
    bp = BIO_new(BIO_s_file());
    if (bp == NULL) {
        printf("BIO_new is failed.\n");
        ret = -1;
        return ret;
    }
    /*read public key from pem file.*/
    ret = BIO_read_filename(bp, priv_pem_file);
    ec_key = PEM_read_bio_ECPrivateKey(bp, &ec_key, NULL, (void*)passwd);
    if (ec_key == NULL) {
        ret = -1;
        printf("open_private_key failed to PEM_read_bio_ECPrivateKey Failed, ret=%d\n", ret);
        goto finish;
    }
    private_evp_key = EVP_PKEY_new();
    if (private_evp_key == NULL) {
        ret = -1;
        printf("open_public_key EVP_PKEY_new failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set1_EC_KEY(private_evp_key, ec_key);
    if (ret != 1) {
        ret = -1;
        printf("EVP_PKEY_set1_EC_KEY failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set_alias_type(private_evp_key, EVP_PKEY_SM2);
    if (ret != 1) {
        printf("EVP_PKEY_set_alias_type to EVP_PKEY_SM2 failed! ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    /*modifying a EVP_PKEY to use a different set of algorithms than the default.*/

    /*do signature.*/
    evp_md_ctx = EVP_MD_CTX_new();
    if (evp_md_ctx == NULL) {
        printf("EVP_MD_CTX_new failed.\n");
        ret = -1;
        goto finish;
    }
    EVP_MD_CTX_init(evp_md_ctx);
    ret = EVP_SignInit_ex(evp_md_ctx, EVP_sm3(), NULL);
    if (ret != 1) {
        printf("EVP_SignInit_ex failed, ret = %d\n", ret);
        goto finish;
    }
    ret = EVP_SignUpdate(evp_md_ctx, sign_rom, sign_rom_len);
    if (ret != 1) {
        printf("EVP_SignUpdate failed, ret = %d\n", ret);
        goto finish;
    }
    ret = EVP_SignFinal(evp_md_ctx, result, (unsigned int*)result_len, private_evp_key);
    if (ret != 1) {
        printf("EVP_SignFinal failed, ret = %d\n", ret);
        goto finish;
    }
    ret = 0;
    finish:
    if (private_evp_key != NULL)
        EVP_PKEY_free(private_evp_key);
    if (ctx != NULL)
        EVP_PKEY_CTX_free(ctx);
    if (bp != NULL)
        BIO_free(bp);
    if (ec_key != NULL)
        EC_KEY_free(ec_key);
    return ret;
}

2.6 SM2 verify

int openssl_evp_sm2_verify(unsigned char *sign_rom, size_t sign_rom_len,
                           unsigned char *result, size_t result_len,
                           const unsigned char *pub_pem_file)
{
    int ret = 0;
    FILE *fp = NULL;
    BIO *bp = NULL;
    EVP_MD_CTX *evp_md_ctx = NULL;
    EC_KEY *ec_key = NULL;
    EVP_PKEY* public_evp_key = NULL;

    /*Check the user input.*/
    if (sign_rom == NULL || sign_rom_len == 0 || result == NULL || result_len == 0) {
        printf("input parameters error, content or len is NULL or 0.\n");
        ret = -1;
        goto finish;
    }
    if (NULL == pub_pem_file) {
        printf("input pem_file name is invalid\n");
        ret = -1;
        goto finish;
    }
    fp = fopen((const char*)pub_pem_file, "r");
    if (NULL == fp) {
        printf("input pem_file is not exit.\n");
        ret = -1;
        goto finish;
    }
    fclose(fp);
    fp = NULL;
    /*read public key from pem file to private_evp_key*/
    //OpenSSL_add_all_algorithms();
    bp = BIO_new(BIO_s_file());
    if (bp == NULL) {
        printf("BIO_new is failed.\n");
        ret = -1;
        return ret;
    }
    /*read public key from pem file.*/
    ret = BIO_read_filename(bp, pub_pem_file);
    ec_key = PEM_read_bio_EC_PUBKEY(bp, NULL, NULL, NULL);
    if (ec_key == NULL) {
        ret = -1;
        printf("open_public_key failed to PEM_read_bio_EC_PUBKEY Failed, ret=%d\n", ret);
        goto finish;
    }
    public_evp_key = EVP_PKEY_new();
    if (public_evp_key == NULL) {
        printf("open_public_key EVP_PKEY_new failed\n");
        ret = -1;
        goto finish;
    }
    ret = EVP_PKEY_set1_EC_KEY(public_evp_key, ec_key);
    if (ret != 1) {
        ret = -1;
        printf("EVP_PKEY_set1_EC_KEY failed\n");
        goto finish;
    }
    ret = EVP_PKEY_set_alias_type(public_evp_key, EVP_PKEY_SM2);
    if (ret != 1) {
        printf("EVP_PKEY_set_alias_type to EVP_PKEY_SM2 failed! ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    /*modifying a EVP_PKEY to use a different set of algorithms than the default.*/
    /*do verify*/
    evp_md_ctx = EVP_MD_CTX_new();
    if (evp_md_ctx == NULL) {
        printf("EVP_MD_CTX_new failed.\n");
        ret = -1;
        goto finish;
    }
    EVP_MD_CTX_init(evp_md_ctx);
    ret = EVP_VerifyInit_ex(evp_md_ctx, EVP_sm3(), NULL);
    if (ret != 1) {
        printf("EVP_VerifyInit_ex failed, ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    ret = EVP_VerifyUpdate(evp_md_ctx, result, result_len);
    if (ret != 1) {
        printf("EVP_VerifyUpdate failed, ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    ret = EVP_VerifyFinal(evp_md_ctx, sign_rom, (unsigned int)sign_rom_len, public_evp_key);
    if (ret != 1) {
        printf("EVP_VerifyFinal failed, ret = %d\n", ret);
        ret = -1;
        goto finish;
    }
    ret = 0;
    finish:
    if (bp != NULL)
        BIO_free(bp);
    if (evp_md_ctx != NULL)
        EVP_MD_CTX_free(evp_md_ctx);
    if (ec_key != NULL)
        EC_KEY_free(ec_key);
    if (public_evp_key != NULL)
        EVP_PKEY_free(public_evp_key);

    return ret;
}

Code Share

You get the driver code and testcase from my github as follows:

https://github.com/carloscn/cryptography

That is a Clion format project.

Reference List

[1]https://blog.csdn.net/weixin_41761608/article/details/107623909

[2]https://www.openssl.org/docs/man1.1.0/man3/EVP_PKEY_decrypt.html

[3]https://www.openssl.org/docs/man1.1.0/man3/EVP_PKEY_encrypt.html

[4]http://man.sourcentral.org/debian-unstable/3+PEM_read_PUBKEY

[5]https://stackoverflow.com/questions/31482186/generating-a-pem-with-openssl-in-c

[6]https://www.cnblogs.com/cocoajin/p/6134382.html

[7]https://github.com/greendow/SM2-signature-creation-and-verification/

上一页Tools下一页基于Mac Silicon M1 的OpenSSL 编译

最后更新于1年前

😾