当前位置:   article > 正文

openssl-1.1.1e 在 linux 和 windows 的使用,并使用 c++ 和 c# 开发 dll 进行文件的签名于验证_openssl1.1.1f编译sm2签名与验签报错

openssl1.1.1f编译sm2签名与验签报错

参考链接

Command Line Elliptic Curve Operations
图解公钥与私钥,加密解密、签名验签流程,什么是 CA 证书

前言

   因工作需要,正在研究 openssl 的使用,在 bootloader 中需要对应用程序 bin 文件进行签名,防止 bin 文件被篡改或者传输错误,使用非对称加密的 ECC 算法,椭圆曲线是 secp256r1 ,在 openssl 中使用命令 ecparam -list_curves 无法查看到该曲线,查阅资料后发现在 openssl 中名称有更改 (prime256v1)。
   openssl(1.1.0.b) is not support secp256r1?(openssl ecparam - list_curves)
   prime192v1 and the secp256r1 curve was referred to as prime256v1.

一、在 linux 使用

   下载源码编译安装即可,比较简单。主要是为了研究一下生成私钥公钥、签名验签的过程,通过查看命令的文档找到使用的命令后,可以根据这些命令的源代码查看如何掉用 openssl 的库函数,最终在 widows 上使用。

需要指定下面这个环境变量:
export LD_LIBRARY_PATH=/home/xhr/Downloads/openssl-1.1.1e/opt/lib

常用命令:

1. 查看支持的曲线
openssl ecparam -list_curves

2. 生成 ecc 私钥
openssl ecparam -out privkey.pem -name prime256v1 -genkey
openssl ecparam -out privkey.pem -name prime256v1 -param_enc explicit -genkey

3. 查看私钥
openssl ecparam -in privkey.pem -text

4. 验证 EC 参数
openssl ecparam -in privkey.pem -check

5. 生成公钥
openssl ec -in privkey.pem -out pubkey.pem -pubout -text

6. 签名
openssl pkeyutl -sign -in test.txt -out test.sign -inkey privkey.pem

7. 验证
openssl pkeyutl -verify -in test.txt -sigfile test.sign -inkey pubkey.pem -pubin

8. 签发 ECC 证书 (没有验证)
openssl ecparam -out EccCA.key -name prime256v1 -genkey
openssl req -config openssl.cnf -key EccCA.key -new -out EccCA.req
openssl x509 -req -in EccCA.req -signkey EccCA.key -out EccCA.pem
openssl ecparam -out EccSite.key -name prime256v1 -genkey
openssl req -config openssl.cnf -key EccSite.key -new -out EccSite.req
openssl x509 -req -in EccSite.req -CA EccCA.pem -CAkey EccCA.key -out EccSite.pem -CAcreateserial
  • 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

二、在 windows 使用

1. 编译 openssl

   = =|| 1.1.1e 版本愣是没能在 windows 上正常编译使用,下面都用的是 1.0.1g 版本的 dll。

2. 使用 C++ 制作 dll

   这里用的是主要使用 libeay32.lib 和 libssl32.dll 两个文件。

  1. EccAlgorithmApi.h
#ifndef _ECC_API_H_
#define _ECC_API_H_

#ifdef _ECC_API_EXPORTS_
#define ECCAPI extern "C" __declspec(dllexport)
#else
#define ECCAPI extern "C" __declspec(dllimport)
#endif

typedef unsigned char uchar;

#define KEY_TYPE_PRIVATE (0x1234)
#define KEY_TYPE_PUBLIC  (0x4321)

#define KEY_FORMAT_DER   (0xABCD)
#define KEY_FORMAT_PEM   (0xDCBA)

/*
	生成 EC 私钥
	/privKey	生成的私钥
	/maxLen		私钥 buff 最大长度
	/curve_name 生成私钥所用的曲线名称
	/return		成功返回正数代表私钥长度,失败返回负数
*/
ECCAPI int GenEcPrivKey(uchar* privKey, int maxLen, const char* curve_name);

