用OpenSSL编写SSL,TLS程序

http://zhoulifa.bokee.com/6134045.html

http://blog.sina.com.cn/s/blog_86ca13bb0100vaph.html

http://blog.chinaunix.net/uid-26575352-id-3048856.html

一、简介:

SSL(SecureSocket Layer)是netscape公司提出的主要用于web的安全通信标准,分为2.0版和3.0版.TLS(TransportLayer Security)是IETF的TLS工作组在SSL3.0基础之上提出的安全通信标准,目前版本是1.0,即RFC2246.SSL/TLS提供的安全机制可以保证应用层数据在互联网络传输不 被监听,伪造和窜改.

openssl(www.openssl.org)是sslv2,sslv3,tlsv1的一份完整实现,内部包含了大量加密算法程序.其命令行提供了丰富的加密,验证,证书生成等功能,甚至可以用其建立一个完整的CA.与其同时,它也提供了一套完整的库函数,可用开发用SSL/TLS的通信程序.Apache的https两种版本mod_ssl和apachessl均基于它实现的.openssl继承于ssleay,并做了一定的扩展,当前的版本是0.9.5a.

openssl的缺点是文档太少,连一份完整的函数说明都没有,man page也至今没做完整:-(,如果想用它编程序,除了熟悉已有的文档(包括ssleay,mod_ssl,apachessl的文档)外,可以到它的maillist上找相关的帖子,许多问题可以在以前的文章中找到答案.

基于OpenSSL的程序都要遵循以下几个步骤:

(1 ) OpenSSL初始化

在使用OpenSSL之前,必须进行相应的协议初始化工作,这可以通过下面的函数实现:

int SSL_library_init(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,intmode,int(*verify_callback),int(X509_STORE_CTX *));

为SSL会话环境加载CA证书的函数是:

SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,constchar *Capath);

为SSL会话加载用户证书的函数是:

SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,inttype);

为SSL会话加载用户私钥的函数是:

SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,inttype);

在将证书和私钥加载到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验证过了。

X509_NAME *X509_get_subject_name(X509 *a);

该函数得到证书所用者的名字。

(6) 进行数据传输

当SSL握手完成之后,就可以进行安全的数据传输了,在数据传输阶段,需要使用SSL_read( )和SSL_write()来替代传统的read( )和write( )函数,来完成对套接字的读写操作:

int SSL_read(SSL *ssl,void *buf,int num);

int SSL_write(SSL *ssl,const void *buf,int num);

(7 ) 结束SSL通信

当客户端和服务器之间的数据通信完成之后,调用下面的函数来释放已经申请的SSL资源:

int SSL_shutdown(SSL *ssl);    //关闭SSL套接字

void SSl_free(SSL *ssl);    //释放SSL套接字


void SSL_CTX_free(SSL_CTX *ctx);     //释放SSL会话环境


客户端与服务端编程框架:

程序分为两部分,客户端和服务器端,我们的目的是利用SSL/TLS的特性保证通信双方能够互相验证对方身份(真实性),并保证数据的完整性, 私密性.


1.客户端程序的框架为:

/*生成一个SSL结构*/
meth = SSLv23_client_method();
ctx = SSL_CTX_new (meth);
ssl = SSL_new(ctx);

/*下面是正常的socket过程*/
fd = socket();
connect();

/*把建立好的socket和SSL结构联系起来*/
SSL_set_fd(ssl,fd);

/*SSL的握手过程*/
SSL_connect(ssl);

/*接下来用SSL_write(), SSL_read()代替原有的write(),read()即可*/
SSL_write(ssl,”Hello world”,strlen(“Hello World!”));

2.服务端程序的框架为:
/*生成一个SSL结构*/
meth = SSLv23_server_method();
ctx = SSL_CTX_new (meth);
ssl = SSL_new(ctx);

/*下面是正常的socket过程*/
fd = socket();
bind();
listen();
accept();

/*把建立好的socket和SSL结构联系起来*/
SSL_set_fd(ssl,fd);

/*SSL的握手过程*/
SSL_connect(ssl);

/*接下来用SSL_write(), SSL_read()代替原有的write(),read()即可*/
SSL_read (ssl, buf, sizeof(buf));

根据RFC2246(TLS1.0)整个TLS(SSL)的流程如下:

Client                                                Server

