National secret signature and verification process analysis

The signature is essentially: AAA = base64_encode (private key encryption (SHA1 (message body msg)))

The essence of the verification is: BBB = the other party’s public key decryption (base64_decode(AAA)) and CCC = SHA1 (message body MSG)

                            if (BBB == CCC) {

                                        Sign verification succeeded

                            }else{

                                      Sign verification failed

                            }

The SHA1 algorithm can be replaced with MD5, SHA256, SHA512 as needed, and base64 is not necessary. The main purpose here is to convert the binary to a string, which is convenient for the HTTP protocol to be transmitted to the server for verification. The data can be decrypted with the other party's public key, which proves that the data is indeed encrypted with the kid's private key, and it can be confirmed whether the person sending the data is Ah Mao or Ah Gou. If it is network data transmission to prevent peeping, it is to encrypt the data with the other party's public key, and the person who receives the data uses his own private key to decrypt it, which is different from signature verification.

The national secret signature and verification functions are as follows:

1、ECDSA_sign 和 ECDSA_verify

2、ECDSA_do_sign 和 ECDSA_do_verify

3、sm2_do_sign 和 sm2_do_verify


/** Computes ECDSA signature of a given hash value using the supplied private key (note: sig must point to ECDSA_size(eckey) bytes of memory).
 *  \param  type     此参数可以被忽略         this parameter is ignored
 *  \param  dgst     指向要签名的HASH值       pointer to the hash value to sign
 *  \param  dgstlen  HASH值的长度             length of the hash value
 *  \param  sig      创建签名的DER加密内存块  memory for the DER encoded created signature
 *  \param  siglen   指向签名的长度           pointer to the length of the returned signature
 *  \param  eckey    包含私钥的EC_KEY对象     EC_KEY object containing a private EC key
 *  \                返回1代表成功            return 1 on success and 0 otherwise
 */
int ECDSA_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, unsigned int *siglen, EC_KEY *eckey);


/** Verifies that the given signature is valid ECDSA signature of the supplied hash value using the specified public key.
 *  \param  type     此参数可以被忽略         this parameter is ignored
 *  \param  dgst     指向HASH值的指针         pointer to the hash value
 *  \param  dgstlen  HASH值的长度             length of the hash value
 *  \param  sig      指向DER编码后的签名                 pointer to the DER encoded signature
 *  \param  siglen   编码的签名长度           length of the DER encoded signature
 *  \param  eckey    包含公钥的EC_KEY对象     EC_KEY object containing a public EC key
 *  \                返回1代表签名有效        return 1 if the signature is valid, 0 if the signature is invalid and -1 on error
 */
int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen, const unsigned char *sig, int siglen, EC_KEY *eckey);

 

/** 使用私钥计算给定的HASH值的ECDSA签名结构体    Computes the ECDSA signature of the given hash value using the supplied private key and returns the created signature.
 *  \param  dgst      指向HASH值的指针       pointer to the hash value
 *  \param  dgst_len  HASH值的长度           length of the hash value
 *  \param  eckey     包含私钥的EC_KEY对象   EC_KEY object containing a private EC key
 *  \                 返回ECDSA_SIG结构体的指针,返回NULL代表出错  return pointer to a ECDSA_SIG structure or NULL if an error occurred
 */
ECDSA_SIG *ECDSA_do_sign(const unsigned char *dgst, int dgst_len,  EC_KEY *eckey);


/** Verifies that the supplied signature is a valid ECDSA  signature of the supplied hash value using the supplied public key.
 *  \param  dgst      指向HASH值的指针         pointer to the hash value
 *  \param  dgst_len  HASH值的长度             length of the hash value
 *  \param  sig       ECDSA_SIG 结构体指针     structure
 *  \param  eckey     包含公钥的EC_KEY结构体   EC_KEY object containing a public EC key
 *  \                 签名成功返回1,失败返回-1   return 1 if the signature is valid, 0 if the signature is invalid and -1 on error
 */
int ECDSA_do_verify(const unsigned char *dgst, int dgst_len,  const ECDSA_SIG *sig, EC_KEY *eckey);
/*
 * SM2 签名操作.  计算Z值和签名H(Z)    Computes Z and then signs H(Z || msg) using SM2
 */
                             私钥         摘要算法          ID值                   ID的长度       签名的消息        消息长度
ECDSA_SIG *sm2_do_sign(const EC_KEY *key, const EVP_MD *digest, const uint8_t *id, const size_t id_len, const uint8_t *msg, size_t msg_len);

	
                                            摘要算法             签名函数返回的结构体       ID值              ID的长度             签名的消息         消息长度
int sm2_do_verify(const EC_KEY *key, const EVP_MD *digest, const ECDSA_SIG *signature, const uint8_t *id, const size_t id_len, const uint8_t *msg, size_t msg_len);