/*
	通过已有的私钥生成对应的公钥
	/privKey  私钥 HEX 格式数据
	/privLen  私钥长度
	/pubKey   生成的公钥
	/maxLen   公钥 buff 最大长度
	/return	  成功返回正数代表公钥长度,失败返回负数
*/
ECCAPI int GenEcPubKey(const uchar* privKey, int privLen, uchar* pubKey, int maxLen);

/*
	保存钥匙到文件
	/file		保存路径
	/key		密钥 HEX 码
	/keyLen		密钥长度
	/keyType	公钥、私钥
	/keyFormat	保存格式:DER、PEM
	/password	文件密码
	/return		成功返回 0
*/
ECCAPI int SavekEeyToFile(const char* file, const uchar* privKey, int privLen,
	int outKeyType, int outKeyFormat, const char* password);


/*
	从文件中读取密钥
	/infile		输入文件
	/keyType	公钥、私钥
	/keyFormat	文件格式
	/outKey		密钥 HEX (输出)
	/maxLen		输出密钥 buff 的最大长度
	/password	文件密码
	/return		负数表示错误编号,正数表示输出字节码的长度
*/
ECCAPI int LoadKeyFormFile(const char* infile, const int keyType,
	const int keyFormat, uchar* outKey, const int maxLen, void* password);


/*
	对buf使用key进行签名
	/sig		存放输出的签名
	/maxlen		签名的最大长度
	/buf		需要进行签名的内容
	/buflen		需要进行签名的内容长度
	/key		密钥 HEX
	/keyType	公钥、私钥
	/keylen		密钥长度
	/curve		如果提供的是公钥,必须提供曲线
	/return		负数表示错误编号,正数表示签名的长度
*/
ECCAPI int Sign(uchar* sig, const int maxlen, const uchar* buf, int buflen,
	const uchar* key, const int keyType, const int keylen, const char* curve);


/*
	验证签名是否正确
	/sig		签名
	/siglen		签名长度
	/buf		验签内容
	/buflen		验签内容长度
	/key		密钥 HEX
	/kenlen		密钥长度
	/keyType	公钥、私钥
	/curve		提供公钥时必须提供该参数,曲线名称
	/return		发生错误返回负数,验证失败返回 0,验证成功返回 1
*/
ECCAPI int Verify(
	const uchar* sig, const int siglen,
	const uchar* buf, const int buflen,
	const uchar* key, const int keylen,
	const int keyType, const char* curve);

#endif // !_ECC_API_H_
  • 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
  1. EccAlgorithm.cpp
#include "pch.h"
#include <EccAlgorithmApi.h>
#include <string>
#include <openssl\ec.h>
#include <openssl\ec.h>
#include <openssl\obj_mac.h>
#include <openssl\bio.h>
#include <openssl\pem.h>

#pragma comment(lib, "libeay32")

#define goto_fault(x) \
{\
	ret_val = -x;\
	goto FAULT_##x;\
}

/*
	通过名称获取曲线代号
*/
static int GetNIDbyCurveName(const char* curve_name)
{
	int nid;
	/* workaround for the SECG curve names secp192r1
	* and secp256r1 (which are the same as the curves
	* prime192v1 and prime256v1 defined in X9.62)
	*/
	if (!strcmp(curve_name, "secp192r1"))
	{
		printf("using curve name prime192v1 instead of secp192r1\n");
		nid = NID_X9_62_prime192v1;
	}
	else if (!strcmp(curve_name, "secp256r1"))
	{
		printf("using curve name prime256v1 instead of secp256r1\n");
		nid = NID_X9_62_prime256v1;
	}
	else
		nid = OBJ_sn2nid(curve_name);

	if (nid == 0)
	{
		printf("unknown curve name (%s)\n", curve_name);
	}
	return nid;
}

