1. 基本原理
參考:
http://www.cnblogs.com/phpinfo/archive/2013/08/09/3246376.html
http://blog.csdn.net/oldmtn/article/details/52208747
公司一個項目要進行交易數據傳輸,因爲這個項目銀行那邊也是剛剛開始啓動,所有的支持只有一個傳輸字段的說明文檔,好吧,總的有人做事不是嘛,於是接口開發正式展開,第一步的難點就是加密解密,我選擇使用OpenSSL.
OpenSSL初接觸的人恐怕最難的在於先理解各種概念
公鑰/私鑰/簽名/驗證簽名/加密/解密/非對稱加密
我們一般的加密是用一個密碼加密文件,然後解密也用同樣的密碼.這很好理解,這個是對稱加密.而有些加密時,加密用的一個密碼,而解密用另外一組密碼,這個叫非對稱加密,意思就是加密解密的密碼不一樣.初次接觸的人恐怕無論如何都理解不了.其實這是數學上的一個素數積求因子的原理的應用,如果你一定要搞懂,百度有大把大把的資料可以看,其結果就是用這一組密鑰中的一個來加密數據,可以用另一個解開.是的沒錯,公鑰和私鑰都可以用來加密數據,相反用另一個解開,公鑰加密數據,然後私鑰解密的情況被稱爲加密解密,私鑰加密數據,公鑰解密一般被稱爲簽名和驗證簽名.
因爲公鑰加密的數據只有它相對應的私鑰可以解開,所以你可以把公鑰給人和人,讓他加密他想要傳送給你的數據,這個數據只有到了有私鑰的你這裏,纔可以解開成有用的數據,其他人就是得到了,也看懂內容.同理,如果你用你的私鑰對數據進行簽名,那這個數據就只有配對的公鑰可以解開,有這個私鑰的只有你,所以如果配對的公鑰解開了數據,就說明這數據是你發的,相反,則不是.這個被稱爲簽名.
實際應用中,一般都是和對方交換公鑰,然後你要發給對方的數據,用他的公鑰加密,他得到後用他的私鑰解密,他要發給你的數據,用你的公鑰加密,你得到後用你的私鑰解密,這樣最大程度保證了安全性.
RSA/DSA/SHA/MD5
非對稱加密的算法有很多,比較著名的有RSA/DSA ,不同的是RSA可以用於加/解密,也可以用於簽名驗籤,DSA則只能用於簽名.至於SHA則是一種和md5相同的算法,它不是用於加密解密或者簽名的,它被稱爲摘要算法.就是通過一種算法,依據數據內容生成一種固定長度的摘要,這串摘要值與原數據存在對應關係,就是原數據會生成這個摘要,但是,這個摘要是不能還原成原數據的,嗯....,正常情況下是這樣的,這個算法起的作用就是,如果你把原數據修改一點點,那麼生成的摘要都會不同,傳輸過程中把原數據給你再給你一個摘要,你把得到的原數據同樣做一次摘要算法,與給你的摘要相比較就可以知道這個數據有沒有在傳輸過程中被修改了.
實際應用過程中,因爲需要加密的數據可能會很大,進行加密費時費力,所以一般都會把原數據先進行摘要,然後對這個摘要值進行加密,將原數據的明文和加密後的摘要值一起傳給你.這樣你解開加密後的摘要值,再和你得到的數據進行的摘要值對應一下就可以知道數據有沒有被修改了,而且,因爲私鑰只有你有,只有你能解密摘要值,所以別人就算把原數據做了修改,然後生成一個假的摘要給你也是不行的,你這邊用密鑰也根本解不開.
CA/PEM/DER/X509/PKCS
一般的公鑰不會用明文傳輸給別人的,正常情況下都會生成一個文件,這個文件就是公鑰文件,然後這個文件可以交給其他人用於加密,但是傳輸過程中如果有人惡意破壞,將你的公鑰換成了他的公鑰,然後得到公鑰的一方加密數據,不是他就可以用他自己的密鑰解密看到數據了嗎,爲了解決這個問題,需要一個公證方來做這個事,任何人都可以找它來確認公鑰是誰發的.這就是CA,CA確認公鑰的原理也很簡單,它將它自己的公鑰發佈給所有人,然後一個想要發佈自己公鑰的人可以將自己的公鑰和一些身份信息發給CA,CA用自己的密鑰進行加密,這裏也可以稱爲簽名.然後這個包含了你的公鑰和你的信息的文件就可以稱爲證書文件了.這樣一來所有得到一些公鑰文件的人,通過CA的公鑰解密了文件,如果正常解密那麼機密后里面的信息一定是真的,因爲加密方只可能是CA,其他人沒它的密鑰啊.這樣你解開公鑰文件,看看裏面的信息就知道這個是不是那個你需要用來加密的公鑰了.
實際應用中,一般人都不會找CA去簽名,因爲那是收錢的,所以可以自己做一個自簽名的證書文件,就是自己生成一對密鑰,然後再用自己生成的另外一對密鑰對這對密鑰進行簽名,這個只用於真正需要簽名證書的人,普通的加密解密數據,直接用公鑰和私鑰來做就可以了.
密鑰文件的格式用OpenSSL生成的就只有PEM和DER兩種格式,PEM的是將密鑰用base64編碼表示出來的,直接打開你能看到一串的英文字母,DER格式是二進制的密鑰文件,直接打開,你可以看到........你什麼也看不懂!.X509是通用的證書文件格式定義.pkcs的一系列標準是指定的存放密鑰的文件標準,你只要知道PEM DER X509 PKCS這幾種格式是可以互相轉化的.
== End http://www.cnblogs.com/phpinfo/archive/2013/08/09/3246376.html ==
爲了方便理解,我畫了一個圖,如下:
2. 生成證書
一:生成CA證書
目前不使用第三方權威機構的CA來認證,自己充當CA的角色。
網上下載一個openssl軟件
1.創建私鑰 :
C:\OpenSSL\bin>openssl genrsa -out ca/ca-key.pem 1024
2.創建證書請求 :
C:\OpenSSL\bin>openssl req -new -out ca/ca-req.csr -key ca/ca-key.pem
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:zhejiang
Locality Name (eg, city) []:hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:skyvision
Organizational Unit Name (eg, section) []:test
Common Name (eg, YOUR name) []:root
Email Address []:sky
3.自簽署證書 :
C:\OpenSSL\bin>openssl x509 -req -in ca/ca-req.csr -out ca/ca-cert.pem -signkey ca/ca-key.pem -days 3650
4.將證書導出成瀏覽器支持的.p12格式 :
C:\OpenSSL\bin>openssl pkcs12 -export -clcerts -in ca/ca-cert.pem -inkey ca/ca-key.pem -out ca/ca.p12
密碼:changeit
二.生成server證書。
1.創建私鑰 :
C:\OpenSSL\bin>openssl genrsa -out server/server-key.pem 1024
2.創建證書請求 :
C:\OpenSSL\bin>openssl req -new -out server/server-req.csr -key server/server-key.pem
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:zhejiang
Locality Name (eg, city) []:hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:skyvision
Organizational Unit Name (eg, section) []:test
Common Name (eg, YOUR name) []:192.168.1.246 註釋:一定要寫服務器所在的ip地址
Email Address []:sky
3.自簽署證書 :
C:\OpenSSL\bin>openssl x509 -req -in server/server-req.csr -out server/server-cert.pem -signkey server/server-key.pem -CA ca/ca-cert.pem -CAkey ca/ca-key.pem -CAcreateserial -days 3650
4.將證書導出成瀏覽器支持的.p12格式 :
C:\OpenSSL\bin>openssl pkcs12 -export -clcerts -in server/server-cert.pem -inkey server/server-key.pem -out server/server.p12
密碼:changeit
三.生成client證書。
1.創建私鑰 :
C:\OpenSSL\bin>openssl genrsa -out client/client-key.pem 1024
2.創建證書請求 :
C:\OpenSSL\bin>openssl req -new -out client/client-req.csr -key client/client-key.pem
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:zhejiang
Locality Name (eg, city) []:hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:skyvision
Organizational Unit Name (eg, section) []:test
Common Name (eg, YOUR name) []:sky
Email Address []:sky 註釋:就是登入中心的用戶(本來用戶名應該是Common Name,但是中山公安的不知道爲什麼使用的Email Address,其他版本沒有測試)
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:tsing
3.自簽署證書 :
C:\OpenSSL\bin>openssl x509 -req -in client/client-req.csr -out client/client-cert.pem -signkey client/client-key.pem -CA ca/ca-cert.pem -CAkey ca/ca-key.pem -CAcreateserial -days 3650
4.將證書導出成瀏覽器支持的.p12格式 :
C:\OpenSSL\bin>openssl pkcs12 -export -clcerts -in client/client-cert.pem -inkey client/client-key.pem -out client/client.p12
密碼:changeit
3. C++實戰例子
- ////////////////////////////////////////////////////////////////
- void EXIT_IF_TRUE(bool x)
- {
- if (x) {
- exit(2);
- }
- }
- // 服務端代碼
- int Test_openssl_server_v2()
- {
- OM_NetworkInit();
- SSL_CTX *ctx;
- SSL *ssl;
- X509 *client_cert;
- char szBuffer[1024];
- int nLen;
- struct sockaddr_in addr;
- int len;
- int nListenFd, nAcceptFd;
- // 初始化
- SSLeay_add_ssl_algorithms();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
- ERR_load_BIO_strings();
- // 我們使用SSL V3,V2
- EXIT_IF_TRUE((ctx = SSL_CTX_new(SSLv23_method())) == NULL);
- // 要求校驗對方證書
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- // 加載CA的證書
- EXIT_IF_TRUE(!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));
- // 加載自己的證書
- EXIT_IF_TRUE(SSL_CTX_use_certificate_file(ctx, "server.cer", SSL_FILETYPE_PEM) <= 0);
- // 加載自己的私鑰
- EXIT_IF_TRUE(SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0);
- // 判定私鑰是否正確
- EXIT_IF_TRUE(!SSL_CTX_check_private_key(ctx));
- // 創建並等待連接
- nListenFd = socket(PF_INET, SOCK_STREAM, 0);
- struct sockaddr_in my_addr;
- memset(&my_addr, 0, sizeof(my_addr));
- my_addr.sin_family = PF_INET;
- my_addr.sin_port = htons(8812);
- my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// INADDR_ANY;
- if (bind(nListenFd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
- int a = 2;
- int b = a;
- }
- //
- listen(nListenFd, 10);
- memset(&addr, 0, sizeof(addr));
- len = sizeof(addr);
- nAcceptFd = accept(nListenFd, (struct sockaddr *)&addr, (int *)&len);
- CPosaLog::Printf(CPosaLog::L_DEBUG, "Accept a connect from [%s:%d]\n",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
- // 將連接付給SSL
- EXIT_IF_TRUE((ssl = SSL_new(ctx)) == NULL);
- SSL_set_fd(ssl, nAcceptFd);
- int n1 = SSL_accept(ssl);
- if (n1 == -1) {
- const char* p1 = SSL_state_string_long(ssl);
- int a = 2;
- int b = a;
- }
- // 進行操作
- memset(szBuffer, 0, sizeof(szBuffer));
- nLen = SSL_read(ssl, szBuffer, sizeof(szBuffer));
- CPosaLog::Printf(CPosaLog::L_DEBUG, "Get Len %d %s ok\n", nLen, szBuffer);
- strcat(szBuffer, " this is from server");
- SSL_write(ssl, szBuffer, strlen(szBuffer));
- // 釋放資源
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- closesocket(nAcceptFd);
- return 0;
- }
- // 客戶端代碼
- int Test_openssl_client_v2()
- {
- OM_NetworkInit();
- SSL_METHOD *meth;
- SSL_CTX *ctx;
- SSL *ssl;
- int nFd;
- int nLen;
- char szBuffer[1024];
- // 初始化
- SSLeay_add_ssl_algorithms();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
- ERR_load_BIO_strings();
- // 我們使用SSL V3,V2
- EXIT_IF_TRUE((ctx = SSL_CTX_new(SSLv23_method())) == NULL);
- // 要求校驗對方證書
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- // 加載CA的證書
- EXIT_IF_TRUE(!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));
- // 加載自己的證書
- EXIT_IF_TRUE(SSL_CTX_use_certificate_file(ctx, "client.cer", SSL_FILETYPE_PEM) <= 0);
- // 加載自己的私鑰
- EXIT_IF_TRUE(SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM) <= 0);
- // 判定私鑰是否正確
- EXIT_IF_TRUE(!SSL_CTX_check_private_key(ctx));
- // new
- // 創建連接
- nFd = socket(PF_INET, SOCK_STREAM, 0);
- struct sockaddr_in dest;
- memset(&dest, 0, sizeof(dest));
- dest.sin_family = AF_INET;
- dest.sin_port = htons(8812);
- dest.sin_addr.s_addr = inet_addr("127.0.0.1");
- if (connect(nFd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
- perror("Connect ");
- exit(errno);
- }
- // 將連接付給SSL
- EXIT_IF_TRUE((ssl = SSL_new(ctx)) == NULL);
- SSL_set_fd(ssl, nFd);
- int n1 = SSL_connect(ssl);
- if (n1 == -1) {
- int n2 = SSL_get_error(ssl, n1);
- const char* p1 = SSL_state_string(ssl);
- }
- // 進行操作
- sprintf(szBuffer, "this is from client %d", getpid());
- int nWriten = SSL_write(ssl, szBuffer, strlen(szBuffer));
- // 釋放資源
- memset(szBuffer, 0, sizeof(szBuffer));
- nLen = SSL_read(ssl, szBuffer, sizeof(szBuffer));
- CPosaLog::Printf(CPosaLog::L_DEBUG, "Get Len %d %s ok\n", nLen, szBuffer);
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- closesocket(nFd);
- return 0;
- }
http://blog.csdn.net/oldmtn/article/details/52208747