The following is a demo example of the test. I compiled the static library of the country secret. You need to connect libssl_static.lib libcrypto_static.lib ws2_32.lib, and the certificate can be generated by gmssl.exe


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/asn1t.h>
#include <openssl/x509.h>

#include <openssl/rsa.h>
#include <openssl/dsa.h>

#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/ui.h>
#include <openssl/safestack.h>

#include <openssl/bn.h>
#include <openssl/ssl.h>
#include "main.h"


//#pragma comment(lib,"legacy_stdio_definitions.lib")


 
//#define stdin  (__acrt_iob_func(0))
//#define stdout (__acrt_iob_func(1))
//#define stderr (__acrt_iob_func(2))

//#define stdin  (&__iob_func()[0])
//#define stdout (&__iob_func()[1])
//#define stderr (&__iob_func()[2])



//参数为私钥文件名,返回私钥
EVP_PKEY* load_privateKey(const char* file) {
    BIO* in;
    EVP_PKEY* key;

    in = BIO_new_file(file, "r");
    if (!in)    return NULL;
    key = PEM_read_bio_PrivateKey(in, NULL, 0, NULL);
    BIO_free(in);
    return key;
}

//参数为公钥文件名,返回公钥
EVP_PKEY* load_publicKey(const char* file) {
    BIO* in;
    EVP_PKEY* key;

    in = BIO_new_file(file, "r");
    if (!in)    return NULL;
    key = PEM_read_bio_PUBKEY(in, NULL, 0, NULL);
    BIO_free(in);
    return key;
}

//参数为证书文件名,返回证书中的公钥
EVP_PKEY* load_cert(const char* file)
{
	STACK_OF(X509_INFO)* allcerts = NULL;
	BIO*  certs = NULL;
	EVP_PKEY* pkey = NULL;
	int i;

	certs = BIO_new_file(file, "r");//bio_open_default(file, 'r', FORMAT_PEM);
	if (certs == NULL)  return NULL;

	allcerts = PEM_X509_INFO_read_bio(certs, NULL, NULL, NULL);
	for (i=0; i<sk_X509_INFO_num(allcerts); i++) {
		X509_INFO* xinfo = sk_X509_INFO_value(allcerts, i);
		if (xinfo->x509 != NULL) {
			X509* xx = xinfo->x509;
			pkey = X509_get_pubkey(xx);
			break;
		}
	}


	sk_X509_INFO_pop_free(allcerts, X509_INFO_free);

	BIO_free(certs);
	return pkey;
}


#define  HASHED_DATA(p)   (((unsigned char*)p)+15)

//计算 SHA-1 的签名HASH值
unsigned char* HashForSign(unsigned char *dst, unsigned int dst_size, unsigned char *src, unsigned int src_size)
{
	unsigned char *buf = (unsigned char *)dst;
	unsigned char sign_data[] =
	{
		0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E,
		0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,
	};
	//校验参数
	if (dst == NULL || src == NULL || src_size == 0 || dst_size < 35)
	{
		return NULL;
	}

	// 
	memcpy(buf, sign_data, sizeof(sign_data));

	// Hash
	SHA1(src, src_size, HASHED_DATA(buf));

	return buf;
}

int main(int argc, char** argv)
{
	//msg为原始消息, 这里随便写
    unsigned char* msg = (unsigned char*)"aaaaaaaaaaaaaaaaaaaaaaa";
	unsigned char sha[64] = {0};

	//用sha1算法计算msg的HASH值,
    unsigned char* sha1 = HashForSign(sha, sizeof(sha), msg, strlen((const char*)msg));
    int res1, res2;
	unsigned char sig[512] = {0};
    unsigned int siglen = sizeof(sig);

	///加载签名证书的私钥文件
    EVP_PKEY* evpPrvKey = load_privateKey("./ClientSign.key");
    EC_KEY* ecPrvKey = EVP_PKEY_get1_EC_KEY(evpPrvKey);

	//加载签名证书,从证书中获取公钥,也可以直接加载公钥文件
    EVP_PKEY* evpPubKey = load_cert("./ClientSign.crt");
    EC_KEY* ecPubKey = EVP_PKEY_get1_EC_KEY(evpPubKey);

	//进行签名,返回1代表成功
    res1 = ECDSA_sign(0, sha1, sizeof(sha), sig, &siglen, ecPrvKey);

    //如果要让服务器验签则把sig用base64加密后传给服务器即可。

	//进行验签,返回1代表成功
    res2 = ECDSA_verify(0, sha1, sizeof(sha), sig, siglen, ecPubKey);

    printf("-------res1=%d, res2=%d--------\n", res1, res2);

    return 0;
}

Source code download address: https://download.csdn.net/download/langeldep/14803127

 

Guess you like

Origin blog.csdn.net/langeldep/article/details/112833031