/*
	生成 EC 私钥
	/privKey	生成的私钥
	/maxLen		私钥 buff 最大长度
	/curve_name 生成私钥所用的曲线名称
	/return		成功返回正数代表私钥长度,失败返回负数
*/
ECCAPI int GenEcPrivKey(uchar* privKey, int maxLen, const char* curve_name)
{
	EC_KEY* ecKey;
	EC_GROUP* ecGroup;
	uchar* pp = privKey;
	int ret_val;

	if (NULL == (ecKey = EC_KEY_new()))
		goto_fault(1);

	if (NULL == (ecGroup = EC_GROUP_new_by_curve_name(GetNIDbyCurveName(curve_name))))
		goto_fault(2);

	if (EC_KEY_set_group(ecKey, ecGroup) != 1)
		goto_fault(3);

	if (!EC_KEY_generate_key(ecKey))
		goto_fault(3);

	ret_val = i2d_ECPrivateKey(ecKey, &pp);
	if (!ret_val || ret_val > maxLen)
		ret_val = -4;

FAULT_3:
	EC_GROUP_free(ecGroup);
FAULT_2:
	EC_KEY_free(ecKey);
FAULT_1:
	return ret_val;
}

/*
	通过已有的私钥生成对应的公钥
	/privKey  私钥 HEX 格式数据
	/privLen  私钥长度
	/pubKey   生成的公钥
	/maxLen   公钥 buff 最大长度
	/return   返回负数表示失败,正数表示公钥的长度
	/return	  成功返回正数代表公钥长度,失败返回负数
*/
ECCAPI int GenEcPubKey(const uchar* privKey, int privLen, uchar* pubKey, int maxLen)
{
	int ret_val;
	EC_KEY* eckey;
	uchar* pp = (uchar*)privKey;
	eckey = d2i_ECPrivateKey(NULL, (const uchar**)&pp, privLen);
	if (!eckey)
		goto_fault(1);

	pp = pubKey;
	ret_val = i2o_ECPublicKey(eckey, &pp);
	if (!ret_val)
		ret_val = -2;

	EC_KEY_free(eckey);
FAULT_1:
	return ret_val;
}

/*
	保存密钥
	/eckey		需要保存的密钥
	/file		文件路径
	/keyType	公钥、私钥
	/keyFormat	保存格式:DER、PEM
	/password	文件密码
	/return		成功返回 true
*/
static bool DoSavingKey(EC_KEY* eckey, const char* file,
	int keyType, int keyFormat, const char* password)
{
	(void)password;
	int ret_val = 0;
	BIO* out;

	if (file == NULL)
		goto_fault(1);

	out = BIO_new(BIO_s_file());
	if (!out)
		goto_fault(1);

	if (0 >= BIO_write_filename(out, (void*)file))
		goto_fault(2);

	if (keyType == KEY_TYPE_PRIVATE) {
		if (keyFormat == KEY_FORMAT_DER) {
			ret_val = i2d_ECPrivateKey_bio(out, eckey);
			if (!ret_val) goto_fault(2);
		}
		else if (keyFormat == KEY_FORMAT_PEM) {
			ret_val = PEM_write_bio_ECPrivateKey(out,eckey, NULL, NULL, 0, NULL, NULL);
			if(!ret_val) goto_fault(2);
		}
		else goto_fault(2);
	}
	else if (keyType == KEY_TYPE_PUBLIC) {
		if (keyFormat == KEY_FORMAT_DER) {
			ret_val = i2d_EC_PUBKEY_bio(out, eckey);
			if (!ret_val) goto_fault(2);
		}
		else if (keyFormat == KEY_FORMAT_PEM) {
			ret_val = PEM_write_bio_EC_PUBKEY(out, eckey);
			if (!ret_val) goto_fault(2);
		}
		else goto_fault(2);
	}
	else ret_val = -100;

FAULT_2:
	BIO_free_all(out);
FAULT_1:
	return ret_val < 0 ? false : true;
}