ClientHello           ——–>
                                                ServerHello
                                                 Certificate*
                                          ServerKeyExchange*
                                            CertificateRequest*
                           <——–      ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished               ——–>
                                             [ChangeCipherSpec]
                            <——–             Finished
Application Data   <——->     Application Data

对程序来说,openssl将整个握手过程用一对函数体现,即客户端的SSL_connect和服务端的SSL_accept.而后的应用层数据交换则用SSL_read和 SSL_write来完成.

二、证书文件生成

除将程序编译成功外,还需生成必要的证书和私钥文件使双方能够成功验证对方,步骤如下:

1.首先要生成服务器端的私钥(key文件):
openssl genrsa -des3 -out server.key 1024
运行时会提示输入密码,此密码用于加密key文件(参数des3便是指加密算法,当然也可以选用其他你认为安全的算法.),以后每当需读取此文 件(通过openssl提供的命令或API)都需输入口令.如果觉得不方便,也可以去除这个口令,但一定要采取其他的保护措施!
去除key文件口令的命令:
openssl rsa -in server.key -out server.key

2.openssl req -new -key server.key -out server.csr
生成Certificate Signing Request(CSR),生成的csr文件交给CA签名后形成服务端自己的证书.屏幕上将有提示,依照其指示一步一步输入要 求的个人信息即可.

3.对客户端也作同样的命令生成key及csr文件:
openssl genrsa -des3 -out client.key 1024
openssl req -new -key client.key -out client.csr

4.CSR文件必须有CA的签名才可形成证书.可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢.
首先生成CA的key文件:
openssl -des3 -out ca.key 1024
在生成CA自签名的证书:
openssl req -new -x509 -key ca.key -out ca.crt
如果想让此证书有个期限,如一年,则加上”-days 365”.
(“如果非要为这个证书加上一个期限,我情愿是..一万年”)

5.用生成的CA的证书为刚才生成的server.csr,client.csr文件签名:
可以用openssl中CA系列命令,但不是很好用(也不是多难,唉,一言难尽),一篇文章中推荐用mod_ssl中的sign.sh脚本,试了一下,确实方便了不 少,如果ca.csr存在的话,只需:
./sigh.sh server.csr
./sign.sh client.csr
相应的证书便生成了(后缀.crt).

现在我们所需的全部文件便生成了.

其实openssl中还附带了一个叫CA.pl的文件(在安装目录中的misc子目录下),可用其生成以上的文件,使用也比较方便,但此处就不作介绍了.

三、需要了解的一些函数:

1.int    SSL_CTX_set_cipher_list(SSL_CTX *,const char *str);

SSL_CTX_set_cipher_list() sets the list of available ciphers for ctx using the control string str. The format of the string is described in ciphers. The list of ciphers is inherited by all ssl objects created from ctx.
根据SSL/TLS规范,在ClientHello中,客户端会提交一份自己能够支持的加密方法的列表,由服务端选择一种方法后在ServerHello中通知客户端, 从而完成加密算法的协商.

如果服务端只设置了一种加密套件,那么客户端要么接受要么返回错误。加密套件的选择是由服务端做出的。

可用的算法为:
EDH-RSA-DES-CBC3-SHA
EDH-DSS-DES-CBC3-SHA
DES-CBC3-SHA
DHE-DSS-RC4-SHA
IDEA-CBC-SHA
RC4-SHA
RC4-MD5
EXP1024-DHE-DSS-RC4-SHA
EXP1024-RC4-SHA
EXP1024-DHE-DSS-DES-CBC-SHA
EXP1024-DES-CBC-SHA
EXP1024-RC2-CBC-MD5
EXP1024-RC4-MD5
EDH-RSA-DES-CBC-SHA
EDH-DSS-DES-CBC-SHA
DES-CBC-SHA
EXP-EDH-RSA-DES-CBC-SHA
EXP-EDH-DSS-DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC4-MD5


下面的openssl命令可以列出支持的所有算法,并按强度排序:

# openssl ciphers -v ‘ALL:!ADH:@STRENGTH’
DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
DHE-DSS-AES256-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(256)  Mac=SHA1
AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
EDH-RSA-DES-CBC3-SHA    SSLv3 Kx=DH       Au=RSA  Enc=3DES(168) Mac=SHA1
EDH-DSS-DES-CBC3-SHA    SSLv3 Kx=DH       Au=DSS  Enc=3DES(168) Mac=SHA1
DES-CBC3-SHA            SSLv3 Kx=RSA      Au=RSA  Enc=3DES(168) Mac=SHA1
DES-CBC3-MD5            SSLv2 Kx=RSA      Au=RSA  Enc=3DES(168) Mac=MD5
DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-DSS-AES128-SHA      SSLv3 Kx=DH       Au=DSS  Enc=AES(128)  Mac=SHA1
AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-DSS-RC4-SHA         SSLv3 Kx=DH       Au=DSS  Enc=RC4(128)  Mac=SHA1
RC4-SHA                 SSLv3 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=SHA1
RC4-MD5                 SSLv3 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=MD5
RC2-CBC-MD5             SSLv2 Kx=RSA      Au=RSA  Enc=RC2(128)  Mac=MD5
RC4-MD5                 SSLv2 Kx=RSA      Au=RSA  Enc=RC4(128)  Mac=MD5
RC4-64-MD5              SSLv2 Kx=RSA      Au=RSA  Enc=RC4(64)   Mac=MD5
EXP1024-DHE-DSS-RC4-SHA SSLv3 Kx=DH(1024) Au=DSS  Enc=RC4(56)   Mac=SHA1 export
EXP1024-RC4-SHA         SSLv3 Kx=RSA(1024) Au=RSA  Enc=RC4(56)   Mac=SHA1 export
EXP1024-DHE-DSS-DES-CBC-SHA SSLv3 Kx=DH(1024) Au=DSS  Enc=DES(56)   Mac=SHA1 export
EXP1024-DES-CBC-SHA     SSLv3 Kx=RSA(1024) Au=RSA  Enc=DES(56)   Mac=SHA1 export
EXP1024-RC2-CBC-MD5     SSLv3 Kx=RSA(1024) Au=RSA  Enc=RC2(56)   Mac=MD5  export
EXP1024-RC4-MD5         SSLv3 Kx=RSA(1024) Au=RSA  Enc=RC4(56)   Mac=MD5  export
EDH-RSA-DES-CBC-SHA     SSLv3 Kx=DH       Au=RSA  Enc=DES(56)   Mac=SHA1
EDH-DSS-DES-CBC-SHA     SSLv3 Kx=DH       Au=DSS  Enc=DES(56)   Mac=SHA1
DES-CBC-SHA             SSLv3 Kx=RSA      Au=RSA  Enc=DES(56)   Mac=SHA1
DES-CBC-MD5             SSLv2 Kx=RSA      Au=RSA  Enc=DES(56)   Mac=MD5
EXP-EDH-RSA-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-EDH-DSS-DES-CBC-SHA SSLv3 Kx=DH(512)  Au=DSS  Enc=DES(40)   Mac=SHA1 export
EXP-DES-CBC-SHA         SSLv3 Kx=RSA(512) Au=RSA  Enc=DES(40)   Mac=SHA1 export
EXP-RC2-CBC-MD5         SSLv3 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv3 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export
EXP-RC2-CBC-MD5         SSLv2 Kx=RSA(512) Au=RSA  Enc=RC2(40)   Mac=MD5  export
EXP-RC4-MD5             SSLv2 Kx=RSA(512) Au=RSA  Enc=RC4(40)   Mac=MD5  export


这些算法按一定优先级排列,如果不作任何指定,将选用DES-CBC3-SHA.用SSL_CTX_set_cipher_list可以指定自己希望用的算法(实际上只是 提高其优先级,是否能使用还要看对方是否支持).

我们在程序中选用了RC4做加密,MD5做消息摘要(先进行MD5运算,后进行RC4加密).即
SSL_CTX_set_cipher_list(ctx,”RC4-MD5”);

在消息传输过程中采用对称加密(比公钥加密在速度上有极大的提高),其所用秘钥(shared secret)在握手过程中中协商(每次对话过程均不同, 在一次对话中都有可能有几次改变),并通过公钥加密的手段由客户端提交服务端.


SSL_set_cipher_list

int SSL_set_cipher_list(SSL *ssl, const char *str);

SSL_set_cipher_list() sets the list of ciphers only for ssl.

RETURN VALUES

SSL_CTX_set_cipher_list() and SSL_set_cipher_list() return 1 if any cipher could be selected and 0 on complete failure.


