Openssl如何实现双向认证

  介绍

这篇文章主要讲解了Openssl如何实现双向认证,内容清晰明了,对此有兴趣的小伙伴可以学习一下,相信大家阅读完之后会有帮助。

<强>一、背景说明

1.1面临问题

最近一份产品检测报告建议使用基于pki的认证方式,由于产品已实现https,商量之下认为其意思是使用双向认证以处理中间人形式攻击。

《信息安全工程》中接触过双向认证,但有两个问题。

第一个是当时最终的课程设计客户端是浏览器,服务端是tomcat双向认证只需要对两者进行配置并不需要自己真的实现代码。

第二个是虽然课程也有接近双向认证的实现代码,但当时是Java + JCE环境现在要用C + + + Openssl环境,总体意思确实还是差不多但具体函数和参数差别还是不少。

所以眼下有的是:证书生成的思想+双向认证实现的思想。对读者而言,即要假定已对证书,SSL/TSL,套接字编程等几个概念有基本的了解,本文不做详细介绍。

基于此本文要解决的问题是:Openssl具体如何生成证书+ Openssl如何实现双向认证。

双向认证的关键点在以下几个函数(服务端和客户端都一样),其他就不细说参看代码注释:

SSL_CTX_set_verify——配置启用双向认证可以

SSL_CTX_load_verify_locations——加载信任的根证书

SSL_CTX_use_certificate_file——加载自己的证书

SSL_CTX_use_PrivateKey_file——加载自己的私钥

SSL_get_verify_result——真正进行验证,一定要调用这个函数不然前面四个光配置而已并不会进行双向验证

<强>二,双向认证程序实现

2.1安装Openssl及开发api

apt-get安装libssl-dev

