基于X.509证书和SSL协议的身份认证过程实现(OpenSSL可以自己产生证书,有TCP通过SSL进行实际安全通讯的实际编程代码)good
上周帮一个童鞋做一个数字认证的实验,要求是编程实现一个基于X.509证书认证的过程,唉!可怜我那点薄弱的计算机网络安全的知识啊!只得恶补一下了。
首先来看看什么是X.509。所谓X.509其实是一种非常通用的证书,什么是证书?唉!这么说吧!当两个人需要进行远程通信而又不想让第三个人知道时就必须建立一种安全措施,因为看不到对方的脸,又不能通过电话直接询问对方,就得想点别的办法,比如我设计一个密码,让后发短信告诉你,这样当我们在网上交流之前就可以对一下密码,暗号之类的。确认后就可以证明你的身份了。这个证书就相当于这里的密码,只是确实比密码要复杂一些,想了解的朋友可以google之~~
那么什么是SSL呢?SSL是一种网络通信的安全协议,在传输层对网络连接进行加密,简单点说就是提过一种方法使我们通过网络进行通信的安全性得到提高。而X.509证书认证的过程就可以通过这个协议来实现。
好了,知道了这些就然我们来开始写这个程序吧!慢着,如果实现这个SSL和X.509呢?我们貌似还是不知道啊!好吧,既然是一种加密通信过程肯定和加密分不开吧,随便查了一下,SSL涉及到的加密方法就不下数十种,而且极其艰深复杂,难道我要自己实现这个过程?自己构建X.509证书?当然不会,互联网就是好啊!让我发现了存在OpenSSL这么个东西,让我们感谢Eric A. Young和Tim J. Hudson这两个人吧!因为他们在1995年就开始做我上面想做的事情了,而且写出了OpenSSL这个没有太多限制的开放源代码的软件包。有了这玩意什么事都好办了。
我先从网上下了openssl-0.9.8的压缩包,然后琢磨了两个多小时终于明白如何安装这么个软件包了。我把自己的安装心得记录下来大家一起分享。
在使用这个软件包之前需要安装PERL组件——ActivePerl-5.8.0.806-MSWin32-x86(PERL和OpenSSL网上有很多免费下载的,这里我就不给出下载地址了),这个安装倒很简单,直接使用安装包就行了。判断perl是否安装成功的方法是通过命令行进入到perl安装目录下的\eg目录里,执行perl example.pl命令,如果显示“Hello from ActivePerl!”,则说明Perl安装成功。然后我们开始安装OpenSSL。
1、将openssl.0.9.8.tar.gz解压到d:盘下。
2、打开命令行窗口进入d: \openssl-0.9.8目录下键入以下命令:perl Configure VC-WIN32。
3、然后键入ms\do_ms命令。
4、然后键入nmake –f ms\ntdll.mak会成功编译很多c文件,如果不成功的话一般就是提示“cl”无法找到之类的错误,这时就需要在系统环境变量PATH里加入VC的bin路径,这样才能找到“cl”命令。
5、接着键入mkdir c:\caroot,这样就会在C盘根目录下生成一个caroot文件夹。
6、然后需要在系统环境变量PATH路径中加入openssl-0.9.8\out32dll路径,并在VC(我使用的是VC编译器)中把include路径增加一个:D:\openssl-0.9.8\include,把library路径增加一个:D:\openssl-0.9.8\out32dll,将D:\openssl-0.9.8\apps下的openssl.cnf文件拷贝到c:\caroot目录下。
到这里可以说我们的安装已经完成了,下面我们需要生成几个证书和密钥,密钥是用来加密的,证书是用来认证的。比如服务器证书、密钥,客户端证书、密钥。但这里我们漏了一个最重要的证书和密钥,就是CA证书和密钥。CA是个什么东东呢?这个解释起来很麻烦,他就像一个中间证明人,证明两方的身份都是真实可信的,这样双方拿着证书才能相互信任,详细解释可以参看这里:http://baike.baidu.com/view/23691.htm#7,维基百科的解释更加详细,不过是英文的我觉得喜欢英文的朋友可以看看:http://en.wikipedia.org/wiki/Certificate_authority。
好吧,让我们把这些该死的证书密钥都生成然后开始编码吧:
在上面C:\caroot目录下(其实可以不在这目录下)输入如下:
#产生CA自签名证书
openssl.exe genrsa -out private\ca.key -rand private\.rnd -des 2048
openssl.exe req -new -x509 -days 3650 -key private\ca.key -out private\ca.crt -config openssl.cnf
openssl.exe x509 -in private\ca.crt -noout -text
#产生server的证书过程
openssl.exe genrsa -out private\server.key 1024
openssl.exe req -new -key private\server.key -out newcerts\server.csr -config openssl.cnf
openssl.exe ca -in newcerts\server.csr -cert private\ca.crt -keyfile private\ca.key
-config openssl.cnf -policy policy_anything -out certs\server.crt
openssl.exe x509 -in certs\server.crt -noout -text
#产生proxy的证书过程
openssl.exe genrsa -out private\proxy.key 1024
openssl.exe req -new -key private\proxy.key -out newcerts\proxy.csr -config openssl.cnf
openssl.exe ca -in newcerts\proxy.csr -cert private\ca.crt -keyfile private\ca.key -config openssl.cnf -policy policy_anything -out certs\proxy.crt
openssl.exe x509 -in certs\proxy.crt -noout -text
#产生client的证书过程
openssl.exe genrsa -out private\client.key 1024
openssl.exe req -new -key private\client.key -out newcerts\client.csr -config openssl.cnf
openssl.exe ca -in newcerts\client.csr -cert private\ca.crt -keyfile private\ca.key -config openssl.cnf -policy policy_anything -out certs\client.crt
openssl.exe x509 -in certs\client.crt -noout -text
上面的命令中会给出提示让用户输入一些证书的信息,只要正常输入就可以了。执行上述操作后C:\caroot目录如下:
private目录如下:
newcerts目录如下:
certs目录如下:
其中证书:
ca.crt为自签名证书;
server.crt,server.key为服务器端的证书和私钥文件;
proxy.crt,proxy.key为代理服务器端的证书和私钥文件;
client.crt,client.key为客户端的证书和私钥文件。
乱七八糟的事情终于做完了,下面总算可以开始编码了。看了一下实验要求,要编写简单的Client程序和Server程序,实现Client程序与Server程序之间基于X509证书和SSL协议身份认证和通信加密,服务器能够接收并且显示客户端发送来的文字消息。
看来需要两个程序,一个服务端的,一个客户端的。
具体编程大家直接看代码就行了,涉及到socket通信,其实相当简单,懂一点点就行了,我这里就不要王婆卖瓜了,直接上代码吧。
客户端程序代码:
- //client
- #include <winsock2.h>
- #include <conio.h>
- #include <stdio.h>
- #include "openssl/x509.h"
- #include "openssl/ssl.h"
- #include "openssl/err.h"
- #include "openssl/rand.h"
- #define PORT 1111
- #define SERVER "127.0.0.1"
- #define CACERT "ca.crt"
- #define MYCERTF "yuliding.crt"
- #define MYKEYF "yuliding.key"
- #define MSGLENGTH 1024
- int main()
- {
- WSADATA wsadata;
- WSAStartup(MAKEWORD(2,2), &wsadata);
- sockaddr_in sin;
- int seed_int[100]; /*存放随机序列*/
- SSL*ssl;
- SSL_METHOD *meth;
- SSL_CTX *ctx;
- //SSL初始化
- OpenSSL_add_ssl_algorithms();
- //SSL错误信息初始化
- SSL_load_error_strings();
- //创建本次会话所使用的协议
- meth = TLSv1_client_method();
- //申请SSL会话的环境
- ctx = SSL_CTX_new(meth);
- if (NULL == ctx)
- exit(1);
- //设置会话的握手方式并加载CA证书
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- SSL_CTX_load_verify_locations(ctx, CACERT, NULL);
- //加载自己的证书
- if (0 >= SSL_CTX_use_certificate_file(ctx, MYCERTF, SSL_FILETYPE_PEM)) {
- ERR_print_errors_fp(stderr);
- exit(1);
- }
- //加载自己的私钥
- if (0 >= SSL_CTX_use_PrivateKey_file(ctx, MYKEYF, SSL_FILETYPE_PEM)) {
- ERR_print_errors_fp(stderr);
- exit(1);
- }
- //检查自己的证书和私钥是否匹配
- if (!SSL_CTX_check_private_key(ctx)) {
- printf("Private key does not match the certificate public key\n");
- exit(1);
- }
- /*构建随机数生成机制,WIN32平台必需*/
- srand((unsigned)time(NULL));
- for (int i = 0; i < 100; i++)
- seed_int[i] = rand();
- RAND_seed(seed_int, sizeof(seed_int));
- //加密方式
- SSL_CTX_set_cipher_list(ctx, "RC4-MD5");
- //处理握手多次
- SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
- /*以下是正常的TCP socket建立过程 .............................. */
- SOCKET sock;
- printf("Begin tcp socket...\n");
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- printf("SOCKET有问题. \n");
- }
- memset(&sin, '\0', sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = inet_addr(SERVER); /* Server IP */
- sin.sin_port = htons(PORT); /* Server Port number */
- int icnn = connect(sock, (sockaddr *)&sin, sizeof(sin));
- if (icnn == SOCKET_ERROR) {
- printf("连不上服务器\n", GetLastError());
- exit(1);
- }
- /* TCP 链接已建立.开始 SSL 握手过程.......................... */
- //绑定套接字
- ssl = SSL_new(ctx);
- if (NULL == ssl)
- exit(1);
- if (0 >= SSL_set_fd(ssl, sock)) {
- printf("Attach to Line fail!\n");
- exit(1);
- }
- //SSL握手
- //SSL_connect(ssl);
- int k = SSL_connect(ssl);
- if (0 >= k) {
- printf("%d\n", k);
- printf("SSL connect fail!\n");
- exit(1);
- }
- printf("连接服务器成功\n");
- char sendmsg[MSGLENGTH] = "\0";
- char revmsg[MSGLENGTH] = "\0";
- int err = SSL_read(ssl, revmsg, sizeof(revmsg));
- revmsg[err] = '\0';
- printf("%s\n", revmsg);
- while (1) {
- printf("请输入所要发送的数据:\n");
- scanf("%s", sendmsg);
- SSL_write(ssl, sendmsg, strlen(sendmsg));
- printf("发送消息“ %s ”成功!\n", sendmsg);
- }
- //关闭套接字
- SSL_shutdown(ssl);
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- closesocket(sock);
- WSACleanup();
- getch();
- return 0;
- }
服务端程序代码:
- //server
- #include <winsock2.h>
- #include <conio.h>
- #include <stdio.h>
- #include <winsock.h>
- #include "openssl/x509.h"
- #include "openssl/ssl.h"
- #include "openssl/err.h"
- #define MSGLENGTH 1024
- #define PORT 1111
- #define CACERT "ca.crt"
- #define SVRCERTF "server.crt"
- #define SVRKEYF "server.key"
- int main()
- {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2,2), &wsaData);
- SOCKET sock;
- SSL_METHOD *meth;
- SSL_CTX* ctx;
- SSL* ssl;
- //SSL初始化
- OpenSSL_add_ssl_algorithms();
- //SSL错误信息初始化
- SSL_load_error_strings();
- //创建本次会话所使用的协议
- meth = TLSv1_server_method();
- //申请SSL会话的环境
- ctx = SSL_CTX_new(meth);
- if (NULL == ctx)
- exit(1);
- //设置会话的握手方式并加载CA证书
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- SSL_CTX_load_verify_locations(ctx, CACERT, NULL);
- //加载服务器端的证书
- if (0 >= SSL_CTX_use_certificate_file(ctx, SVRCERTF, SSL_FILETYPE_PEM)) {
- ERR_print_errors_fp(stderr);
- exit(1);
- }
- //加载服务器端的私钥
- if (0 >= SSL_CTX_use_PrivateKey_file(ctx, SVRKEYF, SSL_FILETYPE_PEM)) {
- ERR_print_errors_fp(stderr);
- exit(1);
- }
- //检查服务器端的证书和私钥是否匹配
- if (!SSL_CTX_check_private_key(ctx)) {
- printf("Private key does not match the certificate public key\n");
- exit(1);
- }
- //加密方式
- SSL_CTX_set_cipher_list(ctx, "RC4-MD5");
- //处理握手多次
- SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
- /*以下是正常的TCP socket建立过程 .............................. */
- printf("Begin tcp socket...\n");
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- printf("SOCKET有问题. \n");
- return 0;
- }
- sockaddr_in addr;
- memset(&addr, '\0', sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(PORT); /* Server Port number */
- addr.sin_addr.s_addr = INADDR_ANY;
- //绑定sock
- int nResult = bind(sock, (sockaddr *)&addr, sizeof(addr));
- if (nResult == SOCKET_ERROR) {
- printf("绑定SOCKET有问题. \n");
- return 0;
- }
- printf("服务器启动成功,端口:%d\n正在等待连接\n", PORT);
- /*接受TCP链接*/
- sockaddr_in sa_cli;
- int err = listen(sock, 5);
- if (-1 == err)
- exit(1);
- int client_len = sizeof(sa_cli);
- int ss = accept(sock, (struct sockaddr *) &sa_cli, &client_len);
- if (ss == -1) {
- exit(1);
- }
- closesocket(sock);
- printf("Connection from %d, port %d\n", sa_cli.sin_addr.s_addr, sa_cli.sin_port);
- /* TCP 链接已建立.开始 SSL 握手过程.......................... */
- //绑定套接字
- ssl = SSL_new(ctx);
- if (NULL == ssl)
- exit(1);
- if (0 >= SSL_set_fd(ssl, ss)) {
- printf("Attach to Line fail!\n");
- exit(1);
- }
- //SSL握手
- //SSL_accept(ssl);
- int k = SSL_accept(ssl);
- if (0 >= k) {
- printf("%d\n", k);
- printf("SSL connect fail!\n");
- exit(1);
- }
- //进行信息验证
- X509 *client_cert;
- client_cert = SSL_get_peer_certificate(ssl);
- printf("发现客户端尝试连接\n");
- if (client_cert != NULL) {
- printf ("Client certificate:\n");
- //读取证书subject名并显示
- char *str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
- if (NULL == str) {
- printf("认证出错!\n");
- exit(1);
- }
- printf("subject: %s\n", str);
- //读取证书的issuer名并显示
- str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
- if (NULL == str) {
- printf("证书名为空\n");
- exit(1);
- }
- printf("issuer: %s\n", str);
- printf("连接成功\n");
- X509_free (client_cert);/*如不再需要,需将证书释放 */
- OPENSSL_free(str);
- }
- else {
- printf("找不到客户端的认证证书\n");
- exit(1);
- }
- char buf[MSGLENGTH];
- SSL_write(ssl, "Server is connect to you!\n", strlen("Server is connect to you!\n"));
- printf("Listen to the client: \n");
- while (1) {
- err = SSL_read(ssl, buf, sizeof(buf));
- buf[err] = '\0';
- printf("%s\n", buf);
- }
- //关闭套接字
- SSL_shutdown(ssl);
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- WSACleanup();
- getch();
- return 0;
- }
编写完后后首先运行服务端程序,然后运行客户端程序,效果如下:
连接成功后服务端的状态如下:
红色部分为我在生成证书文件时输入的一些信息,具体的你可以通过我上面讲解建立证书时试验一下。这里我通过代码把这些证书信息打印出来是为了判断获取的证书是否正确。
连接成功后客户端的状态:
这样,就可以实现通过客户端向服务端发送信息,如下:
细心的朋友可以发现上面我显示Connection from后面貌似是一串乱码,其实不然,那是一串IP地址,只不过是反序排列的,我曾经在做SIP TRUNK项目时也遇到过这个问题,感兴趣的朋友可以自己写个转义程序把他转成IP地址试试看。
最后提醒一点,因为我的代码中把证书和密钥文件的目录设为当前目录了,所以想要程序正确运行必须把证书文件和密钥文件拷贝到当前工程的目录下,如下:
客户端是一样的。或者你可以把目录改到你生成证书和密钥的目录下。
可以看见,上面的程序有众多漏洞和缺陷,如果有朋友想使用的话请一定仔细debug一下,把很多判断的条件加上,这里我就偷下懒罗~~分享快乐~~
本文出自 “菜鸟浮出水” 博客,请务必保留此出处http://rangercyh.blog.51cto.com/1444712/430652
基于X.509证书和SSL协议的身份认证过程实现(OpenSSL可以自己产生证书,有TCP通过SSL进行实际安全通讯的实际编程代码)good的更多相关文章
- 基于SSL协议的双向认证 - 数字证书 [2]
1.1 数字证书 1.1.1 概念理解 一种文件的名称,例如一个机构或人的签名,能够证明这个机构或人的真实性.简而言之数字证书是一种网络上证明持有者身份的文件,同时还包括有公钥.证书是由国际 ...
- 基于SSL协议的双向认证 - SSL协议 [1]
1 概要说明 在互联网通信方式中,目前用的最广泛的是HTTPS配合SSL和数字证书来保证传输和认证安全了. 2 详细介绍 2.1 HTTPS HTTPS全称:Hypertext Transf ...
- 基于SSL协议的双向认证 - 双向认证 [3]
1 SSL双向认证的实现 这里是基于SSL和Tomcat配置实现的,配置方法如下: 1.1 生成CA数字证书 首先需要配置OPENSSL环境变量. 我的OPENSSL配置文件路径是“D ...
- HTTPS协议,SSL协议及完整交互过程
文章转自 https://blog.csdn.net/dfsaggsd/article/details/50910999 SSL 1. 安全套接字(Secure Socket Layer ...
- 加密解密(4)SSL协议及HTTPS握手过程
SSL协议 简介 SSL (Secure Sockets Layer 安全套接层)是一个安全协议,它提供使用 TCP/IP 的通信应用程序间的隐私与完整性.因特网的 超文本传输协议 (HTTP)使用 ...
- SSH协议、HTTPS中SSL协议的完整交互过程
1.(SSH)公私钥认证原理 服务器建立公钥:每一次启动sshd服务时,该服务会主动去找/etc/ssh/ssh_host*的文件 客户端通过ssh工具进行连接,如Xshell,SecureCRT 服 ...
- apache添加ssl协议实现用户认证
目标 1对服务器的访问由http改为https, 2仅有证书的客户端可以访问服务器, 3.通过服务器端的配置,可以停用某个客户端的证书. 一 Apache服务器相关配置: 在../apache/con ...
- 【转】SSL协议、SET协议、HTTPS简介
一.SSL协议简介 SSL是Secure Socket Layer的缩写,中文名为安全套接层协议层.使用该协议后,您提交的所有数据会首先加密后,再提交到网易邮箱,从而可以有效防止黑客盗取您的用户名.密 ...
- SSL身份认证原理 - 目标: 搞清楚数字证书和数字签名的关系
1 概述 1.1 产生背景 基于万维网的电子商务和网上银行等新兴应用,极大地方便了人们的日常生活,受到人们的青睐.由于这些应用都需要在网络上进行在线交易,它们对网络通信的安全性提出了更高的要求.传 ...
随机推荐
- virtualenv对python
使用virtualenv对python进行多版本隔离 最近在用python做一个文本的情感分析的项目,用到tensorflow,需要用python3的版本,之前因为<机器学习实战>那本书的 ...
- Quartz 入门详解 专题
Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings that a ...
- AndroidStudio 3.0 生成jar包的方法
AndroidStudio3.0生成jar包的方法,一下将逐步演示流程,及步骤讲解 1:新建样例工程,目录如下 2:鼠标右键点击app->New->选择Module 选择AndroidLi ...
- WPF 触摸到事件
原文:WPF 触摸到事件 本文从代码底层告诉大家,在触摸屏幕之后是如何拿到触摸点并且转换为事件 在 WPF 界面框架核心就是交互和渲染,触摸是交互的一部分.在 WPF 是需要使用多个线程来做触摸和渲染 ...
- wpf DoEvents
原文:wpf DoEvents 如果在执行一段卡UI的代码,这时如何让UI响应.如果存在代码需要获得依赖属性,那么代码就需要在UI线程执行,但是这时就会卡UI,为了让UI响应,所以就需要使用DoEve ...
- js 图片缩显
<script language="javascript"> var flag=false; function MyImage(Img){ var image=ne ...
- Diffie-Hellman Key Exchange – A Non-Mathematician’s Explanation
The Complete Diffie-Hellman Key Exchange Diagram The process begins when each side of the communicat ...
- 深度学习实践指南(六)—— ReLU(前向和后向过程)
def relu_forward(x): out = x * (x > 0) # * 对于 np.ndarray 而言表示 handmard 积,x > 0 得到的 0和1 构成的矩阵 r ...
- Javascript 基准测试
http://bubkoo.com/2014/02/18/bulletproof-javascript-benchmarks/
- DB 查询分析器 轻松创建DB2定义他们自己的函数
DB 查询分析器 方便地创建DB2自己定义函数 马根峰 (广东联合电子服务股份有限公司, 广州 510300) 摘要 ...