/*
	保存钥匙到文件
	/file		保存路径
	/key		密钥 HEX 码
	/keyLen		密钥长度
	/keyType	公钥、私钥
	/keyFormat	保存格式:DER、PEM
	/password	文件密码
	/return		成功返回 0
*/
ECCAPI int SavekEeyToFile(const char* file, const uchar* privKey, int privLen,
	int outKeyType, int outKeyFormat, const char* password)
{
	(void)password;
	int ret_val = 0;
	EC_KEY* eckey;
	const uchar* pp = privKey;
	/*switch (keyType) {
	case KEY_TYPE_PRIVATE:
		if(!(eckey = d2i_ECPrivateKey(NULL, &pp, keyLen)))
			goto_fault(1);
		break;
	case KEY_TYPE_PUBLIC:
		if (!(eckey = o2i_ECPublicKey(NULL, &pp, keyLen)))
			goto_fault(1);
		break;
	default: goto_fault(1);
	}*/

	if (!(eckey = d2i_ECPrivateKey(NULL, &pp, privLen)))
		goto_fault(1);

	if (!DoSavingKey(eckey, file, outKeyType, outKeyFormat, password))
		goto_fault(2);

FAULT_2:
	EC_KEY_free(eckey);
FAULT_1:
	return ret_val;
}

/*
	从文件中读取密钥
	/infile		输入文件
	/keyType	公钥、私钥
	/keyFormat	文件格式
	/outKey		密钥 HEX (输出)
	/maxLen		输出密钥 buff 的最大长度
	/password	文件密码
	/return		负数表示错误编号,正数表示输出字节码的长度
*/
ECCAPI int LoadKeyFormFile(const char* infile,const int keyType, 
	const int keyFormat, uchar* outKey, const int maxLen, void* password)
{
	int ret_val = 0;
	BIO* in;
	EC_KEY* eckey;
	uchar* pp = outKey;
	in = BIO_new(BIO_s_file());

	if (in == NULL)
		goto_fault(1);

	if (0 >= BIO_read_filename(in, infile))
		goto_fault(2);

	if (keyFormat == KEY_FORMAT_DER) {
		if (keyType == KEY_TYPE_PUBLIC) {
			eckey = d2i_EC_PUBKEY_bio(in, NULL);
		}
		else if (keyType == KEY_TYPE_PRIVATE) {
			eckey = d2i_ECPrivateKey_bio(in, NULL);
		}
		else goto_fault(2);
	}
	else if (keyFormat == KEY_FORMAT_PEM) {
		if (keyType == KEY_TYPE_PUBLIC) {
			eckey = PEM_read_bio_EC_PUBKEY(in, NULL, NULL, NULL);
		}
		else if (keyType == KEY_TYPE_PRIVATE) {
			eckey = PEM_read_bio_ECPrivateKey(in, NULL, NULL, password);
		}
		else goto_fault(2);
	}
	else goto_fault(2);

	if(eckey == NULL)
		goto_fault(3);

	if (keyType == KEY_TYPE_PRIVATE) {
		ret_val = i2d_ECPrivateKey(eckey, &pp);
	}
	else if (keyType == KEY_TYPE_PUBLIC) {
		ret_val = i2o_ECPublicKey(eckey, &pp);
	}
	else goto_fault(4);
	if (!ret_val || ret_val > maxLen)
		ret_val = -5;

FAULT_4:
	EC_KEY_free(eckey);
FAULT_3:
FAULT_2:
	BIO_free(in);
FAULT_1:
	return ret_val;
}

/*
	对buf使用key进行签名
	/sig		存放输出的签名
	/maxlen		签名的最大长度
	/buf		需要进行签名的内容
	/buflen		需要进行签名的内容长度
	/key		密钥 HEX
	/keyType	公钥、私钥
	/keylen		密钥长度
	/curve		如果提供的是公钥,必须提供曲线
	/return		负数表示错误编号,正数表示签名的长度
*/
ECCAPI int Sign(uchar* sig, const int maxlen, const uchar* buf, int buflen,
	const uchar* key, const int keyType, const int keylen, const char* curve)
{
	(void)curve;
	unsigned int siglen = maxlen;
	int ret_val = 0;
	EC_KEY* eckey;
	const unsigned char* pp = key;
	if (keyType == KEY_TYPE_PRIVATE) {
		if(NULL == (eckey = d2i_ECPrivateKey(NULL, &pp, keylen)))
			goto_fault(1);
	}
	else if (keyType == KEY_TYPE_PUBLIC) {
		goto_fault(1);
	}
	else {
		goto_fault(1);
	}
	
	if (ECDSA_sign(0, buf, buflen, sig, &siglen, eckey)) {
		ret_val = (int)siglen;
		if (siglen > maxlen || siglen <= 0)
			ret_val = -3;
	}
	else ret_val = -2; 

	EC_KEY_free(eckey);
FAULT_1:
	return ret_val;
}