2.void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int (*callback)(int, X509_STORE_CTX *));
缺省mode是SSL_VERIFY_NONE,如果想要验证对方的话,便要将此项变成SSL_VERIFY_PEER.SSL/TLS中缺省只验证server,如果没有设置 SSL_VERIFY_PEER的话,客户端连证书都不会发过来.

3.int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,const char *CApath);
要验证对方的话,当然装要有CA的证书了,此函数用来便是加载CA的证书文件的.

4.int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
加载自己的证书文件.

5.int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
加载自己的私钥,以用于签名.

6.int SSL_CTX_check_private_key(SSL_CTX *ctx);
调用了以上两个函数后,自己检验一下证书与私钥是否配对.
7.void RAND_seed(const void *buf,int num);
在win32的环境中client程序运行时出错(SSL_connect返回-1)的一个主要机制便是与UNIX平台下的随机数生成机制不同(握手的时候用的到).具体描述可见mod_ssl的FAQ.解决办法就是调用此函数,其中buf应该为一随机的字符串,作为”seed”.
还可以采用一下两个函数:
void RAND_screen(void);
int RAND_event(UINT, WPARAM, LPARAM);
其中RAND_screen()以屏幕内容作为”seed”产生随机数,RAND_event可以捕获windows中的事件(event),以此为基础产生随机数.如果一直有 用户干预的话,用这种办法产生的随机数能够”更加随机”,但如果机器一直没人理(如总停在登录画面),则每次都将产生同样的数字.

这几个函数都只在WIN32环境下编译时有用,各种UNIX下就不必调了.
大量其他的相关函数原型,见crypto\rand\rand.h.

8.OpenSSL_add_ssl_algorithms()或SSLeay_add_ssl_algorithms()
其实都是调用int SSL_library_init(void)
进行一些必要的初始化工作,用openssl编写SSL/TLS程序的话第一句便应是它.

9.void    SSL_load_error_strings(void );
如果想打印出一些方便阅读的调试信息的话,便要在一开始调用此函数.

10.void ERR_print_errors_fp(FILE *fp);
如果调用了SSL_load_error_strings()后,便可以随时用ERR_print_errors_fp()来打印错误信息了.

11.X509 *SSL_get_peer_certificate(SSL *s);
握手完成后,便可以用此函数从SSL结构中提取出对方的证书(此时证书得到且已经验证过了)整理成X509结构.

12.X509_NAME *X509_get_subject_name(X509 *a);
得到证书所有者的名字,参数可用通过SSL_get_peer_certificate()得到的X509对象.

13.X509_NAME *X509_get_issuer_name(X509 *a)
得到证书签署者(往往是CA)的名字,参数可用通过SSL_get_peer_certificate()得到的X509对象.

14.char *X509_NAME_oneline(X509_NAME *a,char *buf,int size);
将以上两个函数得到的对象变成字符型,以便打印出来.

15.SSL_METHOD的构造函数,包括
SSL_METHOD *TLSv1_server_method(void);    /* TLSv1.0 */
SSL_METHOD *TLSv1_client_method(void);    /* TLSv1.0 */

SSL_METHOD *SSLv2_server_method(void);    /* SSLv2 */
SSL_METHOD *SSLv2_client_method(void);    /* SSLv2 */

SSL_METHOD *SSLv3_server_method(void);    /* SSLv3 */
SSL_METHOD *SSLv3_client_method(void);    /* SSLv3 */

SSL_METHOD *SSLv23_server_method(void);    /* SSLv3 but can rollback to v2 */
SSL_METHOD *SSLv23_client_method(void);    /* SSLv3 but can rollback to v2 */
在程序中究竟采用哪一种协议(TLSv1/SSLv2/SSLv3),就看调哪一组构造函数了.



四:程序源代码:


服务器端:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <sys/types.h>
  6. #include <netinet/in.h>
  7. #include <sys/socket.h>
  8. #include <sys/wait.h>
  9. #include <unistd.h>
  10. #include <arpa/inet.h>
  11. #include <openssl/ssl.h>
  12. #include <openssl/err.h>
  13. #define MAXBUF 1024
  14. /************关于本文档********************************************
  15. *filename: ssl-server.c
  16. *purpose: 演示利用 OpenSSL 库进行基于 IP层的 SSL 加密通讯的方法,这是服务器端例子
  17. *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  18. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  19. *date time:2007-02-02 19:40
  20. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  21. * 但请遵循GPL
  22. *Thanks to:Google
  23. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  24. * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
  25. *********************************************************************/
  26. int main(int argc, char **argv)
  27. {
  28. int sockfd, new_fd;
  29. socklen_t len;
  30. struct sockaddr_in my_addr, their_addr;
  31. unsigned int myport, lisnum;
  32. char buf[MAXBUF + 1];
  33. SSL_CTX *ctx;
  34. if (argv[1])
  35. myport = atoi(argv[1]);
  36. else
  37. myport = 7838;
  38. if (argv[2])
  39. lisnum = atoi(argv[2]);
  40. else
  41. lisnum = 2;
  42. /* SSL 库初始化 */
  43. SSL_library_init();
  44. /* 载入所有 SSL 算法 */
  45. OpenSSL_add_all_algorithms();
  46. /* 载入所有 SSL 错误消息 */
  47. SSL_load_error_strings();
  48. /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */
  49. ctx = SSL_CTX_new(SSLv23_server_method());
  50. /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */
  51. if (ctx == NULL) {
  52. ERR_print_errors_fp(stdout);
  53. exit(1);
  54. }
  55. /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
  56. if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) {
  57. ERR_print_errors_fp(stdout);
  58. exit(1);
  59. }
  60. /* 载入用户私钥 */
  61. if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) {
  62. ERR_print_errors_fp(stdout);
  63. exit(1);
  64. }
  65. /* 检查用户私钥是否正确 */
  66. if (!SSL_CTX_check_private_key(ctx)) {
  67. ERR_print_errors_fp(stdout);
  68. exit(1);
  69. }
  70. /* 开启一个 socket 监听 */
  71. if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  72. perror("socket");
  73. exit(1);
  74. } else
  75. printf("socket created\n");
  76. bzero(&my_addr, sizeof(my_addr));
  77. my_addr.sin_family = PF_INET;
  78. my_addr.sin_port = htons(myport);
  79. if (argv[3])
  80. my_addr.sin_addr.s_addr = inet_addr(argv[3]);
  81. else
  82. my_addr.sin_addr.s_addr = INADDR_ANY;
  83. if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
  84. == -1) {
  85. perror("bind");
  86. exit(1);
  87. } else
  88. printf("binded\n");
  89. if (listen(sockfd, lisnum) == -1) {
  90. perror("listen");
  91. exit(1);
  92. } else
  93. printf("begin listen\n");
  94. while (1) {
  95. SSL *ssl;
  96. len = sizeof(struct sockaddr);
  97. /* 等待客户端连上来 */
  98. if ((new_fd =
  99. accept(sockfd, (struct sockaddr *) &their_addr,
  100. &len)) == -1) {
  101. perror("accept");
  102. exit(errno);
  103. } else
  104. printf("server: got connection from %s, port %d, socket %d\n",
  105. inet_ntoa(their_addr.sin_addr),
  106. ntohs(their_addr.sin_port), new_fd);
  107. /* 基于 ctx 产生一个新的 SSL */
  108. ssl = SSL_new(ctx);
  109. /* 将连接用户的 socket 加入到 SSL */
  110. SSL_set_fd(ssl, new_fd);
  111. /* 建立 SSL 连接 */
  112. if (SSL_accept(ssl) == -1) {
  113. perror("accept");
  114. close(new_fd);
  115. break;
  116. }
  117. /* 开始处理每个新连接上的数据收发 */
  118. bzero(buf, MAXBUF + 1);
  119. strcpy(buf, "server->client");
  120. /* 发消息给客户端 */
  121. len = SSL_write(ssl, buf, strlen(buf));
  122. if (len <= 0) {
  123. printf
  124. ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
  125. buf, errno, strerror(errno));
  126. goto finish;
  127. } else
  128. printf("消息'%s'发送成功,共发送了%d个字节!\n",
  129. buf, len);
  130. bzero(buf, MAXBUF + 1);
  131. /* 接收客户端的消息 */
  132. len = SSL_read(ssl, buf, MAXBUF);
  133. if (len > 0)
  134. printf("接收消息成功:'%s',共%d个字节的数据\n",
  135. buf, len);
  136. else
  137. printf
  138. ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
  139. errno, strerror(errno));
  140. /* 处理每个新连接上的数据收发结束 */
  141. finish:
  142. /* 关闭 SSL 连接 */
  143. SSL_shutdown(ssl);
  144. /* 释放 SSL */
  145. SSL_free(ssl);
  146. /* 关闭 socket */
  147. close(new_fd);
  148. }
  149. /* 关闭监听的 socket */
  150. close(sockfd);
  151. /* 释放 CTX */
  152. SSL_CTX_free(ctx);
  153. return 0;
  154. }

