这个代码有可能不符合国标。。。
#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ecdsa.h>
#include <openssl/sha.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/objects.h>
#include <openssl/buffer.h>
#include <openssl/sm2.h>
int hashForSM3(unsigned char* clearText, int clearTextLen, unsigned char* sm3Data){
int ret = -1;
//初始化摘要结构体
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if(!mdctx)
return -1;
EVP_MD_CTX_init(mdctx);
//设置摘要算法和密码算法引擎
if(!EVP_DigestInit_ex(mdctx, EVP_sm3(), NULL))
goto ERR;
//输入原文clearText
if(!EVP_DigestUpdate(mdctx,clearText, clearTextLen))
goto ERR;
//输出摘要值,外部判断ret是否为32作为成功条件
if(!EVP_DigestFinal(mdctx, sm3Data, (unsigned int*)&ret))
goto ERR;
ERR:
EVP_MD_CTX_destroy(mdctx);
return ret;
}
int hashForSM3WithSM2(unsigned char* clearText, int clearTextLen, unsigned char* puk, int pukLen, unsigned char* sm3Data){
//以下为国密标准推荐参数,id="1234567812345678",长度是128bit则0x0080
unsigned char sm2_par_dig[210] = {//idlen[2]+id[16]+parm[128]+puk[64]
0x00,0x80,
0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,
0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,
0x28,0xE9,0xFA,0x9E,0x9D,0x9F,0x5E,0x34,0x4D,0x5A,0x9E,0x4B,0xCF,0x65,0x09,0xA7,
0xF3,0x97,0x89,0xF5,0x15,0xAB,0x8F,0x92,0xDD,0xBC,0xBD,0x41,0x4D,0x94,0x0E,0x93,
0x32,0xC4,0xAE,0x2C,0x1F,0x19,0x81,0x19,0x5F,0x99,0x04,0x46,0x6A,0x39,0xC9,0x94,
0x8F,0xE3,0x0B,0xBF,0xF2,0x66,0x0B,0xE1,0x71,0x5A,0x45,0x89,0x33,0x4C,0x74,0xC7,
0xBC,0x37,0x36,0xA2,0xF4,0xF6,0x77,0x9C,0x59,0xBD,0xCE,0xE3,0x6B,0x69,0x21,0x53,
0xD0,0xA9,0x87,0x7C,0xC6,0x2A,0x47,0x40,0x02,0xDF,0x32,0xE5,0x21,0x39,0xF0,0xA0,
};
//对应规范pdf中的章节8.1
memcpy(sm2_par_dig + 2 + 16 + 128, puk, pukLen);
unsigned char* sm3_e = (unsigned char*)malloc(32 + clearTextLen);
if(32 != hashForSM3(sm2_par_dig, 210, sm3_e)){
free(sm3_e);
return -1;
}
//对应规范pdf中的章节8.2
memcpy(sm3_e + 32, clearText, clearTextLen);
if(32 != hashForSM3(sm3_e, 32 + clearTextLen, sm3Data)){
free(sm3_e);
return -1;
}
free(sm3_e);
return 0;
}
int main(){
int i = 0;
BIO *bio_pri = NULL;
BIO *bio_puk = NULL;
EVP_PKEY *pkey_pri = NULL;
EC_KEY *prikey = NULL;
EVP_PKEY *pkey_puk = NULL;
EC_KEY *pubkey = NULL;
X509 *cert = NULL;
unsigned char* puk = NULL;
int pukLen = 0;
unsigned char* clearText = (unsigned char*)"1234567890123456";
int clearTextLen = strlen((char*)clearText);
unsigned char sm3Data[32] = {0};
int sm3DataLen = 0;
unsigned char out[1024] = {0};
int outLen = 0;
///公钥相关结构
//bio_puk = BIO_new_file("server_cert.der", "r");
bio_puk = BIO_new_file("server_cert.pem", "r");
if (!bio_puk) goto ERR;
//cert = d2i_X509_bio(bio_puk, NULL);
cert = PEM_read_bio_X509(bio_puk, NULL, NULL, NULL);
if (!cert) goto ERR;
pkey_puk = X509_get_pubkey(cert);
if (!pkey_puk) goto ERR;
pubkey = EVP_PKEY_get0_EC_KEY(pkey_puk);//don't free
if (!pubkey) goto ERR;
//要去除04标识
pukLen = i2d_PublicKey(pkey_puk,(unsigned char**)&puk) - 1;
if (!puk) goto ERR;
///私钥相关结构
bio_pri = BIO_new_file("server_private.key", "r");
if (!bio_pri) goto ERR;
pkey_pri = PEM_read_bio_PrivateKey(bio_pri, NULL, NULL, NULL);
if (!pkey_pri) goto ERR;
prikey = EVP_PKEY_get0_EC_KEY(pkey_pri);//don't free
if (!prikey) goto ERR;
///sm3 sm2 sign/verify
if(hashForSM3WithSM2(clearText, clearTextLen, puk, pukLen, sm3Data))
goto ERR;
if (!SM2_sign(NID_undef, sm3Data, sm3DataLen, out, (unsigned int*)&outLen, prikey))
goto ERR;
printf("out[%d]:\n", outLen);
for (int i = 0; i != outLen; i++) {
printf("%02x ", *(out + i));
if ((i != 0 && (i + 1) % 16 == 0) || i == outLen - 1) {
printf("\n");
}
}
printf("\n");
FILE *fp_w = fopen("server_16.sig", "wb");
for(int i=0; i < outLen; i++)
{
fwrite(((unsigned char*)(out+i)), 1, 1, fp_w);
}
fclose(fp_w);
FILE *fp_r = fopen("server_16.sig", "rb");
size_t slen = 0;
fseek(fp_r, 0, SEEK_SET);
int nRet;
for(int i=0; ; i++)
{
nRet=fread(&(out[i]), 1, 1, fp_r);
if(1 != nRet)
{
break;
}
printf("%02x ",out[i]);
slen++;
}
fclose(fp_r);
if (1 != SM2_verify(NID_undef, sm3Data, sm3DataLen, out, outLen, pubkey))
goto ERR;
printf("verify success\n");
ERR:
if(puk) OPENSSL_free(puk);
if(bio_pri) BIO_free_all(bio_pri);
if(bio_puk) BIO_free_all(bio_puk);
if(pkey_pri) EVP_PKEY_free(pkey_pri);
if(pkey_puk) EVP_PKEY_free(pkey_puk);
if(cert) X509_free(cert);
return 0;
}
参考
Gmssl实现SM2签名验签
ChatGPT
SM2是一种基于椭圆曲线密码学的公钥密码算法,主要包括密钥对生成、加密算法和签名算法三部分。SM3是一种密码散列函数,可以产生一个256位的散列值(hash)。
SM2签名算法和验签算法使用SM3散列函数来生成消息的摘要,然后对该摘要进行加密或验证。一般过程如下:
1. 准备阶段:
- 确保你有一对有效的SM2密钥(公钥和私钥)。
- 确保SM3散列函数库可用。
2. 签名流程:
- Step 1: 使用SM3散列函数对待签名的消息生成摘要。
- Step 2: 使用生成的摘要和SM2私钥生成签名。
3. 验签流程:
- Step 1: 再次使用SM3散列函数对原文消息生成摘要。
- Step 2: 使用SM2公钥和收到的签名来验证生成的摘要。
具体代码实现取决于你所使用的加密库,但以下是一个基本的伪代码,给出了使用SM2和SM3进行签名和验签的大体流程:
#include <your_crypto_library.h> // 替换成你使用的加密库的头文件
// 函数用于生成SM2签名
bool sm2_sign_message(const unsigned char* private_key, const unsigned char* message, size_t message_len, unsigned char* signature) {
unsigned char digest[32]; // SM3的散列值大小为256位,即32字节
sm3(message, message_len, digest);
// 使用SM2私钥对SM3摘要进行签名
if (sm2_sign(private_key, digest, sizeof(digest), signature)) {
return true; // 签名成功
}
return false; // 签名失败
}
// 函数用于验证SM2签名
bool sm2_verify_signature(const unsigned char* public_key, const unsigned char* message, size_t message_len, const unsigned char* signature) {
unsigned char digest[32];
sm3(message, message_len, digest);
// 使用SM2公钥验证签名
if (sm2_verify(public_key, digest, sizeof(digest), signature)) {
return true; // 验签成功
}
return false; // 验签失败
}
上述代码中的`sm3`、`sm2_sign`和`sm2_verify`需要替换为实际使用的加密库提供的API。
请注意,这只是一个伪代码示例,实际的实现会依赖于你选择使用的加密库(例如 OpenSSL、GmSSL、LibTomCrypt 等)。一些库可能已经提供了简化的API来直接对消息进行签名和验证,而无需手动进行散列。参考相关文档和API来获得具体的实现方法。
GmSSL 是一个由中国科学院国家信息安全重点实验室开发的开源密码学库,它基于 OpenSSL 实现,并在此之上添加了支持中华人民共和国国家标准的 SM2/SM3/SM4 等密码算法。SM2 是一种公钥密码算法,SM3 是一种密码散列函数。在实际的安全协议中,经常需要将 SM3 散列函数与 SM2 公钥算法一起使用以实现签名与验签,这通常被称为 SM3WithSM2。
以下是一个使用 GmSSL 库实现 SM3WithSM2 签名和验签的简单过程说明和示例。
步骤 1: 安装 GmSSL
首先你需要在你的系统上安装 GmSSL。这可以通过编译源代码或直接从提供的包安装完成。
步骤 2: 生成 SM2 密钥对
在命令行中,使用 GmSSL 的 ecparam 和 genpkey 命令可以生成 SM2 密钥对。
生成 SM2 参数文件:
gmssl ecparam -genkey -name SM2 -out sm2key.pem
生成 SM2 公钥(从私钥文件提取):
gmssl pkey -pubout -in sm2key.pem -out sm2pub.pem
步骤 3: SM3WithSM2 签名
通过 pkeyutl 命令,你可以使用 SM2 密钥对文件创建一个数字签名。
对文件 message.txt 进行签名:
gmssl pkeyutl -sign -pkeyopt ec_sign_algor:sm2 -inkey sm2key.pem -in message.txt -out sign.sm2
步骤 4: SM3WithSM2 验签
验签是指用公钥验证数字签名是否有效。如果签名是用相应的私钥生成的,则此过程成功。
对签名进行验证:
gmssl pkeyutl -verify -pkeyopt ec_sign_algor:sm2 -pubin -inkey sm2pub.pem -in message.txt -sigfile sign.sm2
如果签名验证成功,则命令不会返回错误,并且通常会输出“Signature Verified Successfully”或类似消息。
以上过程可以在 GmSSL 工具的帮助下在命令行中执行。具体的命令参数依据版本可能有所不同。在您的应用程序中使用 GmSSL 库进行编程时,您可以使用相应的 API 函数来实现类似的功能。
GmSSL是一个开源的密码学库,实现了SM2、SM3、SM4等国家密码算法,由中国科学院信息工程研究所实施。SM2是一种椭圆曲线公钥密码算法,主要用于数字签名、密钥交换和公钥加密。SM3是一种加密哈希函数。在GmSSL中,你可以使用SM2算法和SM3哈希函数结合来进行签名和验证。
下面提供一个GmSSL利用SM3和SM2进行签名和验证的C代码示例。请注意,要成功运行以下代码,你需要有GmSSL库的开发环境设置好,代码仅供参考:
#include <stdio.h>
#include <string.h>
#include <openssl/sm2.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/objects.h>
#include <openssl/err.h>
int main() {
int ret = 0;
//初始化
EVP_PKEY *sm2key = NULL;
EVP_MD_CTX *md_ctx = NULL;
EC_KEY *ec_key = NULL;
unsigned char *sig = NULL;
unsigned int siglen;
// 生成SM2密钥对
ec_key = EC_KEY_new_by_curve_name(NID_sm2);
if (!ec_key || !EC_KEY_generate_key(ec_key)) {
fprintf(stderr, "Error generating the ECC key.\n");
goto end;
}
sm2key = EVP_PKEY_new();
if (!EVP_PKEY_assign_EC_KEY(sm2key, ec_key)) {
fprintf(stderr, "Error assigning ECC key to EVP_PKEY structure.\n");
EC_KEY_free(ec_key);
goto end;
}
ec_key = NULL; // `sm2key` now owns the `ec_key`
// 创建并初始化签名上下文
md_ctx = EVP_MD_CTX_create();
if (!md_ctx) {
fprintf(stderr, "EVP_MD_CTX_create failed.\n");
goto end;
}
// SM3摘要,并生成SM2签名
if (!EVP_DigestSignInit(md_ctx, NULL, EVP_sm3(), NULL, sm2key)) {
fprintf(stderr, "EVP_DigestSignInit failed.\n");
goto end;
}
// Example message to sign
unsigned char message[] = "The quick brown fox jumps over the lazy dog";
size_t message_len = strlen((char *)message);
// 计算签名所需要的空间
if (!EVP_DigestSign(md_ctx, NULL, &siglen, message, message_len)) {
fprintf(stderr, "EVP_DigestSign failed.\n");
goto end;
}
sig = OPENSSL_malloc(siglen);
if (!sig) {
fprintf(stderr, "OPENSSL_malloc failed.\n");
goto end;
}
// 生成签名
if (!EVP_DigestSign(md_ctx, sig, &siglen, message, message_len)) {
fprintf(stderr, "EVP_DigestSign failed.\n");
goto end;
}
// 输出签名
printf("Signature: ");
for (unsigned int i = 0; i < siglen; i++) {
printf("%02x", sig[i]);
}
printf("\n");
// 签名验证
// 重新初始化摘要上下文,设置为验证模式
if (!EVP_DigestVerifyInit(md_ctx, NULL, EVP_sm3(), NULL, sm2key)) {
fprintf(stderr, "EVP_DigestVerifyInit failed.\n");
goto end;
}
// 验证签名
ret = EVP_DigestVerify(md_ctx, sig, siglen, message, message_len);
if (ret == 1) {
printf("Signature verification succeeded.\n");
} else if (ret == 0) {
printf("Signature verification failed.\n");
} else {
fprintf(stderr, "EVP_DigestVerify failed.\n");
}
end:
// 释放资源
if (ec_key) EC_KEY_free(ec_key);
if (sig) OPENSSL_free(sig);
if (md_ctx) EVP_MD_CTX_destroy(md_ctx);
if (sm2key) EVP_PKEY_free(sm2key);
// 清理OpenSSL错误队列
ERR_print_errors_fp(stderr);
return (ret == 1) ? EXIT_SUCCESS : EXIT_FAILURE;
}
这个简单的程序展示了使用OpenSSL库,具体是GmSSL对应的接口来进行SM3摘要计算和SM2签名生成与验证的过程。务必确保GmSSL正确安装并链接在编译过程中。
编译前,确保在GCC命令行中加入 -lcrypto 参数来链接OpenSSL的加密库。如果安装了标准的OpenSSL而不是GmSSL,代码可能需要进行调整。由于GmSSL支持SM2等中国国家密码标准,而标准的OpenSSL可能不支持,因诸如SM2、SM3算法这类特性可能需要特殊的OpenSSL分支如GmSSL才能支持。
上面的代码段展示了如何用GmSSL实现SM3WithSM2的签名过程,并验证签名。这份代码应该在有GmSSL支持的环境中编译和运行。在运行之前,请确保您的环境配置正确。
编译该代码段的命令类似于:
gcc -o sm2_sign_verify example.c -lssl -lcrypto -lgmssl
此处 -lssl -lcrypto -lgmssl 参数确保链接了必要的库。若是GmSSL与标准的OpenSSL同时安装,可能需要指定GmSSL的库路径来确保链接正确的库版本。
如果编译中遇到问题,请确保安装了GmSSL库,且库路径被正确指定在编译器的搜索路径中。另外,GmSSL的安装和配置可能会略有不同,具体可以参考GmSSL的官方文档。
在实际使用中,建议读者理解SM2签名和验证的过程,正确处理密钥生命周期,同时注意任何可能引起安全问题的代码实践,比如避免内存泄漏、正确处理错误等。
文心一言
GM/T 0003.5-2012是中国国家密码管理局发布的一项关于SM2椭圆曲线公钥密码算法的标准,具体针对的是该算法的参数定义部分。该标准于2012年3月21日发布并同时实施,是中国在密码学领域的一项重要标准。
SM2椭圆曲线公钥密码算法是中国自主设计的一种公钥密码算法,它在保障网络通信安全、保护电子交易和电子政务安全等方面有着重要作用。GM/T 0003.5-2012标准详细规定了SM2算法的曲线参数,并提供了数字签名与验证、密钥交换与验证、消息加解密等操作的示例。
此标准的发布和实施,对于推动中国密码产业的发展,提升中国信息安全水平具有重要意义。同时,它也为中国在国际密码学领域的交流与合作提供了重要依据。
请注意,密码学是一个快速发展的领域,新的标准和算法不断涌现。因此,对于涉及密码学应用的系统,应持续关注最新的标准和算法动态,以确保系统的安全性和稳定性。
国密SM2椭圆曲线公钥密码算法是中国自主设计的一种公钥密码算法,它在网络通信安全、电子交易和电子政务安全等方面起着至关重要的作用。SM2算法基于椭圆曲线数学理论,具有高强度、高安全性等特点,能够有效地保护数据的机密性、完整性和真实性。
SM2算法主要包括密钥交换、数字签名和公钥加密三部分。在密钥交换过程中,通信双方可以通过一系列计算生成共享的密钥,用于后续的加密和解密操作。数字签名部分则允许发送方对消息进行签名,接收方通过验证签名来确认消息的真实性和完整性。公钥加密部分则允许发送方使用接收方的公钥对消息进行加密,确保只有拥有对应私钥的接收方才能解密消息。
SM2算法的安全性主要依赖于椭圆曲线参数的选择以及密码学操作的正确性。国家密码管理局发布的GM/T 0003系列标准对SM2算法的参数和操作进行了详细规定,以确保算法的安全性和稳定性。
在实际应用中,SM2算法已经被广泛用于各种安全领域,如电子商务、电子政务、金融交易等。通过使用SM2算法,可以有效地保护用户的隐私和数据安全,提升整个信息系统的安全水平。
总的来说,国密SM2椭圆曲线公钥密码算法是中国在密码学领域的一项重要成果,它为中国的信息安全提供了有力的保障。随着技术的不断发展,SM2算法还将继续完善和优化,以应对日益复杂的网络安全挑战。