/*
	获得合法的 ecKey 对象,返回非空需要调用方释放
	/key		密钥 HEX
	/kenlen		密钥长度
	/keyType	公钥、私钥
	/curve		提供公钥时必须提供该参数,曲线名称
	/return		没有合法的对象返回空,非空需要调用方释放
*/
static EC_KEY* GetValidEcKey(const uchar* key, const int keylen,
	const int keyType, const char* curve)
{
	EC_KEY* ret;
	EC_KEY* eckey = NULL;
	EC_GROUP* ecgroup = NULL;
	int ret_val = 0;
	const unsigned char* pp = key;
	if (keyType == KEY_TYPE_PRIVATE) {
		if (NULL == (eckey = d2i_ECPrivateKey(NULL, &pp, keylen)))
			goto_fault(1);
	}
	else if (keyType == KEY_TYPE_PUBLIC) {
		if(curve == NULL)
			goto_fault(1);

		if(!(eckey = EC_KEY_new()))
			goto_fault(1);

		if(!(ecgroup = EC_GROUP_new_by_curve_name(GetNIDbyCurveName(curve))))
			goto_fault(1);
		
		(void)EC_KEY_set_group(eckey, ecgroup); // 该函数会复制一份 group 对象,所以应该释放

		ret = o2i_ECPublicKey(&eckey, &pp, keylen);
		if(ret != eckey)
			goto_fault(1);
	}
	else goto_fault(1);

FAULT_1:
	if (ret_val)
		if (eckey) {
			EC_KEY_free(eckey);
			eckey = NULL;
		}
	if (ecgroup)
		EC_GROUP_free(ecgroup);
	return eckey;
}

/*
	验证签名是否正确
	/sig		签名
	/siglen		签名长度
	/buf		验签内容
	/buflen		验签内容长度
	/key		密钥 HEX
	/kenlen		密钥长度
	/keyType	公钥、私钥
	/curve		提供公钥时必须提供该参数,曲线名称
	/return		发生错误返回负数,验证失败返回 0,验证成功返回 1
*/
ECCAPI int Verify(
	const uchar* sig, const int siglen,
	const uchar* buf, const int buflen,
	const uchar* key, const int keylen,
	const int keyType, const char* curve)
{
	int ret_val = 0;
	EC_KEY* eckey = GetValidEcKey(key, keylen, keyType, curve);
	if (eckey == NULL)
		goto_fault(1);

	ret_val = ECDSA_verify(0, buf, buflen, sig, siglen, eckey);

	EC_KEY_free(eckey);
FAULT_1:
	return ret_val;
}
  • 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
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399

3. 使用 C# 再封装 C++ dll

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;

namespace EccAlgorithmCs
{
    /// <summary>
    /// 公钥、私钥
    /// </summary>
    public enum KeyType : int
    {
        Private = 0x1234,
        Public = 0x4321,
    }
    /// <summary>
    /// DER、PEM
    /// </summary>
    public enum KeyFormat : int
    {
        DER = 0xABCD, 
        PEM = 0xDCBA,
    }

    /// <summary>
    /// 公私钥
    /// </summary>
    public class EcKey
    {
        // ---------- internal class ------------------------------------

        /// <summary>
        /// 调用cpp库函数
        /// </summary>
        private static class CPP
        {
            /// <summary>
            /// 生成 EC 私钥
            /// </summary>
            /// <param name="privKey">生成的私钥</param>
            /// <param name="maxLen">私钥 buff 最大长度</param>
            /// <param name="curve_name">生成私钥所用的曲线名称</param>
            /// <returns>成功返回正数代表私钥长度,失败返回负数</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int GenEcPrivKey(byte[] privKey, int maxLen, byte[] curve_name);

