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