2.2服务端代码

 # include & lt; stdio.h>
  # include & lt; stdlib.h>
  # include & lt; errno.h>
  # include & lt; string.h>
  # include & lt; sys/types.h>
  # include & lt; netinet/in.h>
  # include & lt; sys/socket.h>
  # include & lt; sys/wait.h>
  # include & lt; unistd.h>
  # include & lt; arpa/inet.h>
  # include & lt; openssl/ssl.h>
  # include & lt; openssl/err.h>
  
  #定义MAXBUF 1024
  
  空白ShowCerts (SSL * SSL)
  {
  X509 *证书;
  char *线;
  
  cert=SSL_get_peer_certificate (ssl);//SSL_get_verify_result()是重点,SSL_CTX_set_verify()只是配置启不启用并没有执行认证,调用该函数才会真证进行证书认证//如果验证不通过,那么程序抛出异常中止连接
  如果(SSL_get_verify_result (ssl)==X509_V_OK) {
  printf(“证书验证通过\ n");
  }
  如果(cert !=NULL) {
  printf(“数字证书信息:\ n");
  行=X509_NAME_oneline (X509_get_subject_name (cert), 0, 0);
  printf(“证书:% s \ n",线);
  免费(线);
  行=X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
  printf(“颁发者:% s \ n",线);
  免费(线);
  X509_free (cert);
  其他}
  printf(“无证书信息! \ n");
  }
  
  int主要(int命令行参数个数,char * * argv) {
  int sockfd new_fd;
  socklen_t len;
  结构指向sockaddr_in my_addr their_addr;
  unsigned int myport lisnum;
  字符缓冲区(MAXBUF + 1);
  SSL_CTX * ctx;
  
  如果(argv [1])
  myport=atoi (argv [1]);
  其他的
  myport=7838;
  
  如果(argv [2])
  lisnum=atoi (argv [2]);
  其他的
  lisnum=2;/* SSL库初始化*/SSL_library_init ();/*载入所有SSL算法*/OpenSSL_add_all_algorithms ();/*载入所有SSL错误消息*/SSL_load_error_strings ();/*以SSL V2和V3标准兼容方式产生一个SSL_CTX,即SSL内容文本*/ctx=SSL_CTX_new来(SSLv23_server_method ());/*也可以用SSLv2_server_method()或SSLv3_server_method()单独表示V2或V3标准*/如果(ctx==NULL) {
  ERR_print_errors_fp (stdout);
  退出(1);
  }//双向验证//SSL_VERIFY_PEER——要求对证书进行认证,没有证书也会放行//SSL_VERIFY_FAIL_IF_NO_PEER_CERT——要求客户端需要提供证书,但验证发现单独使用没有证书也会放行
  SSL_CTX_set_verify (ctx SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT零);//设置信任根证书
  可以如果SSL_CTX_load_verify_locations (ctx,“ca.crt", NULL) & lt;=0) {
  ERR_print_errors_fp (stdout);
  退出(1);
  }/*载入用户的数字证书,此证书用来发送给客户端。证书里包含有公钥*/如果(SSL_CTX_use_certificate_file (ctx, argv [3], SSL_FILETYPE_PEM) & lt;=0) {
  ERR_print_errors_fp (stdout);
  退出(1);
  }/*载入用户私钥*/如果(SSL_CTX_use_PrivateKey_file (ctx, argv [4], SSL_FILETYPE_PEM) & lt;=0) {
  ERR_print_errors_fp (stdout);
  退出(1);
  }/*检查用户私钥是否正确*/如果(! SSL_CTX_check_private_key (ctx)) {
  ERR_print_errors_fp (stdout);
  退出(1);
  }/*开启一个套接字监听*/如果((sockfd=插座(PF_INET SOCK_STREAM 0))==1) {
  perror (“socket");
  退出(1);
  其他}
  printf(“套接字创建\ n");
  
  bzero(及my_addr sizeof (my_addr));
  my_addr。sin_family=PF_INET;
  my_addr。sin_port=htons (myport);
  my_addr.sin_addr。s_addr=INADDR_ANY;
  
  如果绑定(sockfd (struct sockaddr *), my_addr, sizeof (struct sockaddr))==1){
  perror (“bind");
  退出(1);
  其他}
  printf(“绑定\ n");
  
  如果听(sockfd lisnum)==1) {
  perror (“listen");
  退出(1);
  其他}
  printf(“开始听\ n");
  
  而(1){
  SSL * SSL;
  len=sizeof (struct sockaddr);/*等待客户端连上来*/如果((new_fd=接受(sockfd (struct sockaddr *), their_addr,, len))==1){
  perror (“accept");
  退出(errno);
  其他}
  printf(“服务器:收到% s,连接端口% d,插座% d \ n",
  inet_ntoa (their_addr.sin_addr)、ntohs (their_addr.sin_port),
  new_fd);/*基于ctx产生一个新的SSL */ssl=SSL_new (ctx);/*将连接用户的套接字加入到SSL */SSL_set_fd (ssl, new_fd);/*建立SSL连接*/如果(SSL_accept (ssl)==1) {
  perror (“accept");
  关闭(new_fd);
  打破;
  }
  ShowCerts (ssl);/*开始处理每个新连接上的数据收发*/bzero (buf, MAXBUF + 1);
  strcpy (buf,“服务器→client");/*发消息给客户端*/len=SSL_write (ssl、buf strlen (buf));
  
  如果(len & lt;=0) {
  printf(“消息& # 39;% & # 39;发送失败!错误代码是% d,错误信息是& # 39;% & # 39;\ n",但,errno,
  strerror (errno));
  goto完成;
  其他}
  printf(“消息& # 39;% & # 39;发送成功,共发送了% d个字节! \ n",但,len);
  
  bzero (buf, MAXBUF + 1);/*接收客户端的消息*/len=SSL_read (ssl、buf MAXBUF);
  如果(len比;0)
  printf(“接收消息成功:& # 39;% & # 39;,共% d个字节的数据\ n",但,len);
  其他的
  printf(“消息接收失败!错误代码是% d,错误信息是& # 39;% & # 39;\ n",
  errno, strerror (errno));/*处理每个新连接上的数据收发结束*/完成:/*关闭SSL连接*/SSL_shutdown (ssl);/*释放SSL */SSL_free (ssl);
  *//*关闭插座
  关闭(new_fd);
  }/*关闭监听的套接字*/关闭(sockfd);/*释放CTX */SSL_CTX_free (ctx);
  返回0;
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null
  null

Openssl如何实现双向认证