客户端:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <sys/socket.h>
  5. #include <resolv.h>
  6. #include <stdlib.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <unistd.h>
  10. #include <openssl/ssl.h>
  11. #include <openssl/err.h>
  12. #define MAXBUF 1024
  13. void ShowCerts(SSL * ssl)
  14. {
  15. X509 cert;
  16. char *line;
  17. cert = SSL_get_peer_certificate(ssl);
  18. if (cert != NULL) {
  19. printf("数字证书信息:\n");
  20. line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
  21. printf("证书: %s\n", line);
  22. free(line);
  23. line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
  24. printf("颁发者: %s\n", line);
  25. free(line);
  26. X509_free(cert);
  27. } else
  28. printf("无证书信息!\n");
  29. }
  30. /***关于本文档**********************************
  31. filename: ssl-client.c
  32. *purpose: 演示利用 OpenSSL 库进行基于 IP层的 SSL 加密通讯的方法,这是客户端例子
  33. *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  34. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  35. *date time:2007-02-02 20:10
  36. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  37. 但请遵循GPL
  38. Thanks to:Google
  39. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  40. 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
  41. ***********************************************************/
  42. int main(int argc, char *argv)
  43. {
  44. int sockfd, len;
  45. struct sockaddr_in dest;
  46. char buffer[MAXBUF + 1];
  47. SSL_CTX *ctx;
  48. SSL *ssl;
  49. if (argc != 3) {
  50. printf
  51. ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
  52. argv[0], argv[0]);
  53. exit(0);
  54. }
  55. / SSL 库初始化,参看 ssl-server.c 代码 /
  56. SSL_library_init();
  57. OpenSSL_add_all_algorithms();
  58. SSL_load_error_strings();
  59. ctx = SSL_CTX_new(SSLv23_client_method());
  60. if (ctx == NULL) {
  61. ERR_print_errors_fp(stdout);
  62. exit(1);
  63. }
  64. / 创建一个 socket 用于 tcp 通信 /
  65. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  66. perror("Socket");
  67. exit(errno);
  68. }
  69. printf("socket created\n");
  70. / 初始化服务器端(对方)的地址和端口信息 /
  71. bzero(&dest, sizeof(dest));
  72. dest.sin_family = AF_INET;
  73. dest.sin_port = htons(atoi(argv[2]));
  74. if (inet_aton(argv[1], (struct in_addr ) &dest.sin_addr.s_addr) == 0) {
  75. perror(argv[1]);
  76. exit(errno);
  77. }
  78. printf("address created\n");
  79. /* 连接服务器 /
  80. if (connect(sockfd, (struct sockaddr ) &dest, sizeof(dest)) != 0) {
  81. perror("Connect ");
  82. exit(errno);
  83. }
  84. printf("server connected\n");
  85. /* 基于 ctx 产生一个新的 SSL /
  86. ssl = SSL_new(ctx);
  87. SSL_set_fd(ssl, sockfd);
  88. / 建立 SSL 连接 /
  89. if (SSL_connect(ssl) == -1)
  90. ERR_print_errors_fp(stderr);
  91. else {
  92. printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
  93. ShowCerts(ssl);
  94. }
  95. / 接收对方发过来的消息,最多接收 MAXBUF 个字节 /
  96. bzero(buffer, MAXBUF + 1);
  97. / 接收服务器来的消息 /
  98. len = SSL_read(ssl, buffer, MAXBUF);
  99. if (len > 0)
  100. printf("接收消息成功:'%s',共%d个字节的数据\n",
  101. buffer, len);
  102. else {
  103. printf
  104. ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
  105. errno, strerror(errno));
  106. goto finish;
  107. }
  108. bzero(buffer, MAXBUF + 1);
  109. strcpy(buffer, "from client->server");
  110. / 发消息给服务器 /
  111. len = SSL_write(ssl, buffer, strlen(buffer));
  112. if (len < 0)
  113. printf
  114. ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
  115. buffer, errno, strerror(errno));
  116. else
  117. printf("消息'%s'发送成功,共发送了%d个字节!\n",
  118. buffer, len);
  119. finish:
  120. / 关闭连接 */
  121. SSL_shutdown(ssl);
  122. SSL_free(ssl);
  123. close(sockfd);
  124. SSL_CTX_free(ctx);
  125. return 0;
  126. }

