https c语言实现双向认证

First Post:

Last Update:

Word Count:
2.3k

Read Time:
12 min

https c语言实现双向认证

安装openssl安装包

在以下网站下载openssl:

http://www.openssl.org/source/openssl-1.0.0a.tar.gz

1
2
3
tar -zxvf openssl-1.0.0a.tar.gz
mv openssl-1.0.1a openssl
cd openssl

如果需要zlib压缩模块的话,还需要先安装zlib

1
./config --prefix=/usr/local/ssl shared zlib-dynamic enable-camellia

不需要就直接用:

1
./config --prefix=/usr/local/ssl shared no-zlib

更多详细帮助请运行

1
2
3
4
5
6
./config –help
./config –t
make depend
make
make test
sudo make install

设置环境变量:在/etc/profile的PATH中增加如下内容

1
2
3
4
5
6
7
8
PATH=/usr/local/ssl/bin:/sbin/:$PATH:/usr/sbin
export PATH
cat /etc/ssl/openssl.cnf
//查看路径
which openssl
//查看版本
openssl version
openssl安装完毕

如果计算机联网的话可以使用如下命令安装比较简便

Ubuntu系统下安装openssl

1
2
3
4
sudo apt-get install openssl
//安装openssl-devel
//由于ubuntu下无法安装openssl-devel 所以使用libssl-dev代替openssl-devel
sudo apt-get install libssl-dev

CentOS系统下安装openssl

1
2
3
4
5
6
7
8
9
10
11
12
13
//解压openssl安装包
[root@localhost opt]# tar xvzf openssl-1.0.0d.tar.gz
//进入解压后的目录
[root@localhost opt]# cd openssl-1.0.0d
//修改openssl配置文件
[root@localhost openssl-1.0.0d]# ./configure --prefix=/usr/local/openssl
//编译代码
[root@localhost openssl-1.0.0d]# make
//安装
[root@localhost openssl-1.0.0d]# make install
//安装curses.h头文件的库
sudo apt-get install libncurses5-dev
//所需软件安装完毕:openssl、openssl-devel、libncurses5-dev 三个软件

生成工作目录产生CA凭证

ca.crt 为自签名证书;
server.crt,server.key 为服务器端的证书和私钥文件;
proxy.crt,proxy.key 为代理服务器端的证书和私钥文件;
client.crt,client.key 为客户端的证书和私钥文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//把openssl安装目录下的misc拷贝到用户目录下
cd
mkdir sslca
cd sslca
sudo cp /usr/local/ssl/ssl/misc -r ./
sudo cp /usr/local/ssl/ssl/openssl.cnf ./misc
cd misc
sudo CA.sh –newca
//产生一个demoCA文件夹
cd demoCA
//复制安装目录下面的openssl.cnf文件到demoCA下
sudo cp /usr/local/ssl/ssl/openssl.cnf ./
//产生CA自签名证书
openssl genrsa -out ./private/ca.key -rand ./private/.rnd -des 2048
openssl req -new -x509 -days 3650 -key ./private/ca.key -out ./private/ca.crt -config openssl.cnf
openssl x509 -in ./private/ca.crt -noout -text
//产生server的证书过程
openssl genrsa -out ./private/server.key 1024
openssl req -new -key ./private/server.key -out ./newcerts/server.csr -config openssl.cnf
//这一步如果产生错误,请看后面的解决方法
openssl ca -in ./newcerts/server.csr -cert ./private/ca.crt -keyfile ./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/server.crt
openssl x509 -in ./certs/server.crt -noout -text
//产生proxy的证书过程
openssl genrsa -out ./private/proxy.key 1024
//这步要是产生错误,请看后面的解决方法
openssl req -new -key ./private/proxy.key -out ./newcerts/proxy.csr -config openssl.cnf
openssl ca -in ./newcerts/proxy.csr -cert ./private/ca.crt -keyfile./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/proxy.crt
openssl x509 -in ./certs/proxy.crt -noout -text
//产生client的证书过程
openssl genrsa -out ./private/client.key 1024
openssl req -new -key ./private/client.key -out ./newcerts/client.csr -config openssl.cnf
openssl ca -in ./newcerts/client.csr -cert ./private/ca.crt -keyfile ./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/client.crt
openssl x509 -in ./certs/client.crt -noout -text