            /// <summary>
            /// 通过已有的私钥生成对应的公钥
            /// </summary>
            /// <param name="privKey">私钥 HEX 格式数据</param>
            /// <param name="privLen">私钥长度</param>
            /// <param name="pubKey">生成的公钥</param>
            /// <param name="maxLen">公钥 buff 最大长度</param>
            /// <returns>成功返回正数代表公钥长度,失败返回负数</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int GenEcPubKey(byte[] privKey, int privLen, byte[] pubKey, int maxLen);

            /// <summary>
            /// 保存钥匙到文件
            /// </summary>
            /// <param name="file">保存路径</param>
            /// <param name="key">密钥 HEX 码</param>
            /// <param name="keyLen">密钥长度</param>
            /// <param name="keyType">公钥、私钥</param>
            /// <param name="keyFormat">保存格式:DER、PEM</param>
            /// <param name="password">文件密码</param>
            /// <returns>成功返回 0</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int SavekEeyToFile(byte[] file, byte[] privKey, int privLen,
                int outKeyType, int outKeyFormat, byte[] password);

            /// <summary>
            /// 从文件中读取密钥
            /// </summary>
            /// <param name="infile">输入文件</param>
            /// <param name="keyType">公钥、私钥</param>
            /// <param name="keyFormat">文件格式</param>
            /// <param name="outKey">密钥 HEX (输出)</param>
            /// <param name="maxLen">输出密钥 buff 的最大长度</param>
            /// <param name="password">文件密码</param>
            /// <returns>负数表示错误编号,正数表示输出字节码的长度</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int LoadKeyFormFile(byte[] infile, int keyType,
                int keyFormat, byte[] outKey, int maxLen, byte[] password);

