使用OpenSSLAPI进行安全编程
使用 OpenSSL API 进行安全编程
![使用 OpenSSL API 进行安全编程](https://img.taocdn.com/s3/m/5a55700c4a7302768e9939a6.png)
使用OpenSSL API 进行安全编程创建基本的安全连接和非安全连接Kenneth Ballard (kenneth.ballard@), 自由程序员简介:学习如何使用OpenSSL ——用于安全通信的最著名的开放库——的API 有些强人所难,因为其文档并不完全。
您可以通过本文中的提示补充这方面的知识,并驾驭该API。
在建立基本的连接之后,就可以查看如何使用OpenSSL 的BIO 库来建立安全连接和非安全连接。
与此同时,您还会学到一些关于错误检测的知识。
OpenSSL API 的文档有些含糊不清。
因为还没有多少关于OpenSSL 使用的教程,所以对初学者来说,在应用程序中使用它可能会有一些困难。
那么怎样才能使用OpenSSL 实现一个基本的安全连接呢?本教程将帮助您解决这个问题。
学习如何实现OpenSSL 的困难部分在于其文档的不完全。
不完全的API 文档通常会妨碍开发人员使用该API,而这通常意味着它注定要失败。
但OpenSSL 仍然很活跃,而且正逐渐变得强大。
这是为什么?OpenSSL 是用于安全通信的最著名的开放库。
在google 中搜索“SSL library”得到的返回结果中,列表最上方就是OpenSSL。
它诞生于1998 年,源自Eric Young 和Tim Hudson 开发的SSLeay 库。
其他SSL 工具包包括遵循GNU General Public License 发行的GNU TLS,以及Mozilla Network Security Services (NSS)(请参阅本文后面的参考资料,以获得其他信息)。
那么,是什么使得OpenSSL 比GNU TLS、Mozilla NSS 或其他所有的库都优越呢?许可是一方面因素(请参阅参考资料)。
此外,GNS TLS(迄今为止)只支持TLS v1.0 和SSL v3.0 协议,仅此而已。
Mozilla NSS 的发行既遵循Mozilla Public License 又遵循GNU GPL,它允许开发人员进行选择。
利用OpenSSL库对Socket传输进行安全加密(RSA+AES)
![利用OpenSSL库对Socket传输进行安全加密(RSA+AES)](https://img.taocdn.com/s3/m/6dcbe6243069a45177232f60ddccda38376be1f5.png)
利⽤OpenSSL库对Socket传输进⾏安全加密(RSA+AES)轉⾃:/uid-9543173-id-3921143.html利⽤OpenSSL库对Socket传输进⾏安全加密(RSA+AES)1. 利⽤RSA安全传输AES⽣成密钥所需的Seed(32字节)2. 利⽤AES_encrypt/AES_decrypt对Socket上⾯的业务数据进⾏AES加密/解密理论上只需要AES就能保证全部流程,但由于AES加密所需要的AES-KEY是⼀个结构。
这个⼀个结构,如果通过⽹络进⾏传输,就需要对它进⾏⽹络编码,OpenSSL⾥⾯没有现成的API所以就引⼊RSA来完成⾸次安全的传输,保证Seed不会被窃听。
同样,只使⽤RSA也能完成全部流程,但由于RSA的处理效率⽐AES低,所以在业务数据传输加密上还是使⽤AES下⾯的代码包含了上述传输加密流程所需的所有步骤(OpenSSL部分)在实际的Socket应⽤开发时,需要将这些步骤插⼊到Client/Server⽹络通信的特定阶段所需的OpenSSL主要的API及功能描述1. RSA_generate_key() 随机⽣成⼀个RSA密钥对,供RSA加密/解密使⽤2. i2d_RSAPublicKey() 将RSA密钥对⾥⾯的公钥提出到⼀个BUF,⽤于⽹络传输给对⽅3. d2i_RSAPublicKey() 将从⽹络传过来的公钥信息⽣成⼀个加密使⽤的RSA(它⾥⾯只有公钥)4. RSA_public_encrypt() 使⽤RSA的公钥对数据进⾏加密5. RSA_private_decrypt() 使⽤RSA的私钥对数据进⾏解密6. AES_set_encrypt_key() 根据Seed⽣成AES密钥对中的加密密钥7. AES_set_decrypt_key() 根据Seed⽣成AES密钥对中的解密密钥8. AES_encrypt() 使⽤AES加密密钥对数据进⾏加密9. AES_decrypt() 使⽤AES解密密钥对数据进⾏解密⼀个典型的安全Socket的建⽴流程, 其实就是如何将Server随机Seed安全发给ClientC: Client S:ServerC: RSA_generate_key() --> RSAKey --> i2d_RSAPublicKey(RSAKey) --> RSAPublicKeyC: Send(RSAPublicKey) TO ServerS: Recv() --> RSAPublicKey --> d2i_RSAPublicKey(RSAPublicKey) --> RSAKeyS: Rand() --> Seed --> RSA_public_encrypt(RSAKey, Seed) --> EncryptedSeedS: Send(EncryptedSeed) TO ClientC: Recv() --> EncryptedSeed --> RSA_private_decrypt(RSAKey, EncryptedSeed) --> Seed--- 到此, Client和Server已经完成完成传输Seed的处理--- 后⾯的流程是它们怎样使⽤这个Seed来进⾏业务数据的安全传输C: AES_set_encrypt_key(Seed) --> AESEncryptKeyC: AES_set_decrypt_key(Seed) --> AESDecryptKeyS: AES_set_encrypt_key(Seed) --> AESEncryptKeyS: AES_set_decrypt_key(Seed) --> AESDecryptKey--- Client传输数据给ServerC: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> ServerS: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data--- Server传输数据给ClientS: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> ClientC: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data流程图如下:相关的代码实现如下:#include <string.h>#include <openssl/rsa.h>#include <openssl/aes.h>int main(){// 1. 产⽣RSA密钥对// 产⽣512字节公钥指数为RSA_F4的密钥对,公钥指数有RSA_F4和RSA_3两种// 我不清楚它们的区别,就随便选定RSA_F4// 可以使⽤RSA_print_fp()看看RSA⾥⾯的东西RSA *ClientRsa = RSA_generate_key(512, RSA_F4, NULL, NULL);// ---------// 2. 从RSA结构中提取公钥到BUFF,以便将它传输给对⽅// 512位的RSA其公钥提出出来长度是74字节,⽽私钥提取出来有超过300字节// 为保险起见,建议给它们预留⼀个512字节的空间unsigned char PublicKey[512];unsigned char *PKey = PublicKey; // 注意这个指针不是多余,是特意要这样做的,int PublicKeyLen = i2d_RSAPublicKey(ClientRsa, &PKey);// 不能采⽤下⾯的⽅法,因为i2d_RSAPublicKey()会修改PublicKey的值// 所以要引⼊PKey,让它作为替死⿁// unsigned char *PublicKey = (unsigned char *)malloc(512);// int PublicKeyLen = i2d_RSAPublicKey(ClientRsa, &PublicKey);// 逐个字节打印PublicKey信息printf("PublicKeyBuff, Len=%d\n", PublicKeyLen);for (int i=0; i<PublicKeyLen; i++){printf("0x%02x, ", *(PublicKey+i));}printf("\n");// ---------// 3. 跟据上⾯提出的公钥信息PublicKey构造⼀个新RSA密钥(这个密钥结构只有公钥信息)PKey = PublicKey;RSA *EncryptRsa = d2i_RSAPublicKey(NULL, (const unsigned char**)&PKey, PublicKeyLen);// ---------// 4. 使⽤EncryptRsa加密数据,再使⽤ClientRsa解密数据// 注意, RSA加密/解密的数据长度是有限制,例如512位的RSA就只能最多能加密解密64字节的数据// 如果采⽤RSA_NO_PADDING加密⽅式,512位的RSA就只能加密长度等于64的数据// 这个长度可以使⽤RSA_size()来获得unsigned char InBuff[64], OutBuff[64];strcpy((char *)InBuff, "1234567890abcdefghiklmnopqrstuvwxyz.");RSA_public_encrypt(64, (const unsigned char*)InBuff, OutBuff, EncryptRsa, RSA_NO_PADDING); // 加密 memset(InBuff, 0, sizeof(InBuff));RSA_private_decrypt(64, (const unsigned char*)OutBuff, InBuff, ClientRsa, RSA_NO_PADDING); // 解密 printf("RSADecrypt OK: %s \n", InBuff);// ----------// 5. 利⽤随机32字节Seed来产⽣256位的AES密钥对unsigned char Seed[32]; // 可以采⽤Rand()等⽅法来构造随机信息AES_KEY AESEncryptKey, AESDecryptKey;AES_set_encrypt_key(Seed, 256, &AESEncryptKey);AES_set_decrypt_key(Seed, 256, &AESDecryptKey);// ----------// 6. 使⽤AES密钥对来加密/解密数据// 注意,256位的AES密钥只能加密/解密16字节长的数据strcpy((char *)InBuff, "a1b2c3d4e5f6g7h8?");AES_encrypt(InBuff, OutBuff, &AESEncryptKey);memset(InBuff, 0, sizeof(InBuff));AES_decrypt(OutBuff, InBuff, &AESDecryptKey);printf("AESDecrypt OK: %s \n", InBuff);// ----------// 7. 谨记要释放RSA结构RSA_free(ClientRsa);RSA_free(EncryptRsa);return(0);}。
使用openssl编写服务端和客户端程序
![使用openssl编写服务端和客户端程序](https://img.taocdn.com/s3/m/d00017c6c0c708a1284ac850ad02de80d4d80626.png)
使⽤openssl编写服务端和客户端程序1.使⽤相同的ca⽣成两个证书,⼀个是server.cer,⼀个是client.cer,注意⽣成server.cer的时候必须指明证书可以⽤于服务端的。
服务器代码:1. #include "openssl/bio.h"2. #include "openssl/ssl.h"3. #include "openssl/err.h"4. #include <cutil.h>5. #define EXIT_IF_TRUE(x) if (x) \6. do { \7. fprintf(stderr, "Check '%s' is true\n", #x); \8. ERR_print_errors_fp(stderr); \9. exit(2); \10. }while(0)11. int main(int argc, char **argv)12. {13. SSL_CTX *ctx;14. SSL *ssl;15. X509 *client_cert;16. char szBuffer[1024];17. int nLen;18. struct sockaddr_in addr;19. int len;20. int nListenFd, nAcceptFd;21. // 初始化22. cutil_init();23. cutil_log_set_level(LOG_ALL);24. cutil_log_set_stderr(1);25. SSLeay_add_ssl_algorithms();26. OpenSSL_add_all_algorithms();27. SSL_load_error_strings();28. ERR_load_BIO_strings();29. // 我们使⽤SSL V3,V230. EXIT_IF_TRUE((ctx = SSL_CTX_new (SSLv23_method())) == NULL);31. // 要求校验对⽅证书32. SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);33. // 加载CA的证书34. EXIT_IF_TRUE (!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));35. // 加载⾃⼰的证书36. EXIT_IF_TRUE (SSL_CTX_use_certificate_file(ctx, "server.cer", SSL_FILETYPE_PEM) <= 0) ;37. // 加载⾃⼰的私钥38. EXIT_IF_TRUE (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) ;39. // 判定私钥是否正确40. EXIT_IF_TRUE (!SSL_CTX_check_private_key(ctx));41. // 创建并等待连接42. nListenFd = cutil_socket_new(SOCK_STREAM);43. cutil_socket_bind(nListenFd, NULL, 8812, 1);44. memset(&addr, 0, sizeof(addr));45. len = sizeof(addr);46. nAcceptFd = accept(nListenFd, (struct sockaddr *)&addr, (size_t *)&len);47. cutil_log_debug("Accept a connect from [%s:%d]\n",48. inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));49. // 将连接付给SSL50. EXIT_IF_TRUE( (ssl = SSL_new (ctx)) == NULL);51. SSL_set_fd (ssl, nAcceptFd);52. EXIT_IF_TRUE( SSL_accept (ssl) != 1);53. // 进⾏操作54. memset(szBuffer, 0, sizeof(szBuffer));55. nLen = SSL_read(ssl,szBuffer, sizeof(szBuffer));56. fprintf(stderr, "Get Len %d %s ok\n", nLen, szBuffer);57. strcat(szBuffer, " this is from server");58. SSL_write(ssl, szBuffer, strlen(szBuffer));59. // 释放资源60. SSL_free (ssl);61. SSL_CTX_free (ctx);62. close(nAcceptFd);63. }客户端代码1. #include "openssl/bio.h"2. #include "openssl/ssl.h"3. #include "openssl/err.h"4. #include <cutil.h>5. #define EXIT_IF_TRUE(x) if (x) \6. do { \7. fprintf(stderr, "Check '%s' is true\n", #x); \8. ERR_print_errors_fp(stderr); \9. exit(2); \10. }while(0)11. int main(int argc, char **argv)12. {13. SSL_METHOD *meth;14. SSL_CTX *ctx;15. SSL *ssl;16. int nFd;17. int nLen;18. char szBuffer[1024];19. // 初始化20. cutil_init();21. cutil_log_set_level(LOG_ALL);22. cutil_log_set_stderr(1);23. SSLeay_add_ssl_algorithms();24. OpenSSL_add_all_algorithms();25. SSL_load_error_strings();26. ERR_load_BIO_strings();27. // 我们使⽤SSL V3,V228. EXIT_IF_TRUE((ctx = SSL_CTX_new (SSLv23_method())) == NULL);29. // 要求校验对⽅证书30. SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);31. // 加载CA的证书32. EXIT_IF_TRUE (!SSL_CTX_load_verify_locations(ctx, "cacert.cer", NULL));33. // 加载⾃⼰的证书34. EXIT_IF_TRUE (SSL_CTX_use_certificate_file(ctx, "client.cer", SSL_FILETYPE_PEM) <= 0) ;35. // 加载⾃⼰的私钥36. EXIT_IF_TRUE (SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM) <= 0) ;37. // 判定私钥是否正确38. EXIT_IF_TRUE (!SSL_CTX_check_private_key(ctx));39. // 创建连接40. nFd = cutil_socket_new(SOCK_STREAM);41. if(cutil_socket_connect(nFd, "127.0.0.1", 8812, 30) < 0)42. {43. cutil_log_error("连接服务器失败\n");44. return -1;45. }46. // 将连接付给SSL47. EXIT_IF_TRUE( (ssl = SSL_new (ctx)) == NULL);48. SSL_set_fd (ssl, nFd);49. EXIT_IF_TRUE( SSL_connect (ssl) != 1);50. // 进⾏操作51. sprintf(szBuffer, "this is from client %d", getpid());52. SSL_write(ssl, szBuffer, strlen(szBuffer));53. // 释放资源54. memset(szBuffer, 0, sizeof(szBuffer));55. nLen = SSL_read(ssl,szBuffer, sizeof(szBuffer));56. fprintf(stderr, "Get Len %d %s ok\n", nLen, szBuffer);57. SSL_free (ssl);58. SSL_CTX_free (ctx);59. close(nFd);60. }。
openssl3.0加密算法库编程精要05-详解EVPAPI公开密钥密码算法--生成密钥对
![openssl3.0加密算法库编程精要05-详解EVPAPI公开密钥密码算法--生成密钥对](https://img.taocdn.com/s3/m/4b89de3f2bf90242a8956bec0975f46527d3a7cb.png)
openssl3.0加密算法库编程精要05-详解EVPAPI公开密钥密码算法--⽣成密钥对5.1 公开密钥系统简介 公开密钥系统最早于上世纪 70 年代被发明。
在这种密码系统中,已知加密密钥,在现有计算机技术条件下很难快速求出解密密钥,这个推导过程耗费的计算机算⼒巨⼤到不切实际,所以加密密钥是可以公开的,所以这种系统被称为公开密钥系统。
公开密钥系统被⼴泛地⽤于各种密码协议、数字签名以及电⼦商务等各种领域中。
5.2 RSA 算法 公开密钥系统使⽤的算法最流⾏的当属 RSA 算法,它由 Ronald Rivest、Adi Shamir 和 Lenoard Adleman 于上世纪70 年代发明,该算法的安全性基于⼤数分解的难度,它的原理如下: 设:明⽂为P,密⽂为C,加密函数E(x),解密函数D(x); (1)⾸先选取⼀个公钥指数 e,同时⽣成两个⼤素数 p 和 q; (2)计算n=pq,同时计算欧拉函数ϕ(n)=ϕ(pq)=ϕ(p)ϕ(q)=(p−1)(q−1),确保当前公钥指数 e 和ϕ(n) 互素,那么 e 和 n 组成的数对 (e, n) 即为公钥; (3)加密过程为E(P)=C≡P e(mod n), 0⩽; (4)由于 e 和 n 互素,所以存在⼀个 e 的逆 d,使得ed \equiv 1 (mod \phi(n)) 成⽴,故解密时需要先解线 性同余⽅程求出 d; (5)求出 d 之后,解密过程则为D(C) = C^{d} = ( P^{e} )^{d} = P^{ed}; (6)由于ed \equiv 1 (mod \phi(n)) 成⽴,所以存在⼀个整数 k,使得等式ed = k\phi(n) + 1成⽴; (7)如果P和 n 互素,由欧拉定理得出,P^{\phi(ed)} \equiv P^{k\phi(n) + 1} \equiv PP^{k\phi(n)} \equiv P(mod n) (8)⼀般情况下,P和 n 不互素的概率极⼩,但是如果P和 n 不互素,那么由于P必然⼩于 n (如果 P > n,则⽆法通过解密算法计算出明⽂),p 和 q ⼜都是素数,那么 P必然符合以下的条件 P = ap或P = bq,同时P \neq kpq,a 和 b 为正整数,由于 p 和 q 为素数,所以有以下结论 如果P = ap,那么P和 q 互素; 如果P = bq,那么P和 p 互素; 所以我们假设P = ap,那么P和 q 互素,根据欧拉定理得 P^{\phi(q)} \equiv 1(mod q),然后有 (P^{\phi(q)})^{k\phi(p)} \equiv (1)^{k\phi(p)}(mod q) P^{k\phi(n)} \equiv 1(mod q),然后根据同余的定义可知,存在⼀个数 r,使得以下式⼦成⽴ P^{k\phi(n)} = 1 + rq,两边同时乘以P,得 P^{k\phi(n) + 1} = P + rqP,⼜因为P = ap,所以 P^{k\phi(n) + 1} = P + rqap,然后由于n = pq,所以 P^{k\phi(n) + 1} = P + ran,由于 r、a 是常量,所以最终得 P^{k\phi(n) + 1} \equiv P(mod n),进⼀步得到 P^{ed} \equiv P(mod n) 所以我们证明了使⽤ d 可以解密出加密数据,那么数对 (d,n) 即为私钥,但是原⽂P必须⼩于模数 n, 否则⽆法正确解密。
网络安全编程与实践(第6章OpenSSLEVP编程代码)
![网络安全编程与实践(第6章OpenSSLEVP编程代码)](https://img.taocdn.com/s3/m/f43fbdcf32d4b14e852458fb770bf78a65293a85.png)
网络安全编程与实践(第6章OpenSSLEVP编程代码)6.1.2#include <stdio.h>#include <string.h>#include <openssl/evp.h>#define ITERATIVE_ROUND_FOR_KEY 6#define CHARS_PER_LINE_BASE64 65void print(const char* promptStr,unsigned char * data,int len) {int i;printf("=================%s[长度=%d]============\n",promptStr,len);for(i=0;i<len;i++) printf("%02x",data[i]);printf("\n================================ ===========\n");}//base64 编码void encode(unsigned char* outData,int * outlen,const unsigned char * data,int datalen){int tmp=0;EVP_ENCODE_CTX base64;EVP_EncodeInit(&base64); EVP_EncodeUpdate(&base64,outData,outlen,data,datalen);tmp=*outlen;EVP_EncodeFinal(&base64,outData+*outlen,outlen);*outlen+=tmp;outData[*outlen]=0;print("base64 编码后:", outData,*outlen);}//base64 解码bool decode(unsigned char * outData, int *outlen,const unsigned char * data, int datalen)int i=0,tmp=0,currpos=0,lines=0;EVP_ENCODE_CTX base64;EVP_DecodeInit(&base64);for(;;){currpos+=CHARS_PER_LINE_BASE64*lines++;i= EVP_DecodeUpdate(&base64,outData+tmp,outlen,data+currpos,datalen-currpos);if(i<0){printf("解码错误!\n");return false;}tmp+=*outlen;if(i==0) break;}EVP_DecodeFinal(&base64,outData+tmp,outlen);*outlen+=tmp;outData[*outlen]=0;print("base 64 解码后:",outData,*outlen);return true;}void main(){OpenSSL_add_all_ciphers();OpenSSL_add_all_digests();//加密const EVP_CIPHER * type;type=EVP_des_ede3_cbc();printf("密钥长度=%d,向量长度=%d\n",type->key_len,type->iv_len);const char * password="renhl252";//用口令生成密钥printf("密码是:%s\n",password);unsigned char key[EVP_MAX_KEY_LENGTH];unsigned char iv[EVP_MAX_IV_LENGTH];EVP_BytesT oKey(type,//密钥类型EVP_md5(),//摘要计算类型NULL,(const unsigned char *)password,(int)strlen(password),ITERATIVE_ROUND_FOR_KEY,//迭代轮数key,iv);EVP_CIPHER_CTX ctx;EVP_EncryptInit(&ctx,type,key,iv);char SimpleT ext[]="Let's pray for peace of our lovely world.";unsigned char out[512+8];int outl;int temp=(int)strlen(SimpleText);EVP_EncryptUpdate(&ctx,out,&outl,(const unsigned char *)SimpleText,(int)strlen(SimpleT ext));temp=outl;EVP_EncryptFinal(&ctx,out+outl,&outl);outl+=temp;EVP_CIPHER_CTX_cleanup(&ctx);print("加密之后结果:",out,outl);unsigned char txtAfterBase64[sizeof(SimpleText)*3];//进行base64编码encode(txtAfterBase64,&temp,out,outl);memset(out,0,sizeof(out));//解码decode(out,&outl,txtAfterBase64,temp);//解密unsigned char txAfterDecrypt[512]; int txLenAfterDecrypt;EVP_DecryptInit(&ctx,type,key,iv);EVP_DecryptUpdate(&ctx,txAfterDecrypt,&txLenAfterDecrypt,out,outl);temp=txLenAfterDecrypt;EVP_DecryptFinal(&ctx,txAfterDecrypt+txLenAfterDecrypt,&txLenAfterDecrypt);txLenAfterDecrypt+=temp;EVP_CIPHER_CTX_cleanup(&ctx);txAfterDecrypt[txLenAfterDecrypt]=0;printf("解密之后结果(长度=%d):\n[%s]\n",txLenAfterDecrypt,txAfterDecrypt);printf("\n================================ ===========\n");printf("\nclick any key continue.");getchar();}6.2.2#include <stdio.h>#include <string.h>#include <openssl/evp.h>#include <openssl/rsa.h>void print(const char* promptStr,unsigned char * data,int len) {int i;printf("=================%s[长度=%d]============\n",promptStr,len);for(i=0;i<len;i++) printf("%02x",data[i]);printf("\n================================ ===========\n");}//如果成功返回包装了RSA参数的EVP_PKEY,否则返回NULL EVP_PKEY *getRSA(){EVP_PKEY *pkey=NULL;RSA *rsa=RSA_generate_key(1024,RSA_3,NULL,NULL);if(NULL==rsa){printf("生成RSA密钥对失败!\n");return NULL;}//隐藏RSA密钥抵御定时攻击RSA_blinding_on(rsa,NULL);printf("\n成功生成RSA密钥对!\n");pkey=EVP_PKEY_new();if(NULL==pkey){printf("EVP_PKEY_new failed\n");RSA_free(rsa);return NULL;}//将rsa对象赋给EVP_PKEY结构EVP_PKEY_assign_RSA(pkey,rsa);return pkey;}void main(){OpenSSL_add_all_ciphers();EVP_PKEY *pkey=NULL;int len=-1;char plainText[]="[For test to public/private key encryption/decryption]";unsigned char encData[512];unsigned char decData[512];pkey=getRSA();if(pkey==NULL){exit(-1);}len=EVP_PKEY_encrypt(encData,(unsigned char *)plainText,sizeof(plainText),pkey);if(len==-1){printf("EVP_PKEY_encrypt加密失败\n");exit(-1);}print("加密后的数据",encData,len);len=EVP_PKEY_decrypt(decData,encData,len,pkey);if(len==-1){printf("EVP_PKEY_decrypt解密失败\n");exit(-1);}print("解密后的数据",decData,len);printf("\n明文是:[长度=%d字节]:%s\n",len,decData); EVP_PKEY_free(pkey);printf("\nclick any key continue.");getchar();}6.3.2//哈希摘要算法#include <stdio.h>#include <string.h>#include <openssl/evp.h>void main(){OpenSSL_add_all_digests();//初始化上下文对象的初始函数EVP_MD_CTX mdctx;EVP_MD_CTX_init(&mdctx);//初始化摘要算法const char *digestName="sha1";//"md5"; const EVP_MD *md;md=EVP_get_digestbyname(digestName);if(!md){printf("错误的摘要算法名称:%s\n",digestName); exit(1);}EVP_DigestInit(&mdctx,md);//要被计算的消息数组char msgs[][64]={"It's just for test","Author:Jian Shen", "HelloWord from openssl"};//循环加入技术摘要的数据for(int i=0;i<sizeof(msgs)/sizeof(msgs[0]);i++){EVP_DigestUpdate(&mdctx,msgs[i],strlen(msgs[i]));}//结束摘要计算unsigned int md_len;unsigned char md_value[EVP_MAX_MD_SIZE];EVP_DigestFinal(&mdctx,md_value,&md_len);EVP_MD_CTX_cleanup(&mdctx);printf("摘要值是:(类型=%s;长度=%d 字节):",digestName,md_len);for(unsigned int j=0;j<md_len;j++) printf("%02x",md_value[j]);printf("\n");}6.4.2.txt//哈希摘要算法#include <stdio.h>#include <string.h>#include <openssl/evp.h>#include <openssl/hmac.h>void print(const char* promptStr,unsigned char * data,int len) {int i;printf("=================%s[长度=%d]============\n",promptStr,len);for(i=0;i<len;i++) printf("%02x",data[i]);printf("\n================================ ===========\n");}void main(){OpenSSL_add_all_digests();HMAC_CTX mac_ctx;//初始化摘要算法const char *digestName="sha1";//"md5";const EVP_MD *md;md=EVP_get_digestbyname(digestName);if(!md){printf("错误的摘要算法名称:%s\n",digestName);exit(1);}char key[]="simple_key";HMAC_Init(&mac_ctx,key,sizeof(key),md);unsigned char buffer[2048]="yasiuiasdasdooasdjidajiodjajiaiadjioioadidjjdjdjjdj jakkajajgewyeygedbdbbdbdb";HMAC_Update(&mac_ctx,buffer,sizeof("yasiuiasdasdooasdjidajiodjajiaiadjioioadidjjdjdjjdjjakkajajgewyeygedbdbbdbdb"));unsigned char mac_value[EVP_MAX_MD_SIZE];unsigned int mac_len=0;HMAC_Final(&mac_ctx,mac_value,&mac_len);HMAC_cleanup(&mac_ctx);printf("HMAC(%s,%s)\n",buffer,key);print(NULL,mac_value,mac_len);}6.5.2.txt#include <stdio.h>#include <string.h>#include <openssl/evp.h>#include <openssl/dsa.h>#include <openssl/rsa.h>#include <openssl/err.h>void print(const char* promptStr,unsigned char * data,int len) {int i;printf("=================%s[长度=%d]============\n",promptStr,len);for(i=0;i<len;i++) printf("%02x",data[i]);printf("\n================================ ===========\n");}//如果成功返回EVP_KEY,否则返回NULLEVP_PKEY *getDSA(){EVP_PKEY *pkey=NULL;DSA * dsa=DSA_generate_parameters(1024,NULL,0,NULL,NULL,NULL, NULL);if(NULL==dsa){printf("生成DSA参数失败\n");return NULL;}printf("\nDSA参数成功产生!\n");int ret=0;ret=DSA_generate_key(dsa);if(ret==0){printf("\nDSA_generate_key 调用失败\n");goto err;}printf("\nDSA密钥对生成成功\n");pkey=EVP_PKEY_new();if(NULL==pkey){printf("\nEVP_PKEY_new 失败\n");goto err;}EVP_PKEY_assign_DSA(pkey,dsa);return pkey;err:DSA_free(dsa);return NULL;}void main(){OpenSSL_add_all_ciphers();OpenSSL_add_all_digests();EVP_MD_CTX mdctx;EVP_MD_CTX_init(&mdctx);const EVP_MD *md;char mdName[]="dss1";md=EVP_get_digestbyname(mdName);if(!md){printf("错误的摘要算法名称:%s\n",mdName);exit(1);}EVP_SignInit(&mdctx,md);//依次加入所要签名的数据char msgs[][64]={"It's just for test.","Author:Jian Shen","Hello World from openssl"};int i;for(i=0;i<sizeof(msgs)/sizeof(msgs[0]);i++){EVP_SignUpdate(&mdctx,msgs[i],strlen(msgs[i]));}//计算签名;实际上该步先计算摘要值,然后对该值用私钥签名unsigned char sig_value[1024]; unsigned int sig_len;EVP_PKEY * pkey=NULL;pkey=getDSA();if(pkey==NULL){exit(1);}i=EVP_SignFinal(&mdctx,sig_value,&sig_len,pkey);if(i==0){printf("计算签名失败");exit(1);}EVP_MD_CTX_cleanup(&mdctx); EVP_PKEY_free(pkey);print("签名值是:",sig_value,sig_len); }。
OpenAPI安全认证库(C#)V1.0.1开发指南(海康威视iSecure Center)
![OpenAPI安全认证库(C#)V1.0.1开发指南(海康威视iSecure Center)](https://img.taocdn.com/s3/m/2a5d83a8a417866fb94a8e4b.png)
备注: 使用其它接口之前需要通过此接口设置平台信息。
示例(使用 HTTPS 协议):
详见基于 OpenAPI 安全认证库(C#)接口的使用示例。
2.2 POST 请求接口
接口名称: byte[] HttpPost(string uri, string body, int timeout)
参数说明: [in] uri : GET 请 求 的 URI , 需 要 自 行 拼 接 参 数 , 如 /artemis/api/resource/v1/cameras/indexCode?cameraIndexCode=a10cafaa777c49a5af92c165c95970e0 [in] timeout:请求超时时间,单位:秒
2. 接口定义
2.1 设置平台信息接口
接口名称: void SetPlatformInfo(string appkey, string secret, string ip, int port = 443, bool isHttps = true)
接口描述: 统一设置 HTTP/HTTPS 请求的平台参数信息。
VS 版本较高需要升级,可能存在警告或者错误的情况,请自行根据升级结果文档解决),如下图:
图 3.2.2-1 HttpUtillib Demo
2、 选择 Any CPU 模式下的平台。选择“生成”->“生成 HttpUtillib”(或英文模式下的“Build”->“Build HttpUtillibTest”),生成的 EXE 位于 bin 目录下。生成 HttpUtillib 以及生成结果如下图:
3.1 示例:POST 请求根据监控点编号查询监控点信息
// step1:组装POST请求body string body = "{\"cameraIndexCode\": \"a10cafaa777c49a5af92c165c95970e0\"}"; // step2:填充Uri string uri = "/artemis/api/resource/v1/cameras/indexCode"; // step3:发起POST请求,超时时间15秒,返回响应字节数组 byte[] result = HttpUtillib.HttpPost(uri, body, 15);
C语言使用openSSL库AES模块实现加密功能详解
![C语言使用openSSL库AES模块实现加密功能详解](https://img.taocdn.com/s3/m/c4bd9bfd5ff7ba0d4a7302768e9951e79b896911.png)
C语⾔使⽤openSSL库AES模块实现加密功能详解本⽂实例讲述了C语⾔使⽤openSSL库AES模块实现加密功能。
分享给⼤家供⼤家参考,具体如下:概述在密码学⾥⾯⼀共有3中分类:1.对称加密/解密对称加密⽐较常见的有DES/AES。
加密⽅和解密⽅都持有相同的密钥。
对称的意思就是加密和解密都是⽤相同的密钥。
2.⾮对称加密/解密常见的加密算法DSA/RSA。
如果做过Google Pay的话,应该不会陌⽣。
⾮对称意味着加密和解密使⽤的密钥不是相同的。
这种应⽤的场合是需要保持发起⽅的权威性,⽐如Google中⼀次⽀付⾏为,只能Google通过私钥来加密产出来,但是⼤家都能通过公钥来认证这个是真的。
打个更加浅显的⽐⽅:私钥可以理解成美联储的印钞机,公钥可以理解成在民间⽆数的美元验钞机。
还有⼀个场合也是https使⽤证书⽅式登录的时候,也是使⽤的双向的⾮对称加密模式来做的。
3.离散这种只能被称为验签,⽽不是加密。
因为这类算法只能⼀个⽅向(将输⼊数据离散到某个特定的数字,反向解密是⽆法做到的。
)。
最常见的算法就是MD5。
在写php的时候⼤量的使⽤这种验签来做认证。
他可以将字符串离散成32byte的16进制的数字。
本次使⽤AES CBC⽅式来加密。
CBC模式加密是SSL的通讯标准,所以在做游戏的时候经常会使⽤到。
openSSL的基本⽤法可以参考这个两个细节这种加密的需要了解下⾯两个细节:1.加密的内存块⼀般按照16字节(这个也可以调整)对齐;当原始内存块没有对齐字节数的时候,需要填充;2.加密解密不会引发内存的膨胀或者缩⼩;最近在使⽤Python,Java,c#都去看过AES的接⼝,最轻松的是c#,java。
当使⽤C来写,才能明显感受到在这些操作过程中,有多少次内存的分配,多少的内存拼接。
啥事都有成本,封装良好的语⾔损失掉的效率可能来⾃于这些便利。
准备知识函数接⼝int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);// 设置加密keyAES_KEY aes;AES_set_encrypt_key(key,128,&aes);// 这⾥填写的128是bit位,128bit=(128/8)bytes=16bytes,这个换算和32bit对应int为内存指针的原理⼀样。
OpenSSL 编程入门
![OpenSSL 编程入门](https://img.taocdn.com/s3/m/73f62268a45177232f60a2db.png)
OpenSSL 编程入门作者:Eric Rescorla on Sat, 2001-09-01 01:0如果你急切的想构建一个简单的Web客户端和服务器对,这时你就需要使用SSL了..SSL是一种保护基于TCP协议的网络应用最快而且最简单的的方法,如果你正在用C语言做开发,那么对于你来说,最好的选择可能就是使用OpenSSL了. OpenSSL是在Eric Young 的SSLeay包的基础上对TSL/SSL的一个免费的执行(类似于BSD方式的License).然而, 不幸运的事情是, 伴随OpenSSL一起发布的文档和示例代码并不是很完全, 使用它的人需要更多的东西.在OpenSSL被使用之处, man手册都相当优秀,可是这些手册失去了大的背景因为它们只是参考资料而不是教程.OpenSSL的API多而复杂, 因此我们在此并不会作出一个完整的讲述. 相反,我的目的只是教会你如何去高效的使用man手册.在本文中, 我们将会通过构建一个简单的Web客户端和服务器来演示OpenSSL的基本特点. 而在后续的第二篇中我们将会介绍OpenSSL的一些高级特性, 比如会话恢复和客户端认证等.在话题开始之前, 我会认为你已经熟悉SSL和HTTP了, 或者最起码在概念层上应该有一些了解. 如果你对此一无所知, 推荐一个比较好的方法,那就是参考RFC(参见附录).由于篇幅原因, 本文只包涵了源代码的一些摘录, 完整的代码可以从作者的站点/openssl-examples/上下载.程序我们的客户端是一个简单的HTTPS(见RFC 2818)客户端,它在初始化了一个到达服务器的SSL连接之后便通过这个连接将HTTP请求传送给HTTP服务器. 然后等待服务器端的响应,并将响应信息打印在屏幕上.这是对通常那些”获取并且打印信息”的程序功能更简化的一个例子.服务器端程序是一个简单的HTTPS 服务器, 它等待从客户端发出的TCP连接, 每当接收到一个连接时,它会磋商这个连接(的合法性). 一旦这个连接被确定下来, 它会读取客户端的HTTP请求, 并将HTTP请求的响应信息传输给客户端. 当响应传输完毕时它会关闭这个连接.我们的第一个任务就是建立一个上下文对象(一个SSL_CTX), 这个上下文对象会在每次需要建立新的SSL连接的时候被用来创建一个新的连接对象. 而这些连接对象则用于SSL的握手,读和写.(使用上下文对象)这种方法有两个优点: 首先, 上下文对象允许一次初始化多个结构体, 这样就提提高了性能. 在大多数应用中, 每一个SSL连接都使用相同的加密算法(keying material)和CA(certificate authority)列表等. 而采用上面这种方法, 我们就不需要在每次连接的时都去加载这些信息(加密算法和证书), 而只需要在程序启动时将它们加载进上下文对象中. 然后,当我们需要创建一个新的连接时, 只需要将新的连接简单的指向这个上下文对象就可以了. 使用一个简单的上下文对象的第二个好处就是它允许多个SSL连接之间共享数据, 比如用于SSL会话恢复的SSL缓冲(cache). 上下文初始化由主要的四个任务组成, 通过列表1所示的initialize_ctx()函数来完成.列表 1 initialize_ctx()在应用OpenSSL之前, 整个库需要进行初始化, 这个过程通过SSL_library_init()函数来完成,它主要加载OpenSSL将会用到的算法, 如果我们想要很好的报告差错信息, 同样需要通过SSL_load_error_strings()来加载错误字符串, 否则, 就不能够将OpenSSL的错误映射为字符串.我们同样需要创建一个对象来作为错误打印的上下文. OpenSSL为输入和输出抽象了一个叫做BIO对象的概念.这样可以使得程序员针对不同种类的IO通道(socket, 中断,内存缓冲等)使用相同的函数,而唯一的差别就是在函数中使用的是不同种类的BIO对象.在本例中,我们通过将一个BIO对象与标准错误stderr绑定来打印错误信息.如果你正在写一个能够执行客户端认证的服务器或者客户端程序, 你就需要加载自己的公钥或者私钥以及相关的证书.证书存储空隙中, 并且通过SSL_CTX_use_certificate_chain_file()函数与CA证书一起被加载形成证书链表. SSL_CTX_use_PrivateKey_file()函数用来加载私钥.出于安全原因, 私钥通常通过密码来加密, 如果使用密码加密的话, 密码回调函数(通过SSL_CTX_set_default_passwd_cb()来设置)将会在获取密码时被调用的.如果你需要认证已经连接到你的客户端, OpenSSL需要知道你信任哪些CA, SSL_CTX_load_verify_locations()调用用来加载CA.为了保证安全, OpenSSL需要一个好的强性随机数源, 通常,为随机数生成器(RNG)提供种子原料是应用本身的责任, 然而,如果/dev/urandom可用的话,OPenSSL会自动的使用/var/urandom来为RNG播种, 由于/dev/urandom在Linux是标准化的, 我们不需要为它做任何事情, 这个就很方便了, 因为收集随机数很诡异,而且很容易引起系统抖动上升. 注意,如果你在一台不是Linux的系统上,你可能会在某些时刻得到错误数据, 因为随机数产生器没有被播种, OpenSSL的rand(3) man手册为你提供了更多可以参考的信息.客户端当SSL完成了对SSL上下文对象的初始化后,它已经为连接到服务器做好准备。
信息安全技术实验报告-基于openssl库的对称密码实验
![信息安全技术实验报告-基于openssl库的对称密码实验](https://img.taocdn.com/s3/m/7be5fe8b0508763231121292.png)
信息安全技术实验报告姓名:xxx学号:xxxxxxxxx专业:xxxxx日期:xxxxxxLab2: 基于openssl库的对称密码实验一、实验目的让学生熟悉加密的概念,熟悉和了解加密算法(cipher)、加密模式(encryption mode)、以及初始向量(IV)的定义与作用。
二、实验环境1.虚拟机VirtualBox2.Ubuntu-16.04.63.bless十六进制编辑器三、实验内容3.1 使用openssl enc 命令来加密/解密一个文件。
命令基本格式如下:$ openssl enc -e ciphertype -in plain.txt -out cipher.bin -K 00112233445566778899aabbccddeeff-iv 0102030405060708了解openssl命令的基本格式中各个参数和选项的含义,然后将上述命令代码中的ciphertype 替换成指定的加密类型,比如-aes-128-cbc, -aes-128-cfb, -bf-cbc 等,也可以替换命令实例中的参数K和iv。
实验报告中相关问题的回答:1.配置好实验环境,查看openssl版本。
图3.1.1 查看openssl版本2. 创建一个文本文件,并任意输入内容,保存后作为输入文件,执行上面的命令行中的加密命令,然后使用对应的解密命令解密加密文件,并对比解密后的输出和原始输入文件是否相同。
创建一个文本文件plain.txt,并输入内容“I love you”,作为输入文件。
这里使用的加密类型为-aes-128-cbc 。
加密操作如下:$ openssl enc -e -aes-128-cbc -in plain.txt -out cipher.bin -K 00112233445566778899aabbccddeeff-iv 0102030405060708图3.1.2-1 文本加密解密操作如下:$ openssl enc -d -aes-128-cbc -in cipher.bin -out dcipher.txt-K 00112233445566778899aabbccddeeff -iv 0102030405060708图3.1.2-2 文本解密结论:解密后的输出和原始输入文件相同。
OpenSSL程序编写步骤
![OpenSSL程序编写步骤](https://img.taocdn.com/s3/m/a081073f376baf1ffc4fad50.png)
OpenSSL程序编写步骤OpenSSL是一个开放源代码的SSL协议的产品实现,它采用C语言作为开发语言,具备了跨系统的性能。
调用OpenSSL 的函数就可以实现一个SSL加密的安全数据传输通道,从而保护客户端和服务器之间数据的安全。
头文件:#include <openssl/ssl.h>#include <openssl/err.h>基于OpenSSL的程序都要遵循以下几个步骤:(1 ) OpenSSL初始化在使用OpenSSL之前,必须进行相应的协议初始化工作,这可以通过下面的函数实现:int SSL_library_int(void);(2 ) 选择会话协议在利用OpenSSL开始SSL会话之前,需要为客户端和服务器制定本次会话采用的协议,目前能够使用的协议包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。
需要注意的是,客户端和服务器必须使用相互兼容的协议,否则SSL会话将无法正常进行。
(3 ) 创建会话环境在OpenSSL中创建的SSL会话环境称为CTX,使用不同的协议会话,其环境也不一样的。
申请SSL会话环境的OpenSSL函数是:SSL_CTX *SSL_CTX_new(SSL_METHOD * method);当SSL会话环境申请成功后,还要根据实际的需要设置CTX的属性,通常的设置是指定SSL 握手阶段证书的验证方式和加载自己的证书。
制定证书验证方式的函数是:int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *));为SSL会话环境加载CA证书的函数是:SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath);为SSL会话加载用户证书的函数是:SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type);为SSL会话加载用户私钥的函数是:SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type);在将证书和私钥加载到SSL会话环境之后,就可以调用下面的函数来验证私钥和证书是否相符:int SSL_CTX_check_private_key(SSL_CTX *ctx);(4) 建立SSL套接字SSL套接字是建立在普通的TCP套接字基础之上,在建立SSL套接字时可以使用下面的一些函数:SSL *SSl_new(SSL_CTX *ctx);//申请一个SSL套接字int SSL_set_fd(SSL *ssl,int fd);)//绑定读写套接字int SSL_set_rfd(SSL *ssl,int fd);//绑定只读套接字int SSL_set_wfd(SSL *ssl,int fd);//绑定只写套接字(5) 完成SSL握手在成功创建SSL套接字后,客户端应使用函数SSL_connect( )替代传统的函数connect( )来完成握手过程:int SSL_connect(SSL *ssl);而对服务器来讲,则应使用函数SSL_ accept ( )替代传统的函数accept ( )来完成握手过程:int SSL_accept(SSL *ssl);握手过程完成之后,通常需要询问通信双方的证书信息,以便进行相应的验证,这可以借助于下面的函数来实现:X509 *SSL_get_peer_certificate(SSL *ssl);该函数可以从SSL套接字中提取对方的证书信息,这些信息已经被SSL验证过了。
第19章 利用OpenSSL实现安全的Web Server程序
![第19章 利用OpenSSL实现安全的Web Server程序](https://img.taocdn.com/s3/m/1de7923983c4bb4cf7ecd151.png)
《计算机网络高级软件编程技术》
OpenSSL库
• OpenSSL库的安装
安装VC++ 6.0 从官方网站/ActivePerl 下载ActivePerl的Win32版本并安装 从官方网站/下载OpenSSL 源代码并安装
3
《计算机网络高级软件编程技术》
提纲
• • • • •
编程训练目的 编程训练要求 相关知识介绍 程序设计分析 扩展与提高
4
《计算机网络高级软件编程技术》
编程训练目的
• 在基础类编程训练10的基础上,掌握基 于OpenSSL协议的安全Web Server的软 件结构的设计与编程实现 • 掌握利用OpenSSL生成认证证书的方法 • 熟悉客户端认证与利用IPSec实现安全通 信的方法
为Web服务器签发证书
openssl x509 -req -in serverreq.pem -sha1 -extensions usr_cert -CA rootcert.pem -CAkey rootkey.pem -CAcreateserial -out server.pem server.pem即为CA中心为Web服务器签发的证书
5
《计算机网络高级软件编程技术》
提纲
• • • • •
编程训练目的 编程训练要求 相关知识介绍 程序设计分析 扩展与提高
6
《计算机网络高级软件编程技术》
编程训练要求
编写程序,利用OpenSSL实现安全的Web Server,具体要求如下 :
• • • Server启动时可指定端口,缺省为443 可指定Web Server根目录的路径,比如C:\Server Server应能够并发处理多个请求,要求至少能支持 Get命令。可以增强Web Server的功能,如支持 Head、Post以及Delete命令等 书写必要的客户端测试程序用于发送HTTPS请求并 显示返回结果,也可使用一般的Web浏览器测试
基于OpenSSL的安全聊天系统网络安全程序设计
![基于OpenSSL的安全聊天系统网络安全程序设计](https://img.taocdn.com/s3/m/88e69f19fab069dc5122016e.png)
目录一、实验选题 (2)二、实验环境 (2)三、系统设计 (2)1、Windows下进行OpenSSL编程的主要步骤 (2)2、系统框图 (3)3、重要函数 (3)四、实验过程截图 (15)1、安装openssl (16)2、客户端与服务器端安全通信 (17)五、实验与课程总结 (19)六、源代码清单 (19)一、实验选题基于OpenSSL的安全聊天系统–Windows或Linux平台均可–点到点模式–基于OpenSSL的安全套接字通信–客户端服务器双向认证–聊天记录本地加密存储,输入正确口令可查看二、实验环境操作系统Windows XP sp3开发平台VC++6.0其它OpenSSL ActivePerl三、系统设计1、Windows下进行OpenSSL编程的主要步骤1.安装ActivePerl,阅读openssl开发包中的install.w32 ,编译openssl,;2.下载CA证书并对其进行公私钥的分割,使用openssl生成证书文件;3.阅读相关文档,熟悉OpenSSL编程接口,编写server和client端代码;4.将所需要的文件放到源代码目录,调试并运行程序,使用证书来验证进行安全通信的过程及身份认证方式。
2、系统框图3、重要函数Client :连接服务器端并接收服务器消息的线程函数:void ClientThreadProc(void* void_parm){WSADATA wsaData;int err;int sd;struct sockaddr_in sa;SSL* ssl;X509* server_cert;char* str; charbuffer [8912];int maxFd;fd_set writeFds, readFds, excFds;DWORD dwIP;dwIP = (DWORD)void_parm;//char *msg = (char *)malloc(128);char msg[128];//初始化windows socketif (WSAStartup(MAKEWORD(2, 2), &wsaData)){return;}//新建socketsd = socket (AF_INET, SOCK_STREAM, 0);memset (&sa, '\0', sizeof(sa));//设置服务端IP地址、和端口sa.sin_family = AF_INET;sa.sin_addr.s_addr = dwIP;sa.sin_port = htons(8443);//连接服务器err = connect(sd, (struct sockaddr*) &sa,sizeof(sa));if(err < 0){// char *msg = (char *)malloc(128);strcpy(msg,"连接服务器失败!");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);return;}strcpy(msg,"SSL开始握手!");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);//新建SSL句柄ssl = SSL_new (ctx);//设置socket句柄到SSL句柄SSL_set_fd (ssl, sd);//SSL连接err = SSL_connect (ssl);if(err != 1){// char *msg = (char *)malloc(128);strcpy(msg,"SSL连接服务器失败!");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);return;}server_cert = SSL_get_peer_certificate (ssl);strcpy(msg,"Client certificate:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);strcpy(msg,"subject: ");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);strcpy(msg,str);SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);OPENSSL_free (str);str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);strcpy(msg,"issuer:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);strcpy(msg,str);SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);OPENSSL_free (str);X509_free (server_cert);// printf ("SSL connection using %s\n", SSL_get_cipher (ssl));strcpy(msg,"SSL connection using:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);/*打印所有加密算法的信息(可选)*/strcpy(msg,SSL_get_cipher (ssl));SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);maxFd = sd;client.fd=sd;client.ssl=ssl;strcpy(msg,"连接服务器成功!");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg); for(;;){FD_ZERO(&writeFds);FD_ZERO(&readFds);FD_ZERO(&excFds);FD_SET(sd, &readFds);//select 异步等待服务器int nfd = select(maxFd + 1, &readFds, &writeFds, &excFds, NULL);if(nfd <= 0){if(errno == EINTR) /* interrupted system call */continue;return;}if(FD_ISSET(sd, &readFds)){//接收服务器消息int len = SSL_read(ssl,buffer,sizeof(buffer));if(len <=0){char *msg = (char *)malloc(128);strcpy(msg,"服务器退出!");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,sd,(long )msg);return;}buffer[len]='\0';char *msg = (char *)malloc(len +1);strcpy(msg,buffer);//通过WM_CLIENT_MSG消息,发送到主窗体SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_CLIENT_MSG,sd,(long )msg);}}return;}链接服务器:void CClientDlg::OnConnect(){unsigned long idThread;DWORD dwIP=0;CIP ipaddr;//让用户输入客户端IPm_liststate.ResetContent();if(client.fd==0)//判断是否已经连接{if(ipaddr.DoModal() == IDOK){dwIP=inet_addr(ipaddr.IP.GetBuffer(0));}//创建线程,连接服务器,并接收服务器端消息CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ClientThreadProc,(void *)dwIP, 0 ,&idThread);}else{m_liststate.InsertString(0,"已经连接!");}}发送消息到服务器:void CClientDlg::OnSend(){UpdateData();if(client.fd !=0){//发送消息SSL_write(client.ssl,m_str.GetBuffer(0),m_str.GetLength());m_str.ReleaseBuffer();}else{return;}//插入发送的消息的列表框m_list.InsertString(0,m_str);m_str.Empty();UpdateData(FALSE);((CEdit *)GetDlgItem(IDC_EDIT1))->SetActiveWindow();}处理WM_CLIENT_MSG消息:LRESULT CClientDlg::OnClientMsg(WPARAM wParam,LPARAM lParam) {CString msg;msg.Format("[Socket:%d]%s",wParam,(char *)lParam);m_list.InsertString(0,msg);free((void*)lParam);return 0L;}LRESULT CClientDlg::OnStateMsg(WPARAM wParam,LPARAM lParam) {CString msg;msg.Format("[Socket:%d]%s",wParam,(char *)lParam);m_liststate.InsertString(-1,msg);// m_liststate.AddString(msg);// free((void*)lParam);return 0L;}void CClientDlg::OnDestroy(){WSACleanup();SSL_CTX_free(ctx);CDialog::OnDestroy();}断开链接:void CClientDlg::OnDisconnect(){if(client.fd !=0){closesocket(client.fd);SSL_shutdown(client.ssl);SSL_free(client.ssl);client.ssl=NULL;client.fd=0;m_liststate.InsertString(0,"已断开和服务器的链接!");m_liststate.ResetContent();}}Server:处理客户端事务线程函数,接收客户端消息:void client( void* void_parmint ){int clientFd = (int) void_parmint;int maxFd;int len;int flag=0;fd_set writeFds, readFds, excFds;char buffer[8192];descriptor_t clientDesc;clientDesc.fd = clientFd;clientDesc.ssl = NULL;SSL *ssl;char *str;X509* client_cert;//新建SSL连接句柄if((ssl = SSL_new(ctx)) == NULL){return;}//设置SSL连接Socket句柄SSL_set_fd(ssl, clientFd);//接收SSL连接if(SSL_accept(ssl) <= 0){return;}// char *msg = (char *)malloc(128);char msg[128];strcpy(msg,"SSL connection using");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )msg );/*打印所有加密算法的信息(可选)*/strcpy(msg,SSL_get_cipher (ssl));SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )msg );/*得到客户端的证书并打印些信息(可选) */client_cert = SSL_get_peer_certificate (ssl);if (client_cert != NULL) {// printf ("Client certificate:\n");strcpy(msg,"Client certificate:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )msg );str=X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);//printf ("\t subject: %s\n", str);strcpy(msg,"subject:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )msg );SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )str);OPENSSL_free (str);str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);//printf ("\t issuer: %s\n", str);strcpy(msg,"issuer:");SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )msg );SendMessage(AfxGetMainWnd()->GetSafeHwnd(),WM_STATE_MSG,clientFd,(long )str);OPENSSL_free (str);X509_free (client_cert);/*如不再需要,需将证书释放*/}clientDesc.ssl = ssl;//把客户端链接句柄保存到全局变量中。
安全协议实验报告(基于OpenSSL CryptoAPI的数字签名系统设计与实现) 李鑫 20103277
![安全协议实验报告(基于OpenSSL CryptoAPI的数字签名系统设计与实现) 李鑫 20103277](https://img.taocdn.com/s3/m/b51e1f372af90242a895e52e.png)
上海电力学院安全协议大作业题目: 基于OpenSSL/CryptoAPI的数字签名系统设计与实现学号: 20103277 学生姓名:李鑫院系:计算机科学与技术学院专业:信息安全班级: 20102512013年12 月13 日基于OpenSSL/CryptoAPI的数字签名系统设计与实现李鑫(上海电力学院信息安全系,上海市201300)摘要:数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。
一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
数字签名是非对称密钥加密技术与数字摘要技术的应用。
数字签名了的文件的完整性是很容易验证的(不需要骑缝章,骑缝签名,也不需要笔迹专家),而且数字签名具有不可抵赖性(不需要笔迹专家来验证)。
关键字:数字签名;签名验证;哈希算法0引言1. CryptoAPI简介CryptoAPI是一组函数,为了完成数学计算,必须具有密码服务提供者模块(CSP)。
Microsoft通过捆绑RSA Base Provider在操作系统级提供一个CSP,使用RSA公司的公钥加密算法,更多的CSP可以根据需要增加到应用中。
事实上,CSP有可能与特殊硬件设备(如智能卡)一起来进行数据加密。
CryptoAPI接口允许简单的函数调用来加密数据,交换公钥,散列一个消息来建立摘要以及生成数字签名。
它还提供高级的管理操作,如从一组可能的CSP中使用一个CSP。
此外,CryptoAPI还为许多高级安全性服务提供了基础,包括用于电子商务的SET,用于加密客户机/服务器消息的PCT,用于在各个平台之间来回传递机密数据和密钥的PFX,代码签名等等。
目前支持CryptoAPI的Windows系统有:Windows 95 OSR2、Windows NT SP3及后续版本、Windows 98、Windows 2000等。
实用密码库openssl简单编程实践
![实用密码库openssl简单编程实践](https://img.taocdn.com/s3/m/4b963508e418964bcf84b9d528ea81c758f52ee8.png)
实用密码库openssl简单编程实践一、概述在网络信息安全的应用中,密码学技术是一项非常重要的技术之一。
而openssl是一个强大的密码库,提供了多种密码学算法的实现,是开发安全网络应用的理想选择。
本文将介绍openssl密码库的简单编程实践,帮助读者快速上手使用openssl库进行密码学应用的开发。
二、概述openssl密码库1. openssl密码库简介openssl密码库是一个开源的密码库,提供了丰富的密码学算法的实现,包括对称加密算法、非对称加密算法、数字签名算法等。
它广泛应用于网络安全、加密通信、数字证书等领域,是当前最流行的密码库之一。
2. openssl密码库的特点- 开源免费:openssl密码库是一个开源项目,可以免费获取源代码进行使用和修改。
- 跨评台:openssl密码库支持多种操作系统,包括Linux、Windows、Mac等,可以在不同评台上进行应用开发。
- 支持多种密码算法:openssl密码库支持对称加密算法如AES、DES,非对称加密算法如RSA、ECC,以及数字签名算法如DSA、ECDSA 等,满足各种安全需求。
- 丰富的应用库:openssl密码库还提供了丰富的应用库,包括SSL/TLS协议实现、数字证书管理等,可以满足各种安全应用的开发需求。
三、openssl密码库的安装和配置1. openssl密码库的安装在Linux系统中,openssl密码库通常已经预装。
如果没有预装,可以通过包管理工具安装openssl库:```sudo apt-get install openssl```在Windows系统中,可以从openssl全球信息站下载对应版本的openssl库进行安装。
2. openssl密码库的配置安装完成后,需要配置openssl密码库的环境变量,以方便编程时调用openssl库中的函数和头文件。
在Linux系统中,可以将openssl库的路径添加到LD_LIBRARY_PATH环境变量中:```export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/ssl/lib```在Windows系统中,可以将openssl库的路径添加到环境变量中,在编译时指定openssl库的路径。
OpenSSL使用之API指南
![OpenSSL使用之API指南](https://img.taocdn.com/s3/m/c86bf804580102020740be1e650e52ea5518ce9e.png)
OpenSSL使用之API指南4算法编程APIOpenSSL中支持众多的密码算法,并提供了很好的封装和接口。
密码算法主要分为如下几类:对称算法、公钥算法、散列算法、随机数产生算法等。
OpenSSL的目标是实现安全协议。
其中相关协议和标准包括:SSL/TLS、PKCS#1、PCKS#10、X.509、PEM、OCSP等。
4.1对称算法接口OpenSSL中实现的对称算法太多,举三个例子:DES、AES、RC4。
4.1.1DESDES加密算法是分组算法。
DES的基本操作是把64比特明文在56比特密钥指引下加密成64比特密文。
在实际使用中把密钥看作64比特可以更方便。
DES(IN,KEY)= OUT4.1.1.1DES ECB模式在OpenSSL中ECB操作模式对应的函数是DES_ecb_encrypt(),该函数把一个8字节明文分组input加密成为一个8字节密文分组output。
参数中密钥结构ks是用函数DES_set_key()准备好的,而密钥key是用随机数算法产生的64个随机比特。
参数enc指示是加密还是解密。
该函数每次只加密一个分组,因此用来加密很多数据时不方便使用。
void DES_ecb_encrypt(const_DES_cblock *input,DES_cblock *output, DES_key_schedule *ks,int enc);int DES_set_key(const_DES_cblock *key,DES_key_schedule *schedule);4.1.1.2DES CBC模式DES算法CBC操作模式加解密函数是DES_ncbc_encrypt()。
参数length指示输入字节长度。
如果长度不是8字节的倍数,则会被用0填充到8字节倍数。
因此,输出可能比length长,而且必然是8字节的倍数。
void DES_ncbc_encrypt(const unsigned char *input,unsigned char *output, long length, DES_key_schedule *schedule, DES_cblock *ivec, int enc);4.1.1.3DES CFB模式DES算法CFB操作模式加解密函数是DES_cfb_encrypt()。
opensslAES加密算法API的使用示例
![opensslAES加密算法API的使用示例](https://img.taocdn.com/s3/m/93d452ae294ac850ad02de80d4d8d15abe230073.png)
opensslAES加密算法API的使⽤⽰例openssl为⽤户提供了丰富的指令,同时也提供了供编程调⽤的API,本⽂以使⽤128位aes算法的ecb模式进⾏加密和解密验证,如下所⽰第⼀种⽅法,直接使⽤aes算法提供的api进⾏调⽤,代码如下#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <openssl/aes.h>int main(void){char userkey[AES_BLOCK_SIZE];unsigned char *date = malloc(AES_BLOCK_SIZE*3);unsigned char *encrypt = malloc(AES_BLOCK_SIZE*3 + 4);unsigned char *plain = malloc(AES_BLOCK_SIZE*3);AES_KEY key;memset((void*)userkey, 'k', AES_BLOCK_SIZE);memset((void*)date, 'p', AES_BLOCK_SIZE*3);memset((void*)encrypt, 0, AES_BLOCK_SIZE*6);memset((void*)plain, 0, AES_BLOCK_SIZE*3);/*设置加密key及密钥长度*/AES_set_encrypt_key(userkey, AES_BLOCK_SIZE*8, &key);int len = 0;/*循环加密,每次只能加密AES_BLOCK_SIZE长度的数据*/while(len < AES_BLOCK_SIZE*3) {AES_encrypt(date+len, encrypt+len, &key);len += AES_BLOCK_SIZE;}/*设置解密key及密钥长度*/AES_set_decrypt_key(userkey, AES_BLOCK_SIZE*8, &key);len = 0;/*循环解密*/while(len < AES_BLOCK_SIZE*3) {AES_decrypt(encrypt+len, plain+len, &key);len += AES_BLOCK_SIZE;}/*解密后与原数据是否⼀致*/if(!memcmp(plain, date, AES_BLOCK_SIZE*3)){printf("test success\n");}else{printf("test failed\n");}printf("encrypt: ");int i = 0;for(i = 0; i < AES_BLOCK_SIZE*3 + 4; i++){printf("%.2x ", encrypt[i]);if((i+1) % 32 == 0){printf("\n");}}printf("\n");return0;}编译执⾏结果如下xlzh@cmos:~/cmos/openssl-code/aes$ gcc aes.c -o aes.out -lssl -lcryptoxlzh@cmos:~/cmos/openssl-code/aes$ ./aes.outtest successencrypt: 08 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5308 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5308 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5300000000xlzh@cmos:~/cmos/openssl-code/aes$第⼆种⽅法,使⽤EVP框架,⽰例如下#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <openssl/evp.h>#include <openssl/aes.h>int main(void){char userkey[EVP_MAX_KEY_LENGTH];char iv[EVP_MAX_IV_LENGTH];unsigned char *date = malloc(AES_BLOCK_SIZE*3);unsigned char *encrypt = malloc(AES_BLOCK_SIZE*6);unsigned char *plain = malloc(AES_BLOCK_SIZE*6);EVP_CIPHER_CTX ctx;int ret;int tlen = 0;int mlen = 0;int flen = 0;memset((void*)userkey, 'k', EVP_MAX_KEY_LENGTH);memset((void*)iv, 'i', EVP_MAX_IV_LENGTH);memset((void*)date, 'p', AES_BLOCK_SIZE*3);memset((void*)encrypt, 0, AES_BLOCK_SIZE*6);memset((void*)plain, 0, AES_BLOCK_SIZE*6);/*初始化ctx*/EVP_CIPHER_CTX_init(&ctx);/*指定加密算法及key和iv(此处IV没有⽤)*/ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, userkey, iv);if(ret != 1) {printf("EVP_EncryptInit_ex failed\n");exit(-1);}/*禁⽤padding功能*/EVP_CIPHER_CTX_set_padding(&ctx, 0);/*进⾏加密操作*/ret = EVP_EncryptUpdate(&ctx, encrypt, &mlen, date, AES_BLOCK_SIZE*3); if(ret != 1) {printf("EVP_EncryptUpdate failed\n");exit(-1);}/*结束加密操作*/ret = EVP_EncryptFinal_ex(&ctx, encrypt+mlen, &flen);if(ret != 1) {printf("EVP_EncryptFinal_ex failed\n");exit(-1);}tlen = mlen + flen;tlen = 0;mlen = 0;flen = 0;EVP_CIPHER_CTX_cleanup(&ctx);EVP_CIPHER_CTX_init(&ctx);ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, userkey, iv);if(ret != 1) {printf("EVP_DecryptInit_ex failed\n");exit(-1);}EVP_CIPHER_CTX_set_padding(&ctx, 0);ret = EVP_DecryptUpdate(&ctx, plain, &mlen, encrypt, AES_BLOCK_SIZE*3); if(ret != 1) {printf("EVP_DecryptUpdate failed\n");exit(-1);}ret = EVP_DecryptFinal_ex(&ctx, plain+mlen, &flen);if(ret != 1) {printf("EVP_DecryptFinal_ex failed\n");exit(-1);}/*对⽐解密后与原数据是否⼀致*/if(!memcmp(plain, date, AES_BLOCK_SIZE*3)) {printf("test success\n");} else {printf("test failed\n");}printf("encrypt: ");int i;for(i = 0; i < AES_BLOCK_SIZE*3+4; i ++){printf("%.2x ", encrypt[i]);if((i+1)%32 == 0){printf("\n");}}printf("\n");return0;}编译执⾏结果如下:xlzh@cmos:~/cmos/openssl-code/aes$ gcc evp.c -o evp.out -lssl -lcryptoxlzh@cmos:~/cmos/openssl-code/aes$ ./evp.outtest successencrypt: 08 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5308 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5308 a9 74 4d b0 6657 1b 57 fe 60 3d 91 e4 ed 5300000000xlzh@cmos:~/cmos/openssl-code/aes$EVP框架是对openssl提供的所有算法进⾏了封装,在使⽤⼯程中只需要修改少量的代码就可以选择不同的加密算法,在⼯作中通常采⽤这种⽅式。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
使用OpenSSL API 进行安全编程创建基本的安全连接和非安全连接Kenneth Ballard (kenneth.ballard@), 自由程序员简介:学习如何使用 OpenSSL ——用于安全通信的最著名的开放库——的API 有些强人所难,因为其文档并不完全。
您可以通过本文中的提示补充这方面的知识,并驾驭该 API。
在建立基本的连接之后,就可以查看如何使用 OpenSSL 的 BIO 库来建立安全连接和非安全连接。
与此同时,您还会学到一些关于错误检测的知识。
OpenSSL API 的文档有些含糊不清。
因为还没有多少关于 OpenSSL 使用的教程,所以对初学者来说,在应用程序中使用它可能会有一些困难。
那么怎样才能使用 OpenSSL 实现一个基本的安全连接呢?本教程将帮助您解决这个问题。
学习如何实现 OpenSSL 的困难部分在于其文档的不完全。
不完全的 API 文档通常会妨碍开发人员使用该 API,而这通常意味着它注定要失败。
但 OpenSSL 仍然很活跃,而且正逐渐变得强大。
这是为什么?OpenSSL 是用于安全通信的最著名的开放库。
在 google 中搜索“SSL library”得到的返回结果中,列表最上方就是 OpenSSL。
它诞生于 1998 年,源自 Eric Young 和 Tim Hudson 开发的 SSLeay 库。
其他 SSL 工具包包括遵循 GNU General Public License 发行的 GNU TLS,以及 Mozilla Network Security Services(NSS)(请参阅本文后面的参考资料,以获得其他信息)。
那么,是什么使得 OpenSSL 比 GNU TLS、Mozilla NSS 或其他所有的库都优越呢?许可是一方面因素(请参阅参考资料)。
此外,GNS TLS(迄今为止)只支持 TLS v1.0 和 SSL v3.0 协议,仅此而已。
Mozilla NSS 的发行既遵循 Mozilla Public License 又遵循 GNU GPL,它允许开发人员进行选择。
不过,Mozilla NSS 比 OpenSSL 大,并且需要其他外部库来对库进行编译,而 OpenSSL 是完全自包含的。
与 OpenSSL 相同,大部分 NSS API 也没有文档资料。
Mozilla NSS 获得了 PKCS #11 支持,该支持可以用于诸如智能卡这样的加密标志。
OpenSSL 就不具备这一支持。
先决条件要充分理解并利用本文,您应该:•精通 C 编程。
•熟悉 Internet 通信和支持 Internet 的应用程序的编写。
并不绝对要求您熟悉 SSL ,因为稍后将给出对 SLL 的简短说明;不过,如果您希望得到详细论述 SSL 的文章的链接,请参阅参考资料部分。
拥有密码学方面的知识固然好,但这并不是必需的。
什么是 SSL?SSL 是一个缩写,代表的是 Secure Sockets Layer。
它是支持在 Internet 上进行安全通信的标准,并且将数据密码术集成到了协议之中。
数据在离开您的计算机之前就已经被加密,然后只有到达它预定的目标后才被解密。
证书和密码学算法支持了这一切的运转,使用 OpenSSL,您将有机会切身体会它们。
理论上,如果加密的数据在到达目标之前被截取或窃听,那些数据是不可能被破解的。
不过,由于计算机的变化一年比一年快,而且密码翻译方法有了新的发展,因此,SSL 中使用的加密协议被破解的可能性也在增大。
可以将 SSL 和安全连接用于 Internet 上任何类型的协议,不管是 HTTP、POP3,还是 FTP。
还可以用 SSL 来保护 Telnet 会话。
虽然可以用 SSL 保护任何连接,但是不必对每一类连接都使用 SSL。
如果连接传输敏感信息,则应使用 SSL。
什么是 OpenSSL?OpenSSL 不仅仅是 SSL。
它可以实现消息摘要、文件的加密和解密、数字证书、数字签名和随机数字。
关于 OpenSSL 库的内容非常多,远不是一篇文章可以容纳的。
OpenSSL 不只是 API,它还是一个命令行工具。
命令行工具可以完成与 API 同样的工作,而且更进一步,可以测试 SSL 服务器和客户机。
它还让开发人员对OpenSSL 的能力有一个认识。
要获得关于如何使用 OpenSSL 命令行工具的资料,请参阅参考资料部分。
您需要什么首先需要的是最新版本的 OpenSSL。
查阅参考资料部分,以确定从哪里可以获得最新的可以自己编译的源代码,或者最新版本的二进制文件(如果您不希望花费时间来编译的话)。
不过,为了安全起见,我建议您下载最新的源代码并自己编译它。
二进制版本通常是由第三方而不是由 OpenSSL 的开发人员来编译和发行的。
一些 Linux 的发行版本附带了 OpenSSL 的二进制版本,对于学习如何使用OpenSSL 库来说,这足够了;不过,如果您打算去做一些实际的事情,那么一定要得到最新的版本,并保持该版本一直是最新的。
对于以 RPM 形式安装的 Linux 发行版本(Red Hat、Mandrake 等),建议您通过从发行版本制造商那里获得 RPM 程序包来更新您的 OpenSSL 发行版本。
出于安全方面的原因,建议您使用最新版本的发行版本。
如果您的发行版本不能使用最新版本的 OpenSSL,那么建议您只覆盖库文件,不要覆盖可执行文件。
OpenSSL 附带的 FAQ 文档中包含了有关这方面的细节。
还要注意的是,OpenSSL 并没有在所有的平台上都获得官方支持。
虽然制造商已经尽力使其能够跨平台兼容,但仍然存在 OpenSSL 不能用于您的计算机和/或操作系统的可能。
请参阅 OpenSSL 的 Web 站点(参考资料中的链接),以获得关于哪些平台可以得到支持的信息。
如果想使用 OpenSSL 来生成证书请求和数字证书,那么必须创建一个配置文件。
在 OpenSSL 程序包的 apps 文件夹中,有一个名为 f 的可用模板文件。
我不会对该文件进行讨论,因为这不在本文要求范围之内。
不过,该模板文件有一些非常好的注释,而且如果在 Internet 上搜索,您可以找到很多讨论修改该文件的教程。
头文件和初始化本教程所使用的头文件只有三个:ssl.h、bio.h 和 err.h。
它们都位于 openssl 子目录中,而且都是开发您的项目所必需的。
要初始化 OpenSSL 库,只需要三个代码行即可。
清单 1 中列出了所有内容。
其他的头文件和/或初始化函数可能是其他一些功能所必需的。
清单 1. 必需的头文件/* OpenSSL headers */#include "openssl/bio.h"#include "openssl/ssl.h"#include "openssl/err.h"/* Initializing OpenSSL */SSL_load_error_strings();ERR_load_BIO_strings();OpenSSL_add_all_algorithms();建立非安全连接不管连接是安全的还是不安全的,OpenSSL 都使用了一个名为 BIO 的抽象库来处理包括文件和套接字在内的各种类型的通信。
您还可以将 OpenSSL 设置成为一个过滤器,比如用于 UU 或 Base64 编码的过滤器。
在这里对 BIO 库进行全面说明有点麻烦,所以我将根据需要一点一点地介绍它。
首先,我将向您展示如何建立一个标准的套接字连接。
相对于使用 BSD 套接字库,该操作需要的代码行更少一些。
在建立连接(无论安全与否)之前,要创建一个指向 BIO 对象的指针。
这类似于在标准 C 中为文件流创建 FILE 指针。
清单 2. 指针BIO * bio;打开连接创建新的连接需要调用 BIO_new_connect 。
您可以在同一个调用中同时指定主机名和端口号。
也可以将其拆分为两个单独的调用:一个是创建连接并设置主机名的 BIO_new_connect 调用,另一个是设置端口号的 BIO_set_conn_port (或者 BIO_set_conn_int_port )调用。
不管怎样,一旦 BIO 的主机名和端口号都已指定,该指针会尝试打开连接。
没有什么可以影响它。
如果创建 BIO 对象时遇到问题,指针将会是 NULL。
为了确保连接成功,必须执行 BIO_do_connect 调用。
清单 3. 创建并打开连接bio = BIO_new_connect("hostname:port");if(bio == NULL){/* Handle the failure */}if(BIO_do_connect(bio) <= 0)/* Handle failed connection */}在这里,第一行代码使用指定的主机名和端口创建了一个新的 BIO 对象,并以所示风格对该对象进行格式化。
例如,如果您要连接到 的 80 端口,那么该字符串将是 :80 。
调用 BIO_do_connect 检查连接是否成功。
如果出错,则返回 0 或 -1。
与服务器进行通信不管 BIO 对象是套接字还是文件,对其进行的读和写操作都是通过以下两个函数来完成的: BIO_read 和 BIO_write 。
很简单,对吧?精彩之处就在于它始终如此。
BIO_read 将尝试从服务器读取一定数目的字节。
它返回读取的字节数、 0 或者-1。
在受阻塞的连接中,该函数返回 0,表示连接已经关闭,而 -1 则表示连接出现错误。
在非阻塞连接的情况下,返回 0 表示没有可以获得的数据,返回 -1 表示连接出错。
可以调用 BIO_should_retry 来确定是否可能重复出现该错误。
清单 4. 从连接读取int x = BIO_read(bio, buf, len);if(x == 0){/* Handle closed connection */}else if(x < 0){if(! BIO_should_retry(bio)){/* Handle failed read here */}/* Do something to handle the retry */}BIO_write 会试着将字节写入套接字。
它将返回实际写入的字节数、0 或者 -1。
同 BIO_read ,0 或 -1 不一定表示错误。
BIO_should_retry 是找出问题的途径。
如果需要重试写操作,它必须使用和前一次完全相同的参数。
清单 5. 写入到连接if(BIO_write(bio, buf, len) <= 0)if(! BIO_should_retry(bio)){/* Handle failed write here */}/* Do something to handle the retry */}关闭连接关闭连接也很简单。