产生一般错误的解决方法

1
2
3
4
5
6
7
8
//出现:I am unable to access the ./demoCA/newcerts directory
./demoCA/newcerts:Nosuch file or directory
解决:修改openssl.cnf
在42行:dir = ./demoCA修改为 dir = ./
//出现:failed to update database
TXT_DB error number 2
解决:修改index.txt
将unique_subject = yes修改为 unique_subject = no

正常openssl生成证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mkdir ssl
cd ssl
cp -r /etc/ssl/misc ./
cp /etc/ssl/openssl.cnf ./misc
sudo ./CA.pl -newca
cp /etc/ssl/openssl.cnf ./
mkdir private
echo "abc" > ./private/.rnd # 随机种子
openssl genrsa -out ./private/ca.key -rand ./private/.rnd -des 2048 # 生成私钥,输入两次密码 1234
openssl req -new -x509 -days 3650 -key ./private/ca.key -out ./private/ca.crt -config openssl.cnf # 生成证书,输入之前密码1234
openssl x509 -in ./private/ca.crt -noout -text # 查看证书
# 生成server证书
openssl genrsa -out ./private/server.key 1024
mkdir newcerts
openssl req -new -key ./private/server.key -out ./newcerts/server.csr -config openssl.cnf # 生成服务端私钥,输入密码12345
mkdir certs

客户端的代码

vim client.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//client 
#include <openssl/rand.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <errno.h>
#include <curses.h>
#define PORT 7979
#define SERVER "127.0.0.1"
#define CACERT "./private/ca.crt"
/* 如果需要与不是本机的服务器通信,在这个地方的证书导入对应服务器的证书,我的是和java通信,所以就导入了java端的服务器证书
注意,java端的证书需要进行格式转换,具体操作方法请参见开头的说明。*/
#define MYCERTF "./certs/proxy.crt"
#define MYKEYF "./private/proxy.key"
#define MSGLENGTH 1024
int
main ()
{
struct sockaddr_in sin;
int seed_int[100];
SSL *ssl;
SSL_METHOD *meth;
SSL_CTX *ctx;
int i;

/* 初始化OpenSSL */
SSL_library_init();
/*加载算法库 */
OpenSSL_add_ssl_algorithms ();
/*加载错误处理信息 */
SSL_load_error_strings ();
/* 选择会话协议 */
meth = (SSL_METHOD *) TLSv1_client_method ();
/* 创建会话环境 */
ctx = SSL_CTX_new (meth);
if (NULL == ctx)
exit (1);
/* 制定证书验证方式 */
SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, NULL);

/* 为SSL会话加载CA证书*/
SSL_CTX_load_verify_locations (ctx, CACERT, NULL);

