使用c語言實如今linux下的openssl客戶端和服務器端編程

  前幾天組長讓我實現一個使用opensslc語言編寫的客戶端和java編寫的服務器實現字符流的通訊,給了段代碼。在本身的ubuntu上跑服務器和客戶端收發信息都沒有問題,可是就是和java的通訊不了。後來發現組長給的客戶端代碼有問題,因而網上找到了比較正確的客戶端和服務器代碼,本身作了稍微的改動。有一點要說一下,個人c客戶端使用的證書格式是.pem的,而java那邊使用kittool生成的證書格式是.cer的因此須要進行cerpem格式的轉換纔可以使用。html

我使用的是這個網址下進行證書格式轉換:https://www.sslshopper.com/ssl-converter.htmljava

操做是:web

1.選擇要轉換的文件。算法

2.選擇文件的格式,裏面沒有cer格式選項,可使用der就能夠。 編程

3.選擇要轉換成爲何格式我是選擇standard pem。生成的是。crt文件,這個在ubuntu下面導入到個人客戶端中便可。ubuntu

如下是我編程的具體的操做步驟,大部分是從網上整理的,小部分的作了改動,加了註釋。vim

安裝openssl安裝包服務器

在如下網站下載openssl:socket

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

tar -zxvf openssl-1.0.0a.tar.gz
mv openssl-1.0.1a openssl
cd openssl

 若是須要zlib壓縮模塊的話,還須要先安裝zlib

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

 不須要就直接用:

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

 更多詳細幫助請運行

./config –help
./config –t
make depend
make
make test
sudo make install

 設置環境變量:在/etc/profilePATH中增長以下內容

PATH=/usr/local/ssl/bin:/sbin/:$PATH:/usr/sbin
export PATH
cat /etc/ssl/openssl.cnf
//查看路徑
which openssl
//查看版本
openssl version
openssl安裝完畢

若是計算機聯網的話可使用以下命令安裝比較簡便

Ubuntu系統下安裝openssl

sudo apt-get install openssl
//安裝openssl-devel
//因爲ubuntu下沒法安裝openssl-devel 因此使用libssl-dev代替openssl-devel
sudo apt-get install libssl-dev

 

CentOS系統下安裝openssl

//解壓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 爲客戶端的證書和私鑰文件。

//把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
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

產生通常錯誤的解決方法

//出現: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.attr
  將unique_subject = yes修改成 unique_subject = no

#客戶端的代碼

vim client.c

//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

//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

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
相關文章
相關標籤/搜索