1、Openssl库安装及交叉编译
下载openssl库,https://www.openssl.org/source/
将库文件解压到本地文件后进行配置:
a、config配置:
进入解压后的目录,执行 ./config shared --prefix=/usr/local/openssl --openssldir=/usr/local/
其中
shared 为生成动态连接库,后续程序接口调用需要的引用这些动态库。
--prefix=/usr/local/openssl 为配置openssl安装的目录,一般都安装装usr/local下
openssldir 为配置证书文件目录
执行后只是完成了配置项,/usr/local目录下还没有想要的库文件夹
b. make 进行编译工作
c、make install 完成安装过程 这之后在之前的安装目录下看到openssl文件和ssl
2、ECDSA签名实现
ECDSA算法用于数字签名,是ECC与DSA的结合,具有很好的公开密钥算法特性,通过公钥无法逆向获得私钥。
一般通过配对的私钥和公钥,发送方将信息根据安全散列算法SHA(SHA-1、SHA-224、SHA-256)等,获得摘要,然后利用私钥private Key对摘要进行签名。
a、生成签名过程:
- 产生一个随机数k 。
- 利用随机数k,计算出两个大数r和s。将r和s拼在一起就构成了对消息摘要的签名。
在openssl库里,只需调用相应的接口
ECDSA_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, unsigned int *siglen, EC_KEY *eckey);
--dgst 为数据摘要 可以通过
EVP_MD_CTX_init(&md_ctx); EVP_DigestInit(&md_ctx,EVP_sha256()); EVP_DigestUpdate(&md_ctx, (const void*)szBufferData,nBufferData); EVP_DigestFinal(&md_ctx, digest, &dgst_len);
生成,中间EVP_sha256为摘要的hash算法。
--dgstlen 为摘要数据的长度
--sig是签名缓存buff地址
--siglen得到的长度
--eckey是私钥
由于随即数的存在,执行签名过程每次都会得到不同的结果。
b、签名验证 :
1. 消息方将签名内容,分离出r、s ,即sig(ECDSA_SIG结构体的内容为BIGNUM *r、*s)
2. 将获得的消息源内容,算出摘要dgst
3. 获取公钥。
导入到签名验证函数中进行验证
ECDSA_verify(int type, const unsigned char *dgst, int dgstlen, const unsigned char *sig, int siglen, EC_KEY *eckey);
若验证ok,返回1,否则返回为 0
3、关于证书
利用openssl 进行数据签名,会涉及到公钥私钥的内容。
一般ECU和CSP端通信 ,都会经由相关的认证机构颁发获取到证书,然后通过证书生成相关的公钥和私钥。
当然自己也可以通过openssl库自己生成配对秘钥进行测试。
生成秘钥对:
openssl genrsa -out key.pem 2048 openssl pkcs8 -topk8 -inform PEM -outform PEM -in key.pem -out private_key.pem -nocrypt
得到公钥:
openssl rsa -in key.pem -pubout -outform PEM-out public_key.pem
查看打印:
openssl ec -in private_key.pem -text openssl ec -in public_key.pem -pubin –text
另证书获取需要说明,pem是base64编码的文件,其内部证书格式有RSA、DSA、ECDSA三种格式。所以其对应的加载方式也不同。
不过都可以用对应的接口函数获取证书内容,然后提取其pkey。
/*publickey_path 为公钥的路径*/ pbio_key_file = BIO_new_file(publickey_path, "r");
/*将消息存储在usrCert结构体中 获得证书相关的信息*/ usrCert = PEM_read_bio_X509(pbio_key_file, NULL, NULL, NULL);
然后根据
pubKey = X509_get_pubkey(usrCert);
获得pubKey 其类型为EVR_PKEY。
查询相关的结构体可知
struct evp_pkey_st { int type; int save_type; int references; const EVP_PKEY_ASN1_METHOD *ameth; ENGINE *engine; union { char *ptr; #ifndef OPENSSL_NO_RSA struct rsa_st *rsa; /* RSA */ #endif #ifndef OPENSSL_NO_DSA struct dsa_st *dsa; /* DSA */ #endif #ifndef OPENSSL_NO_DH struct dh_st *dh; /* DH */ #endif #ifndef OPENSSL_NO_EC struct ec_key_st *ec; /* ECC */ #endif } pkey; int save_parameters; STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ } /* EV
EV->type表示key的类型。所以可以直接通过接口转换得
EVP_PKEY * pubKey = X509_get_pubkey(usrCert); if (pubKey->type == EVP_PKEY_EC) { printf("pubkey is ec\n"); ec_key = EVP_PKEY_get1_EC_KEY(pubKey); if (!ec_key) { printf("get key fail \n"); return 0; } }
附上签名、验证代码:
int ECDSAManager::SmsSignature(const char * privatekey_path, const char *message, int dlen,\ unsigned char *sig_buf,unsigned int * sig_len) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len = 0; EVP_MD_CTX md_ctx; EC_KEY *ec_key = NULL; BIO *pbio_key_file = NULL; /*get key from pem file*/ pbio_key_file = BIO_new_file(privatekey_path, "rb"); ec_key = PEM_read_bio_ECPrivateKey(pbio_key_file, NULL, NULL, NULL); if (!ec_key) { printf("get key fail \n"); return 0; } EVP_MD_CTX_init(&md_ctx); if (!EVP_DigestInit(&md_ctx, EVP_sha256())) { printf("EVP_digest fail \n"); return 0; } if (!EVP_DigestUpdate(&md_ctx, (const void *)message, dlen)) { printf("EVP_DigestUpdate fail \n"); return 0; } if (!EVP_DigestFinal(&md_ctx, digest, &digest_len)) { printf("EVP_DigestFinal fail \n"); return 0; } /*do sign*/ if (!ECDSA_sign(0, digest, digest_len, sig_buf, sig_len, ec_key)) { printf("ECDSA_sign fail \n"); return 0; } if (pbio_key_file) { BIO_free(pbio_key_file); pbio_key_file = NULL; } if (ec_key) { EC_KEY_free(ec_key); ec_key = NULL; } return 1; }
ECDSA Verify :
int ECDSAManager::VerifySignature(const char * publickey_path, const char *message, int dlen, \ unsigned char *sig_buf, int sig_len) { BIO * pbio_key_file; int ret; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len = 0; EVP_MD_CTX md_ctx; unsigned char Cert[4099]; unsigned long Certlen; unsigned char *pTmp = NULL; X509 *usrCert = NULL; //the struct of X509 certificate FILE *fp; EC_KEY * ec_key; fp = fopen(publickey_path, "rb"); if (fp == NULL) { printf("read cert file fail \n"); return 0; } Certlen = fread(Cert, 1, 4096, fp); fclose(fp); pTmp = Cert; //Convert to x509 data usrCert = d2i_X509(NULL, (const unsigned char **)&pTmp, Certlen); if (usrCert == NULL) { /*Determine whether or not is a PEM certificate*/ pbio_key_file = BIO_new_file(publickey_path, "r"); usrCert = PEM_read_bio_X509(pbio_key_file, NULL, NULL, NULL); if (usrCert == NULL) { printf("format conver error\n"); return 0; } } EVP_PKEY * pubKey = X509_get_pubkey(usrCert); if (pubKey->type == EVP_PKEY_EC) { printf("pubkey is ec\n"); ec_key = EVP_PKEY_get1_EC_KEY(pubKey); if (!ec_key) { printf("get key fail \n"); return 0; } } /* //printf the key data printf("EC_key is: \n"); derpubkeyLen = i2d_EC_PUBKEY(ec_key, &pTmp); for (int i = 0; i < derpubkeyLen; i++) { printf("%02x", derpubkey[i]); } */ EVP_MD_CTX_init(&md_ctx); if (!EVP_DigestInit(&md_ctx, EVP_sha256())) { printf("EVP_digest fail \n"); return 0; } if (!EVP_DigestUpdate(&md_ctx, (const void *)message, dlen)) { printf("EVP_DigestUpdate fail \n"); return 0; } if (!EVP_DigestFinal(&md_ctx, digest, &digest_len)) { printf("EVP_DigestFinal fail \n"); return 0; } /*do verify*/ ret = ECDSA_verify(0, (const unsigned char*)digest, digest_len, (const unsigned char *)sig_buf, sig_len, ec_key); if (pbio_key_file) { BIO_free(pbio_key_file); pbio_key_file = NULL; } if (ec_key) { EC_KEY_free(ec_key); ec_key = NULL; } return ret; }
*补充 --以上得到的签名数据都是经过ASN1编码后的数据,关于签名后的数据可以参考链接:http://m.blog.csdn.net/iDivines/article/details/51911484