编译程序用下列命令:
gcc -Wall ssl-client.c -o client -L /usr/local/openssl/lib/ -Wl,-R /usr/local/openssl/lib/ -lssl -lcrypto
gcc -Wall ssl-server.c -o server -L /usr/local/openssl/lib/ -Wl,-R /usr/local/openssl/lib/ -lssl -lcrypto
运行程序用如下命令:
./server 7838 1 127.0.0.1 cacert.pem privkey.pem
./client 127.0.0.1 7838

用下面这两个命令产生上述cacert.pem和privkey.pem文件:
openssl genrsa -out privkey.pem 2048
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/440335.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信息技术计算机伦理与安全教案,龙教版信息技术七年级下册第7课 安全与道德 教案...

ID:9954219分类&#xff1a;全国,2019资源大小&#xff1a;228KB资料简介:题 目第七课 安全与道德总课时1设计来源自我设计教学时间教材分析这节课计算机与网络安全部分定义介绍和叙述较多,所以为了避免枯燥可以设计课件和并准备病毒计算机安全报道的视频、多媒体讲解、图片等…

【HDU - 5706】GirlCat(bfs)

题干&#xff1a; As a cute girl, Kotori likes playing Hide and Seek with cats particularly. Under the influence of Kotori, many girls and cats are playing Hide and Seek together. Koroti shots a photo. The size of this photo is nmnm, each pixel of the ph…

8.Using Categorical Data with One Hot Encoding

本教程是机器学习系列的一部分。 在此步骤中&#xff0c;您将了解“分类”变量是什么&#xff0c;以及处理此类数据的最常用方法。 Introduction 分类数据是仅采用有限数量值的数据。 例如&#xff0c;如果人们回答一项关于他们拥有哪种品牌汽车的调查&#xff0c;结果将是明…

iPhone换屏幕测试软件,怎样检验iPhone是否更换过屏幕?

原标题&#xff1a;怎样检验iPhone是否更换过屏幕&#xff1f;关注下图公众号&#xff0c;鉴定苹果手机真假↓↓↓购买新手机时&#xff0c;到手后会想手机各零部件是否是正品原装&#xff0c;就好比屏幕是否原装屏&#xff01;入手一部iPhone新机的时候&#xff0c;该如何检验…

*【HDU - 5707】Combine String(dp)

题干&#xff1a; Given three strings aa, bb and cc, your mission is to check whether cc is the combine string of aa and bb. A string cc is said to be the combine string of aa and bb if and only if cc can be broken into two subsequences, when you read the…

《TCP/IP详解》学习笔记(一):基本概念

为什么会有TCP/IP协议 在世界上各地&#xff0c;各种各样的电脑运行着各自不同的操作系统为大家服务&#xff0c;这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱 了各地人的口音&#xff0c;让他们无法合作一样。计算机使用者意识到&#xff0c;…

【POJ - 3272】Cow Traffic(dp,建反向图,DAG拓扑图)

题干&#xff1a; The bovine population boom down on the farm has caused serious congestion on the cow trails leading to the barn. Farmer John has decided to conduct a study to find the bottlenecks in order to relieve the traffic jams at milking time. The…

pc服务器不同型号,服务器与PC系统软件之不同

服务器与PC系统软件之不同对于中关村在线的网友来说&#xff0c;PC系统应该都不陌生&#xff0c;而且分分钟重装的水准。但在笔者过往的服务器装机经验中&#xff0c;可谓是一部千年血泪史。服务器和PC系统差别还是很大的。现在的PC系统多是windows7和windows10&#xff0c;而在…

9.XGBoost

本教程是机器学习系列的一部分。 在此步骤中&#xff0c;您将学习如何使用功能强大的xgboost库构建和优化模型。 What is XGBoost XGBoost是处理标准表格数据的领先模型&#xff08;您在Pandas DataFrames中存储的数据类型&#xff0c;而不是像图像和视频这样的更奇特的数据类…

