openssl-1.1.1e 在 linux 和 windows 的使用,并使用 c++ 和 c# 开发 dll 进行文件的签名于验证

参考链接

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

二、在 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. 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;
}

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;
        }
    }
}

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");
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值