文章目录
参考链接
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 两个文件。
- 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_
- 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");