            /// <summary>
            /// 对buf使用key进行签名
            /// </summary>
            /// <param name="sig">存放输出的签名</param>
            /// <param name="maxlen">签名的最大长度</param>
            /// <param name="buf">需要进行签名的内容</param>
            /// <param name="buflen">需要进行签名的内容长度</param>
            /// <param name="key">密钥 HEX</param>
            /// <param name="keyType">公钥、私钥</param>
            /// <param name="keylen">密钥长度</param>
            /// <param name="curve">如果提供的是公钥,必须提供曲线</param>
            /// <returns>负数表示错误编号,正数表示签名的长度</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int Sign(byte[] sig, int maxlen, byte[] buf, int buflen,
                byte[] key, int keyType, int keylen, byte[] curve);

            /// <summary>
            /// 验证签名是否正确,发生错误返回负数,验证失败返回 0,验证成功返回 1
            /// </summary>
            /// <param name="sig">签名</param>
            /// <param name="siglen">签名长度</param>
            /// <param name="buf">验签内容</param>
            /// <param name="buflen">验签内容长度</param>
            /// <param name="key">密钥 HEX</param>
            /// <param name="keylen">密钥长度</param>
            /// <param name="keyType">公钥、私钥</param>
            /// <param name="curve">提供公钥时必须提供该参数,曲线名称</param>
            /// <returns>发生错误返回负数,验证失败返回 0,验证成功返回 1</returns>
            [DllImport("EccAlgorithmCpp.dll", CallingConvention = CallingConvention.Cdecl)]
            public extern static int Verify(byte[] sig, int siglen, byte[] buf, int buflen,
                 byte[] key, int keylen, int keyType, byte[] curve);
        }
        /// <summary>
        /// 密钥信息
        /// </summary>
        private class KeyInfo
        {
            public KeyInfo(byte[] hex, int len)
            {
                Hex = new byte[len];
                Len = len;
                for (int i = 0; i < len; i++)
                {
                    Hex[i] = hex[i];
                }
            }

            public byte[] Hex { get; private set; }
            public int Len { get; private set; }
        }

        // ---------- field ----------------------------------------------
        /// <summary>
        /// 私钥
        /// </summary>
        private KeyInfo PrivKey;
        /// <summary>
        /// 公钥
        /// </summary>
        private KeyInfo PubKey;

        // ---------- public attrib ---------------------------------------
        /// <summary>
        /// 最后一次发生错误的原因
        /// </summary>
        public string TheLastErrorInfo { get; private set; }


        // ---------- public method ---------------------------------------
        /// <summary>
        /// 通过文件导入钥匙
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="type">公钥、私钥</param>
        /// <param name="format">文件格式</param>
        /// <returns>成功返回true</returns>
        public bool ImportEcKey(string file, KeyType type, KeyFormat format)
        { return ImportEcKey(file, type, format, null); }

        /// <summary>
        /// 通过文件导入钥匙,需要输入文件密码
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="type">公钥、私钥</param>
        /// <param name="format">文件格式</param>
        /// <param name="password">文件密码</param>
        /// <returns>成功返回true</returns>
        private bool ImportEcKey(string file, KeyType type, KeyFormat format, string password)
        {
            const int maxLen = 1024;
            byte[] infile = Encoding.Default.GetBytes(file);
            byte[] key = new byte[maxLen];
            int len = CPP.LoadKeyFormFile(infile, (int)type, (int)format, key, maxLen, null);
            if(len <= 0)
            {
                TheLastErrorInfo = "LoadKeyFormFile fault code = " + len;
                if(type == KeyType.Public && format == KeyFormat.PEM)
                {
                    TheLastErrorInfo += "  不支持导入 PEM 格式的公钥";
                }
                goto FAULT;
            }

            switch (type)
            {
                case KeyType.Private:
                    PrivKey = new KeyInfo(key, len);
                    len = CPP.GenEcPubKey(PrivKey.Hex, PrivKey.Len, key, maxLen);
                    if (len <= 0)
                    {
                        TheLastErrorInfo = "Auto GenEcPubKey fault code = " + len;
                        goto FAULT;
                    }
                    PubKey = new KeyInfo(key, len);
                    break;
                case KeyType.Public:
                    PubKey = new KeyInfo(key, len);
                    break;
                default:
                    TheLastErrorInfo = "No Such KeyType!";
                    goto FAULT;
            }

            return true;
        FAULT:
            PrivKey = null;
            PubKey = null;
            return false;
        }

        /// <summary>
        /// 通过 HEX 导入密钥
        /// </summary>
        /// <param name="keyHex">密钥</param>
        /// <param name="type">公钥、私钥</param>
        /// <returns>成功返回true</returns>
        private bool ImportEcKey(byte[] keyHex, KeyType type)
        { throw new NotImplementedException(); }

        /// <summary>
        /// 导出密钥
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="type">公钥、私钥</param>
        /// <param name="format">文件格式</param>
        /// <returns>成功返回true</returns>
        private bool ExportEcKey(string file, KeyType type, KeyFormat format)
        { return ExportEcKey(file, type, format, null); }
        /// <summary>
        /// 导出密钥
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="type">公钥、私钥</param>
        /// <param name="format">文件格式</param>
        /// <param name="password">文件密码</param>
        /// <returns>成功返回true</returns>
        private bool ExportEcKey(string file, KeyType type, KeyFormat format, string password)
        { 
            byte[] xfile = Encoding.Default.GetBytes(file);
            KeyInfo key = PrivKey;
            //switch (type)
            //{
            //    case KeyType.Private:
            //        key = PrivKey;
            //        break;
            //    case KeyType.Public:
            //        key = PubKey;
            //        break;
            //}
            if (key == null)
            {
                TheLastErrorInfo = "不存在私钥,无法导出";
                return false;
            }

            int ret = CPP.SavekEeyToFile(xfile, key.Hex, key.Len, 
                (int)type, (int)format, null);

            return ret < 0 ? false : true;
        }

        /// <summary>
        /// 导出私钥
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="format">保存格式</param>
        /// <returns>成功返回true</returns>
        public bool ExportPrivKey(string file, KeyFormat format)
        {
            return ExportEcKey(file, KeyType.Private, format);
        }

        /// <summary>
        /// 导出公钥
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <param name="format">保存格式</param>
        /// <returns>成功返回true</returns>
        public bool ExportPubKey(string file, KeyFormat format)
        {
            return ExportEcKey(file, KeyType.Public, format);
        }
        
        /// <summary>
        /// 通过曲线名称生成密钥
        /// </summary>
        /// <param name="curveName">曲线名称</param>
        /// <returns>成功返回true</returns>
        public bool GenerateEcKey(string curveName)
        {
            const int maxLen = 1024;
            byte[] key = new byte[maxLen];
            byte[] curve = Encoding.Default.GetBytes(curveName.ToLower());
            int len;

            len = CPP.GenEcPrivKey(key, maxLen, curve);
            if (len < 0)
                goto FAULT;
            PrivKey = new KeyInfo(key, len);

            len = CPP.GenEcPubKey(PrivKey.Hex, PrivKey.Len, key, maxLen);
            if (len < 0)
                goto FAULT;
            PubKey = new KeyInfo(key, len);

            return true;
            FAULT:
            PrivKey = null;
            PubKey = null;
            return false;
        }

        /// <summary>
        /// 对文件进行签名
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <returns>成功返回签名,失败返回null</returns>
        public byte[] Sign(string file)
        {
            return Sign(GetFileBytes(file));
        }

        /// <summary>
        /// 对字节码进行签名
        /// </summary>
        /// <param name="article">需要签名的内容</param>
        /// <returns>签名,失败返回 null</returns>
        public byte[] Sign(byte[] article)
        {
            if (article == null)
            {
                TheLastErrorInfo = "CS.Sign 中 article 参数不能为空";
                return null;
            }

            const int maxlen = 1024;
            byte[] tmp = new byte[maxlen];
            int len = CPP.Sign(tmp, maxlen, article, article.Length,
                PrivKey.Hex, (int)KeyType.Private, PrivKey.Len, null);
            if(len < 0 || len > maxlen)
            {
                TheLastErrorInfo = "CPP.Sign fault code = " + len;
                return null;
            }
            byte[] sig = new byte[len];
            for (int i = 0; i < len; i++)
            {
                sig[i] = tmp[i];
            }
            return sig;
        }

        /// <summary>
        /// 验证签名是否被篡改
        /// </summary>
        /// <param name="article">需要验证的内容</param>
        /// <param name="signature">签名</param>
        /// <param name="curve">曲线名称</param>
        /// <returns>验证成功返回true</returns>
        public bool Verify(byte[] article, byte[] signature, string curve)
        {
            if (article == null || signature == null)
            {
                TheLastErrorInfo = "CS.Verify 中参数不能为 null";
                return false;
            }

            int ret = CPP.Verify(signature, signature.Length, article, article.Length,
                PubKey.Hex, PubKey.Len, (int)KeyType.Public, Encoding.Default.GetBytes(curve.ToLower()));

            if (ret < 0)
            {
                TheLastErrorInfo = "验证过程发生错误 CPP.Verify fault code = " + ret;
                return false;
            }
            else if (ret == 0)
            {
                TheLastErrorInfo = "验证失败";
                return false;
            }
            else return true;
        }


        /// <summary>
        /// 获取文件的二进制内容
        /// </summary>
        /// <param name="file">需要读取的文件</param>
        /// <returns>失败返回空</returns>
        public byte[] GetFileBytes(string file)
        {
            const int limit = int.MaxValue;
            byte[] buf = null;
            try
            {
                FileStream fs = new FileStream(file, FileMode.Open);
                int len;
                if (fs.Length > limit)
                    len = limit;
                else len = (int)fs.Length;
                buf = new byte[len];
                fs.Read(buf, 0, len);
                fs.Close();
            }
            catch (Exception ex)
            {
                TheLastErrorInfo = ex.Message;
                return null;
            }
            return buf;
        }
    }
}
  • 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
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420

4. 使用 C# 进行签名、验签

EcKey ec = new EcKey();
byte[] digest = ec.GetFileBytes("digest.txt");
ec.GenerateEcKey("secp256r1");
ec.ExportPrivKey(@"\privKey.der", KeyFormat.DER);
ec.ImportEcKey(@"\privKey.der", KeyType.Private, KeyFormat.DER);
byte[] sig = ec.Sign(digest);
ec.Verify(digest, sig, "secp256r1");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/388353
推荐阅读
相关标签
  

闽ICP备14008679号