*【HDU - 5711】Ingress(tsp旅行商问题,优先队列贪心,状压dp,floyd最短路,图论)

题干&#xff1a; Brickgao, who profited from your accurate calculating last year, made a great deal of money by moving bricks. Now he became gay shy fool again and recently he bought an iphone and was deeply addicted into a cellphone game called Ingress. …

ajax get请求成功,成功()函数的AJAX GET请求

后不叫我有一个jQuery的AJAX脚本像下面&#xff1a;成功()函数的AJAX GET请求function FillCity() {var stateID $("#ddlState").val();$.ajax({url: Url.Action("Employee", "Index"),type: "GET",dataType: "json",data:…

《TCP/IP详解》学习笔记(二):数据链路层

数据链路层有三个目的&#xff1a; 为IP模块发送和 接收IP数据报。为ARP模块发送ARP请求和接收ARP应答。为RARP发送RARP请 求和接收RARP应答ip大家都听说过。至于ARP和RARP&#xff0c;ARP叫做地址解析协议&#xff0c;是用IP地址换MAC地址的一种协议&#xff0c;而RARP则叫…

【POJ - 2762】Going from u to v or from v to u?(Tarjan缩点,树形dp 或 拓扑排序,欧拉图相关)

题干&#xff1a; In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the o…

《TCP/IP详解》学习笔记(三):IP协议、ARP协议

把这三个协议放到一起学习是因为这三个协议处于同一层,ARP 协议用来找到目标主机的 Ethernet 网卡 Mac 地址,IP 则承载要发 送的消息。数据链路层可以从 ARP 得到数据的传送信息,而从 IP 得到要传输的数据信息。 IP 协议 IP 协议是 TCP/IP 协议的核心,所有的 TCP,UDP,IMCP,IGCP…

光与夜之恋服务器维护中,光与夜之恋7月16日停服维护说明 维护详情一览

光与夜之恋7月16日停服维护说明维护详情一览。光与夜之恋7月16日停服维护更新了哪些内容?我们去了解一下。【7月16日停服维护说明】亲爱的设计师&#xff1a;为了给设计师们提供更好的游戏体验&#xff0c;光启市将于7月16日(周五)00:00进行预计5小时的停服维护&#xff0c;可…

10.Partial Dependence Plots

本教程是ML系列的一部分。 在此步骤中&#xff0c;您将学习如何创建和解释部分依赖图&#xff0c;这是从模型中提取洞察力的最有价值的方法之一。 What Are Partial Dependence Plots 有人抱怨机器学习模型是黑盒子。这些人会争辩说我们无法看到这些模型如何处理任何给定的数据…

springboot监控服务器信息,面试官:聊一聊SpringBoot服务监控机制

目录前言任何一个服务如果没有监控&#xff0c;那就是两眼一抹黑&#xff0c;无法知道当前服务的运行情况&#xff0c;也就无法对可能出现的异常状况进行很好的处理&#xff0c;所以对任意一个服务来说&#xff0c;监控都是必不可少的。就目前而言&#xff0c;大部分微服务应用…

0.《Apollo自动驾驶工程师技能图谱》

【新年礼物】开工第一天&#xff0c;送你一份自动驾驶工程师技能图谱&#xff01; 布道团队 Apollo开发者社区 1月 2日 AI时代到来&#xff0c;人才的缺乏是阻碍行业大步发展的主要因素之一。Apollo平台发布以来&#xff0c;我们接触到非常多的开发者他们并不是专业自动驾驶领…

【HDU - 1116】【POJ - 1386】Play on Words(判断半欧拉图,欧拉通路)

题干&#xff1a; Some of the secret doors contain a very interesting word puzzle. The team of archaeologists has to solve it to open that doors. Because there is no other way to open the doors, the puzzle is very important for us. There is a large number…

11.Pipelines

本教程是ML系列的一部分。 在此步骤中&#xff0c;您将了解如何以及为何使用管道清理建模代码。 What Are Pipelines 管道是保持数据处理和建模代码有序的简单方法。 具体来说&#xff0c;管道捆绑了预处理和建模步骤&#xff0c;因此您可以像使用单个包一样使用整个捆绑包。…