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