/* 为SSL会话加载用户证书 */
if (0 == SSL_CTX_use_certificate_file (ctx, MYCERTF, SSL_FILETYPE_PEM))
{
ERR_print_errors_fp (stderr);
exit (1);
}
/* 为SSL会话加载用户私钥 */
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);
}
/* 设置随机数 */
srand ((unsigned) time (NULL));
for (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);
int sock;
printf ("Begin tcp socket...\n");
/* 创建socket描述符 */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
printf ("SOCKET error. \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, (struct sockaddr *) &sin, sizeof (sin));
if (icnn == -1)
{
printf ("can not connect to server,%s\n", strerror (errno));
exit (1);
}


/* 创建一个SSL套接字*/
ssl = SSL_new (ctx);
if (NULL == ssl)
exit (1);

/* 申请一个ssl套接字 */
if (0 >= SSL_set_fd (ssl, sock))
{
printf ("Attach to Line fail!\n");
exit (1);
}
//建立SSL连接
int k = SSL_connect (ssl);
if (0 == k)
{
printf ("%d\n", k);
printf ("SSL connect fail!\n");
exit (1);
}
printf ("connect to server\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 ("please input the data to send:\n");
scanf ("%s", sendmsg);
//向服务器发送消息
SSL_write (ssl, sendmsg, strlen (sendmsg));
printf ("send message ' %s ' success\n", sendmsg);
}
//关闭连接
SSL_shutdown (ssl);
SSL_free (ssl);
SSL_CTX_free (ctx);
close (sock);
getch ();
return 0;
}

服务端的代码

vim server.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//server 
#include <stdio.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <curses.h>
#define MSGLENGTH 1024
#define PORT 7979
#define CACERT "./private/ca.crt"
#define SVRCERTF "./certs/server.crt"
#define SVRKEYF "./private/server.key"
#define ADDRESS “127.0.0.1”
int main ()
{
int sock;
SSL_METHOD *meth;
SSL_CTX *ctx;
SSL *ssl;
//SSL初库始化
SSL_library_init();
//载入所有SSL算法
OpenSSL_add_ssl_algorithms ();
//载入所有错误信息
SSL_load_error_strings ();
meth = (SSL_METHOD *) TLSv1_server_method ();
ctx = SSL_CTX_new (meth);
if (NULL == ctx)
exit (1);
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);
printf ("Begin tcp socket...\n");
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
printf ("SOCKET error! \n");
return 0;
}
//准备通信地址和端口号
struct 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;
addr.sin_addr.s_addr = inet_addr(ADDRESS);
//绑定地址和端口号
int nResult = bind (sock, (struct sockaddr *) &addr, sizeof (addr));
if (nResult == -1)
{
printf ("bind socket error\n");
return 0;
}
printf ("server start successfully,port:%d\nwaiting for connections\n",
PORT);
struct 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);
}
close (sock);
printf ("Connection from %d, port %d\n", sa_cli.sin_addr.s_addr,
sa_cli.sin_port);
ssl = SSL_new (ctx);
if (NULL == ssl)
exit (1);
if (0 == SSL_set_fd (ssl, ss))
{
printf ("Attach to Line fail!\n");
exit (1);
}
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 ("find a customer to try to connect\n");
if (client_cert != NULL)
{
printf ("Client certificate:\n");
char *str =
X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
if (NULL == str)
{
printf ("auth error!\n");
exit (1);
}
printf ("subject: %s\n", str);
str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
if (NULL == str)
{
printf ("certificate name is null\n");
exit (1);
}
printf ("issuer: %s\n", str);
printf ("connect successfully\n");
X509_free (client_cert);
OPENSSL_free (str);
}
else
{
printf ("can not find the customer's certificate\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);
getch ();
return 0;
}

makefile的代码:

vim makefile

1
2
3
4
5
6
7
8
all:client.c server.c
gcc –o clientclient.c –Wall –lssl –ldl -lcurses
gcc –o serverserver.c –Wall –lssl –ldl –lcurses
#如果是自定义安装路径的,可以使用下面的
#gcc -Wall -o clientclient.c -I/usr/openssl-1.0.0c/include/usr/openssl-1.0.0c/libssl.a /usr/openssl-1.0.0c/libcrypto.a –ldl
#gcc -Wall -o serverserver.c -I/usr/openssl-1.0.0c/include/usr/openssl-1.0.0c/libssl.a /usr/openssl-1.0.0c/libcrypto.a -ldl
clean::
rm -f client server

参考工具:

证书格式转换:https://www.sslshopper.com/ssl-converter.html

打赏点小钱
支付宝 | Alipay
微信 | WeChat