openssl——server和client

一. openssl中的s_server命令與s_client命令

1.1 s_server的man函數

     

 

NAME

       s_server - SSL/TLS server program

 

SYNOPSIS

       openssl s_server [-accept port] [-context id] [-verify depth] [-Verify

       depth] [-crl_check] [-crl_check_all] [-cert filename] [-certform DER|PEM]

       [-key keyfile] [-keyform DER|PEM] [-pass arg] [-dcert filename] [-dcertform

       DER|PEM] [-dkey keyfile] [-dkeyform DER|PEM] [-dpass arg] [-dhparam

       filename] [-nbio] [-nbio_test] [-crlf] [-debug] [-msg] [-state] [-CApath

       directory] [-CAfile filename] [-trusted_first] [-krb5svc service] [-keytab

       filename] [-nocert] [-cipher cipherlist] [-quiet] [-no_tmp_rsa] [-ssl2]

       [-ssl3] [-tls1] [-tls1_1] [-tls1_2] [-dtls1] [-no_ssl2] [-no_ssl3]

       [-no_tls1] [-no_tls1_1] [-no_tls1_2] [-no_dhe] [-no_ecdhe] [-bugs] [-hack]

       [-www] [-WWW] [-HTTP] [-engine id] [-tlsextdebug] [-no_ticket] [-id_prefix

       arg] [-rand file(s)] [-nextprotoneg protocols]

 

DESCRIPTION

       The s_server command implements a generic SSL/TLS server which listens for

       connections on a given port using SSL/TLS.

 

OPTIONS

       -accept port

           the TCP port to listen on for connections. If not specified 4433 is

           used.

 

       -context id

           sets the SSL context id. It can be given any string value. If this

           option is not present a default value will be used.

   **** ***** ****** 

 

選項說明:

-accept port:監聽的TCP端口。缺省爲4433。

-context id:設置SSL context的id, 可以設置爲任何值。SSL context是會話ID的上下文。也可以不設置這個選項,,有缺省的給你用的。

-verify depth、-Verify depth:意義和s_client的這個選項一樣,但同時表示必須驗證client的證書。不記得server對client的證書驗證是可以選的嗎?-verify表示向client要求證書,但client還是可以選擇不發送證書,-Verify表示一定要client的證書驗證,否則握手失敗。

-crl_check、-crl_check_all:檢查客戶端的證書是否在CA的廢除列表中。CRL(s)在證書文件中。crl_check_all表示要檢查所有的CA證書中的廢除列表。

-cert filename:使用的證書文件名。大多數服務器算法套件需要一個證書,還有一些需要證書的公鑰類型,例如DSS算法組件需要證書(包含一個DSS(DSA)密鑰)。缺省使用 ./server.pem。

-certform DER|PEM:證書的格式,一般爲DER或PEM。缺省爲PEM。

-key filename:使用的私有密鑰文件。如果沒有指定,那麼證書文件會被使用(使用的是證書公鑰值)。

-keyform DER|PEM:私有密鑰文件的格式,一般爲DER或PEM。缺省爲PEM。

-pass arg:私鑰保護口令來源。

-dcert filename、-dkey keyfile:指定一個附加的證書文件和私有密鑰文件。不同的cipher需要不同的證書和私有密鑰文件。這個不同的cipher主要指cipher裏面的不對稱加密算法不同。比如基於RSA的cipher需要的是RSA的私有密鑰文件和證書,而基於DSA的算法則需要的是DSA的私有密鑰文件和證書。這個option可以讓這樣我們的server同時支持倆種算法的cipher成爲可能。

-dcertform DER|PEM:附加證書的格式,一般爲DER或PEM。缺省爲PEM。

-dkeyform DER|PEM:附加的私有密鑰文件的格式,一般爲DER或PEM。缺省爲PEM。

-dpass arg:附加私鑰保護口令來源。

-dhparam filename:使用的DH參數文件名。如果沒有設置,那麼server會試圖去從證書文件裏面獲得這些參數。如果證書裏面沒有這麼參數,一些hard code的參數就被調用。

-name_curve arg:橢圓曲線算法的選擇類型。

-nbio_test:檢查非阻塞socket的I/O運行情況。

-nbio:使用非阻塞socket。

-crlf:把在終端輸入的換行回車轉化成/r/n送出去。

-debug:打印所有的調試信息。

-msg:用16進制顯示所有的協議數據。

-state:打印SSL session的狀態, ssl也是一個協議,當然有狀態。

-CApath directory:設置信任CA文件所在路徑,此路徑中的ca文件名採用特殊的形式:xxx.0,其中xxx爲CA證書持有者的哈希值,它通過x509 -hash命令獲得。

-CAfile filename:某文件,裏面是所有你信任的CA的證書的內容。當你要建立client的證書鏈的時候也需要用到這個文件。

-nocert:如果server不想使用任何證書,就設置這個選項。目前只有anonymous DH算法有需要這麼做。

-cipher cipherlist:由我們自己來決定選用什麼加密算法,儘管是由server來決定使用什麼算法列表,但它一般都會採用我們送過去的cipher列表裏的第一個cipher。

-quiet:禁止打印sessionhe 和證書信息值。

-no_tmp_rsa:現在的接口cipher有時會使用臨時RSA密鑰。那就是說每次對話的時候臨時生成密鑰對。本選項就是用來禁止這種情況的。

-ssl2、-ssl3、-tls1_1、-tls1_2、-tls1、-dtls1、-no_ssl2、-no_ssl3、-no_tls1、-no_tls1_1、-no_tls1_2:使用的協議狀態值。

-no_dhe:如果這個選項被設置,則沒有DH參數提供,即不能夠使用關於DH相關的ciphers。

-no_ecdhe:能夠使用關於ECDH相關的ciphers。

-bugs:打印所有的調試信息。

-hack:這個選項對更早的Netscape SSL代碼提供一個可行的解決方案。

-www:當client連接上來的時候,發回一個網頁,內容就是SSL握手的一些內容。

-HTTP、-WWW:用來把具體某個文件當網頁發回給client的請求。比如client的URL請求是 https://myhost/page.html ,就把 ./page.html發回給client。-engine id:硬件引擎。

-tlsextdebug:打印TLS協議中服務器端接收到的額外信息值。

-no_ticket:不支持RFC4507bis會話類型。

-id_prefix arg:根據arg的值來產生SSL/TLS中的session IDs的前綴。當有多個服務器的時候,大多數用於測試SSL/TLS代碼。

-rand file(s):指定隨機數種子文件,多個文件間用分隔符分開,windows用“;”,OpenVMS用“,“,其他系統用“:”。

連接的命令:

沒有設置 –www、 -WWW這倆個選項,當一個ssl client連接上來的話它所發過來的任何東西都會顯示出來,你在終端輸入的任何東西都會發回給client。你可以通過在終端輸入的行的第一個字母控制一些行爲。

q:

    中斷當前連接,但不關閉server.

Q

    中斷當前連接,退出程序。

r

    進行renegotiate行爲。

R

    進行renegotiate行爲, 並且要求client的證書。

P

    在TCP層直接送一些明文。這會使client認爲我們沒有按協議的遊戲規則進行通信而斷開連接。

S

    打印出session-cache的狀態信息。session-cache在編程章節會詳細介紹。

注意:

B<s_server>可以用於調試SSL客戶端。爲了從一個web瀏覽器接受連接,命令如下:

openssl s_server -accept 443 -www

可以這樣使用。

大多數的web(Netscape 和 MSIE除外)僅僅支持RSA算法套件。因此當使用非RSA格式的證書時,他們不能夠連接服務器。

即使指定一個空的CA列表,當請求一個客戶端證書時敬愛那個嚴重的違反協議標準,一些SSL客戶端解釋說任何CA都可以接受。這個對調試證書用途非常有效。

可以使用B<sess_id>命令來打印出使用的session參數。

BUGs:

因爲該項目有很多選項,好多用的是老的技術,c代碼的s_client很難去讀取爲什麼會被關閉。一個典型的SSL客戶端項目將會更加簡單的。

普通的算法輸出是個錯誤:它僅僅給出了OpenSSL確認的客戶端支持的算法套件列表。

 

這是一個方法:打印客戶端支持的不知名的算法套件的詳細信息值。

 

1.2 s_client的man函數

 

NAME

       s_client - SSL/TLS client program

 

SYNOPSIS

       openssl s_client [-connect host:port] [-verify depth] [-cert filename]

       [-certform DER|PEM] [-key filename] [-keyform DER|PEM] [-pass arg] [-CApath

       directory] [-CAfile filename] [-trusted_first] [-krb5svc service] [-keytab

       filename] [-reconnect] [-pause] [-showcerts] [-debug] [-msg] [-nbio_test]

       [-state] [-nbio] [-crlf] [-ign_eof] [-no_ign_eof] [-quiet] [-ssl2] [-ssl3]

       [-tls1] [-tls1_1] [-tls1_2] [-dtls1] [-no_ssl2] [-no_ssl3] [-no_tls1]

       [-no_tls1_1] [-no_tls1_2] [-fallback_scsv] [-bugs] [-cipher cipherlist]

       [-starttls protocol] [-engine id] [-tlsextdebug] [-no_ticket] [-sess_out

       filename] [-sess_in filename] [-rand file(s)] [-nextprotoneg protocols]

 

DESCRIPTION

       The s_client command implements a generic SSL/TLS client which connects to

       a remote host using SSL/TLS. It is a very useful diagnostic tool for SSL

       servers.

 

OPTIONS

       -connect host:port

           This specifies the host and optional port to connect to. If not

           specified then an attempt is made to connect to the local host on port

           4433.

 

       -cert certname

           The certificate to use, if one is requested by the server. The default

           is not to use a certificate.

 

 

選項說明:

-host host:設置服務地址。

-port port:設置服務端口,默認爲4433。

-connect host:port:設置服務器地址和端口號。如果沒有設置,則默認爲本地主機以及端口號4433。

-verify depth:設置證書的驗證深度。記得CA也是分層次的吧?如果對方的證書的簽名CA不是Root CA,那麼你可以再去驗證給該CA的證書籤名的CA,一直到Root CA. 目前的驗證操作即使這條CA鏈上的某一個證書驗證有問題也不會影響對更深層的CA的身份的驗證。所以整個CA鏈上的問題都可以檢查出來。當然CA的驗證出問題並不會直接造成連接馬上斷開,好的應用程序可以讓你根據驗證結果決定下一步怎麼走。

-cert filename:使用的證書文件。如果server不要求要證書,這個可以省略。

-certform DER|PEM:證書的格式,一般爲DER和PEM。默認爲PEM格式。

-key filename:使用的證書私鑰文件。

-keyform DER|PEM:證書私鑰文件的格式,一般爲DER和PEM。默認爲PEM格式。

-pass arg:私鑰保護口令來源,比如:-pass file:pwd.txt,將私鑰保護口令存放在一個文件中,通過此選項來指定,不需要用戶來輸入口令。

-CApath directory:設置信任CA文件所在路徑,此路徑中的ca文件名採用特殊的形式:xxx.0,其中xxx爲CA證書持有者的哈希值,它通過x509 -hash命令獲得。

-CAfile filename:某文件,裏面是所有你信任的CA的證書的內容。當你要建立client的證書鏈的時候也需要用到這個文件。

-reconnect:使用同樣的session-id連接同一個server五次,用來測試server的session緩衝功能是否有問題。

-pause:每當讀寫數據時,sleep 1秒。

-showcerts:顯示整條server的證書的CA的證書鏈。否則只顯示server的證書。

-debug:打印所有的調試信息。

-msg:用16進制顯示所有的協議數據。

-state:打印SSL session的狀態, ssl也是一個協議,當然有狀態。

-nbio_test:檢查非阻塞socket的I/O運行情況。

-nbio:使用非阻塞socket。

-crlf:把在終端輸入的換行回車轉化成/r/n送出去。

-ign_eof:當輸入文件到達文件尾的時候並不斷開連接。

-no_ign_eof:當輸入文件到達文件尾的時候斷開連接。

-quiet:不打印出session和證書的信息。同時會打開-ign_eof這個選項。

-ssl2、-ssl3、-tls1_1、-tls1_2、-tls1、-dtls1、-no_ssl2、-no_ssl3、-no_tls1、-no_tls1_1、-no_tls1_2:使用的協議狀態值。

-bugs:兼容老版本服務端的中的bug。

-cipher cipherlist:由我們自己來決定選用什麼加密算法,儘管是由server來決定使用什麼算法列表,但它一般都會採用我們送過去的cipher列表裏的第一個cipher。

-starttls protocol:protocol可以爲smtp或pop3,用於郵件安全傳輸。

-engine id:硬件引擎。

-tlsextdebug:打印TLS協議中服務器端接收到的額外信息值。

-no_ticket:不支持RFC4507bis會話類型。

-sess_out filename:輸出SSL會話信息值到filename中。

-sess_in filename:從filename中獲取SSL Session值。

-rand file(s):指定隨機數種子文件,多個文件間用分隔符分開,windows用“;”,OpenVMS用“,“,其他系統用“:”。

連接選項:

如果一個確認的連接到SSL服務器,並顯示了從服務器端接收到了的數據,任何操作都被髮送到服務器。當交互(這意味着沒有給出B<-quiet> 、B<-ign_eof>這兩個選項)的時候,如果命令行B<R>,被設置則session有可能會被重啓。如果設置的是命令行B<Q>或到達了文件的結尾,連接將會被斷開。

注意:

S_client可用於調試SSL服務器端。爲了連接一個SSL HTTP服務器,命令如下:

openssl s_client -connect servername:443

一旦和某個SSL server建立連接之後,所有從server得到的數據都會被打印出來,所有你在終端上輸入的東西也會被送給server. 這是人機交互式的。這時候不能設置-quiet和 -ign_eof這倆個選項。如果輸入的某行開頭字母是R,那麼在這裏session會重啓, 如果輸入的某行開頭是Q,那麼連接會被斷開。你完成整個輸入之後連接也會被斷開。

如果連接成功,你可以用HTTP的指令,比如"GET /"什麼的去獲得網頁了。

如果握手失敗,原因可能有以下幾種:

1.          server需要驗證你的證書,但你沒有證書。

2.          如果肯定不是原因1,那麼就慢慢一個一個set以下幾個選項:-bugs, -ssl2, -ssl3, -tls1,-no_ssl2,-no_ssl3, -no_dtls。

3.          這可能是因爲對方的server處理SSL有bug。

有的時候,client會報錯:沒有證書可以使用,或者供選擇的證書列表是空的。這一般是因爲Server沒有把給你簽名的CA的名字列進它自己認爲可以信任的CA列表,你可以用檢查一下server的信任CA列表。有的http server只在 client給出了一個URL之後才驗證client的證書,這中情況下要設置 -prexit這個選項,並且送給server一個頁面請求。

即使使用-cert指明使用的證書,如果server不要求驗證client的證書,那麼該證書也不會被驗證。所以不要以爲在命令行里加了-cert 的參數又連接成功就代表你的證書沒有問題。

如果驗證server的證書有問題,就可以設置-showcerts來看看server的證書的CA鏈了。

自從SSLv23客戶端hello不能夠包含壓縮方法或擴展僅僅會被支持。

BUGs:

因爲該項目有很多選項,好多用的是老的技術,c代碼的s_client很難去讀取爲什麼會被關閉。一個典型的SSL客戶端項目將會更加簡單的。

如果服務器驗證失敗,B<-verify>將會退出。

B<-prexit>選項是一個很小的空間。當一個session重啓後,我們必須報告。

參考--https://blog.csdn.net/as3luyuan123/article/details/16812071

 

二.分別製作服務器與客戶端的密鑰與證書

2.1服務器——

[a4729821@JYstd socket_key]$ openssl req -x509 -days 365 -newkey rsa:2048 -keyout server.pem -out server.pem

Generating a 2048 bit RSA private key

.........................................................+++

..+++

writing new private key to 'server.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:CN

State or Province Name (full name) []:Hubei

Locality Name (eg, city) [Default City]:Wuhan  

Organization Name (eg, company) [Default Company Ltd]:lingyun

Organizational Unit Name (eg, section) []:IT

Common Name (eg, your name or your server's hostname) []:www.10086.com

Email Address []:[email protected]

 

[a4729821@JYstd socket_key]$ ls

server.pem

 

 

 

打印信息

[a4729821@JYstd socket_key]$ openssl x509 -in server.pem -noout -text          

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 14208309324865622410 (0xc52e0e0d577c518a)

    Signature Algorithm: sha1WithRSAEncryption

        Issuer: C=CN, ST=Hubei, L=Wuhan, O=lingyun, OU=IT, CN=www.10086.com/emailAddress=[email protected]

        Validity

            Not Before: Jul 31 06:32:50 2018 GMT

            Not After : Jul 31 06:32:50 2019 GMT

        Subject: C=CN, ST=Hubei, L=Wuhan, O=lingyun, OU=IT, CN=www.10086.com/emailAddress=[email protected]

        Subject Public Key Info:

            Public Key Algorithm: rsaEncryption

                Public-Key: (2048 bit)

                Modulus:

                    00:dd:b3:08:d5:be:2f:94:ae:9c:b7:9e:0b:22:1e:

                    d4:f6:06:55:bf:55:06:fe:6b:e5:33:cd:c7:ed:e8:

                    75:46:81:c1:51:d9:90:f8:ec:28:50:81:4b:5a:4d:

                    df:7e:59:97:48:43:4d:59:a6:6b:f0:54:60:51:16:

                    2b:7a:8f:77:cc:09:ca:1e:7d:a5:ac:bf:c4:65:5a:

                    1b:e8:f9:61:1b:fa:8a:00:7a:e1:99:2e:ad:f5:6b:

                    74:49:c8:f1:fa:bb:d7:8f:d8:89:c4:cc:81:de:1a:

                    61:94:df:fc:ff:94:74:f2:2c:cf:83:60:2a:12:7d:

                    0f:bd:e8:10:12:eb:12:52:26:08:1d:88:5b:e8:24:

                    ae:e2:35:3b:3f:3a:45:90:b2:e6:d3:ae:15:7a:6d:

                    9c:6e:84:fa:4e:00:4d:90:bc:2f:e4:26:a0:ce:42:

                    61:f6:c1:4f:54:32:06:ed:21:28:2b:65:b3:c0:d3:

                    df:fe:5a:f6:25:8d:15:15:06:97:4d:17:df:25:ef:

                    3d:2c:a9:7e:88:15:f5:cb:86:ac:7d:79:6e:c4:d3:

                    ce:17:08:67:4a:03:68:bd:6d:11:cd:d3:f9:58:99:

                    2c:c3:f9:25:56:83:2c:6a:9b:9a:5a:0b:48:ce:4b:

                    f4:24:71:27:36:df:3c:70:24:4e:9e:5f:41:ee:c9:

                    7a:d7

                Exponent: 65537 (0x10001)

        X509v3 extensions:

            X509v3 Subject Key Identifier:

                08:1C:FE:EA:93:CC:43:67:BC:A1:34:24:75:4F:F3:A9:7B:53:89:BB

            X509v3 Authority Key Identifier:

                keyid:08:1C:FE:EA:93:CC:43:67:BC:A1:34:24:75:4F:F3:A9:7B:53:89:BB

 

            X509v3 Basic Constraints:

                CA:TRUE

    Signature Algorithm: sha1WithRSAEncryption

         d8:29:4e:a7:64:97:0f:3f:fd:1f:d8:c2:c2:54:09:2f:ff:2c:

         be:8e:c0:20:64:20:a5:4c:b0:6d:9d:45:62:c1:07:39:2b:7d:

         3b:e0:1f:e9:c5:a6:a8:c6:ca:f3:51:4a:6c:07:be:11:7c:e9:

         9e:c6:ec:12:38:68:91:67:c8:f1:cb:29:db:1f:10:b5:9e:8a:

         be:f0:07:f4:41:c9:9f:f4:9d:12:17:53:46:f9:5f:0c:fc:32:

         39:11:cf:1c:4c:df:da:54:6f:93:2f:73:0f:97:5a:fe:69:88:

         b1:22:22:e4:53:41:5e:b9:2d:ef:4c:03:ed:eb:c4:89:46:85:

         87:8b:f4:7a:3c:9c:e4:ac:c3:4d:81:2e:40:20:4e:8c:39:31:

         e0:5f:da:cb:50:b2:0a:e2:4b:04:c6:a1:08:92:74:ad:63:fd:

         c4:99:3b:42:1e:b2:6d:fd:01:4c:f8:89:a2:3c:e3:43:5f:6d:

         5f:02:8f:4f:fd:f6:74:97:b6:8d:c0:bb:68:6c:c3:61:24:5a:

         dd:ed:03:96:6a:0b:63:65:31:46:01:69:92:99:c2:03:fb:db:

         e0:f6:76:79:dc:a1:3e:64:dd:4c:3f:37:80:00:ef:8c:3c:fc:

         6f:e7:82:f5:81:81:54:45:34:6a:8c:6e:5a:fa:2b:49:1e:fd:

         98:16:16:7f

 

2.2客戶端——

[a4729821@JYstd socket_key]$ openssl req -x509 -days 365 -newkey rsa:2048 -keyout client.pem -out client.pem   

Generating a 2048 bit RSA private key

............+++

..................+++

writing new private key to 'client.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:CN

State or Province Name (full name) []:guangxi

Locality Name (eg, city) [Default City]:nanning

Organization Name (eg, company) [Default Company Ltd]:lanxiang

Organizational Unit Name (eg, section) []:muzhu

Common Name (eg, your name or your server's hostname) []:www.10000.com

Email Address []:[email protected]

打印密鑰信息

[a4729821@JYstd socket_key]$ openssl x509 -in client.pem -noout -text

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 13484683701559912667 (0xbb233861b3fa68db)

    Signature Algorithm: sha1WithRSAEncryption

        Issuer: C=CN, ST=guangxi, L=nanning, O=lanxiang, OU=muzhu, CN=www.10000.com/emailAddress=[email protected]

        Validity

            Not Before: Jul 31 06:37:16 2018 GMT

            Not After : Jul 31 06:37:16 2019 GMT

        Subject: C=CN, ST=guangxi, L=nanning, O=lanxiang, OU=muzhu, CN=www.10000.com/emailAddress=[email protected]

        Subject Public Key Info:

            Public Key Algorithm: rsaEncryption

                Public-Key: (2048 bit)

                Modulus:

                    00:d9:8b:04:41:27:bc:a1:e3:cc:6e:96:81:ee:50:

                    82:9e:93:bb:ee:8c:3c:6b:55:8b:f7:50:95:d2:d6:

                    cd:36:b6:d4:b2:9b:85:d9:87:2e:c7:42:74:b3:53:

                    64:54:46:86:2e:df:57:f9:6a:a8:47:ac:da:ac:fa:

                    38:08:94:34:d2:b9:b0:b9:fa:a6:31:ba:79:a2:15:

                    3f:2d:65:12:32:05:59:ba:d6:f6:df:73:2b:d6:76:

                    eb:d4:92:d1:93:49:f3:4e:22:f1:7b:4c:0d:df:9f:

                    1b:63:a6:53:43:11:f9:e6:ca:39:6e:cc:62:cb:d2:

                    96:c1:3c:c3:4d:bc:49:b6:24:1f:8d:72:34:66:0a:

                    21:86:94:d2:e0:94:18:57:8d:63:0e:a6:51:69:91:

                    d4:7c:95:d1:2f:37:07:b5:20:29:16:20:cd:61:b0:

                    50:84:4b:a7:d7:c5:bd:3f:67:c6:4d:49:f6:57:f9:

                    48:f8:8c:27:4b:8c:ff:ae:0c:b4:cf:8b:63:c8:fc:

                    0b:47:86:3c:85:ee:f9:f8:d4:a7:5f:4b:3f:f9:d5:

                    46:a7:b7:ec:b5:0c:f9:04:49:a0:eb:66:f7:ea:03:

                    23:0c:fe:b7:4b:ed:36:0d:7e:3a:69:87:57:7b:0b:

                    0d:05:99:ab:29:f0:0b:fd:56:df:5d:f3:1e:bc:82:

                    1c:0f

                Exponent: 65537 (0x10001)

        X509v3 extensions:

            X509v3 Subject Key Identifier:

                5F:EA:A5:0C:C5:BD:4A:6D:39:DB:E0:AA:29:BB:69:BC:78:14:9D:5A

            X509v3 Authority Key Identifier:

                keyid:5F:EA:A5:0C:C5:BD:4A:6D:39:DB:E0:AA:29:BB:69:BC:78:14:9D:5A

 

            X509v3 Basic Constraints:

                CA:TRUE

    Signature Algorithm: sha1WithRSAEncryption

         bf:54:86:2b:2d:ba:d5:0c:4e:5b:e4:05:c1:a1:8f:1e:b4:07:

         3b:02:11:3a:d9:fa:c4:ac:8e:13:77:90:bd:49:3a:60:6b:0d:

         ad:07:72:07:4a:9c:ad:2e:22:91:a5:cb:d0:8c:0e:22:c4:2a:

         fd:ea:fd:32:5e:63:51:c6:c6:9b:fb:af:0a:26:79:85:e7:b3:

         7b:3a:45:60:c4:fd:43:82:d5:b9:e5:e6:e7:b1:74:c3:36:46:

         41:b9:7c:a9:5d:9e:a8:53:16:3f:29:33:b4:b2:67:c3:c8:13:

         61:89:42:db:46:26:3c:a3:80:ef:95:11:93:73:8f:f6:6f:38:

         73:7c:dc:5f:97:4c:2a:53:05:cd:45:6b:71:80:29:51:a0:25:

         6d:04:23:06:9b:6f:15:18:97:ba:c9:11:7f:2b:18:38:8b:b3:

         c8:51:e9:26:6d:e7:20:94:ab:ed:e7:e8:e6:6c:79:34:75:1f:

         26:d1:0c:cf:28:75:26:f0:fb:c5:6a:1c:4b:16:51:6d:0e:96:

         6f:db:d5:a5:0b:20:b9:25:33:c4:fd:70:d8:45:fc:55:f2:6e:

         8a:a5:a9:c2:cb:4b:70:29:71:cf:7e:1a:4e:2b:67:3b:7b:73:

         72:e1:54:7b:6e:44:e7:85:ef:34:07:70:30:d0:fa:87:6e:53:

         5b:36:95:fc

三.

OpenSSL是一個開放源代碼的SSL協議的產品實現,它採用C語言作爲開發語言,具備了跨系統的性能。調用OpenSSL的函數就可以實現一個SSL加密的安全數據傳輸通道,從而保護客戶端和服務器之間數據的安全。

 

頭文件:

#include <openssl/ssl.h>

#include <openssl/err.h>

基於OpenSSL的程序都要遵循以下幾個步驟:

 

(1 ) OpenSSL初始化

在使用OpenSSL之前,必須進行相應的協議初始化工作,這可以通過下面的函數實現:

int SSL_library_init(void);

SSL_load_error_strings();

OpenSSL_add_ssl_algorithms();

(2 ) 選擇會話協議

在利用OpenSSL開始SSL會話之前,需要爲客戶端和服務器制定本次會話採用的協議,目前能夠使用的協議包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。

需要注意的是,客戶端和服務器必須使用相互兼容的協議,否則SSL會話將無法正常進行。

ctx = SSL_CTX_new(method); /* Create new context */

SSLv3_client_method 是指定ssl要使用的協議。SSL協議由美國 NetScape公司開發的,V1.0版本從沒有公開發表過;V2.0版本於1995年2月發佈。但是,由於V2.0版本有許多安全漏洞,所以,1996年緊接着就發佈了V3.0版本。微軟從IE 7開始就已經把瀏覽器的缺省設置不支持SSL 2.0,但可能是考慮到有些網站還只支持SSL 2.0,所以IE瀏覽器留了一個可以由用戶設置支持SSL 2.0的選項,以便能正常訪問只支持SSL 2.0的網站。IE7/IE8支持SSL 3.0和TLS1.0,而IE9還支持TLS1.1和1.2。

在openssl裏指定協議很簡單,每個協議都有對應的函數,一行代碼就可以搞定。

SSL_METHOD* TLSv1_client_method(void); TLSv1.0 協議

SSL_METHOD* SSLv2_client_method(void); SSLv2 協議

SSL_METHOD* SSLv3_client_method(void); SSLv3 協議

SSL_METHOD* SSLv23_client_method(void); SSLv2/v3 協議

SSL_CTX_new創建ssl上下文,這裏面很多全局變量要被各個階段共享

(3 ) 創建會話環境

在OpenSSL中創建的SSL會話環境稱爲CTX,使用不同的協議會話,其環境也不一樣的。

 

申請SSL會話環境的OpenSSL函數是:

SSL_CTX *SSL_CTX_new(SSL_METHOD * method);

 

當SSL會話環境申請成功後,還要根據實際的需要設置CTX的屬性,通常的設置是指定SSL握手階段證書的驗證方式和加載自己的證書。

制定證書驗證方式的函數是:

int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *));

設置證書驗證的方式。第一個參數是當前的CTX指針,第二個是驗證方式,如果是要驗證對方的話,就使用SSL_VERIFY_PEER。不需要的話,使用 SSL_VERIFY_NONE.一般情況下,客戶端需要驗證對方,而服務器不需要。第三個參數是處理驗證的回調函數,如果沒有特殊的需要,使用空指針就可以了。

 

爲SSL會話環境加載CA證書的函數是:

SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath);

 

 SSL_CTX_load_verify_locations用於加載受信任的CA證書,CAfile如果不爲NULL,則他指向的文件包含PEM編碼格式的一個或多個證書,可以用e.g.來簡要介紹證書內容

  CApath如果不爲NULL,則它指向一個包含PEM格式的CA證書的目錄,目錄中每個文件包含一份CA證書,文件名是證書中CA名的HASH值

證書的編碼類型一般分爲兩種:PEM格式和ASN1格式,即Base64編碼格式和DER編碼文件,它們分別傳入SSL_FILETYPE_PEM、SSL_FILETYPE_ASN1

缺省mode是SSL_VERIFY_NONE,如果想要驗證對方的話,便要將此項變成SSL_VERIFY_PEER.SSL/TLS中缺省只驗證server,如果沒有設置 SSL_VERIFY_PEER的話,客戶端連證書都不會發過來

//驗證方式

SSL_VERIFY_NONE

           Server mode: the server will not send a client certificate

           request to the client, so the client will not send a

           certificate.

 

           Client mode: if not using an anonymous cipher (by default

           disabled), the server will send a certificate which will be

           checked. The result of the certificate verification process can

           be checked after the TLS/SSL handshake using the

           SSL_get_verify_result(3) function.  The handshake will be

           continued regardless of the verification result.

 

       SSL_VERIFY_PEER

           Server mode: the server sends a client certificate request to

           the client.  The certificate returned (if any) is checked. If

           the verification process fails, the TLS/SSL handshake is

           immediately terminated with an alert message containing the

           reason for the verification failure.  The behaviour can be

           controlled by the additional SSL_VERIFY_FAIL_IF_NO_PEER_CERT and

           SSL_VERIFY_CLIENT_ONCE flags.

 

           Client mode: the server certificate is verified. If the

           verification process fails, the TLS/SSL handshake is immediately

           terminated with an alert message containing the reason for the

           verification failure. If no server certificate is sent, because

           an anonymous cipher is used, SSL_VERIFY_PEER is ignored.

 

       SSL_VERIFY_FAIL_IF_NO_PEER_CERT

           Server mode: if the client did not return a certificate, the

           TLS/SSL handshake is immediately terminated with a "handshake

           failure" alert.  This flag must be used together with

           SSL_VERIFY_PEER.

 

           Client mode: ignored

 

爲SSL會話加載用戶證書的函數是:

SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type);

 

爲SSL會話加載用戶私鑰的函數是:

SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type);

 

在將證書和私鑰加載到SSL會話環境之後,就可以調用下面的函數來驗證私鑰和證書是否相符:

int SSL_CTX_check_private_key(SSL_CTX *ctx);

 

(4) 建立SSL套接字

SSL套接字是建立在普通的TCP套接字基礎之上,在建立SSL套接字時可以使用下面的一些函數:

 

SSL *SSl_new(SSL_CTX *ctx);

//申請一個SSL套接字

 

int SSL_set_fd(SSL *ssl,int fd);)

//綁定讀寫套接字

 

int SSL_set_rfd(SSL *ssl,int fd);

//綁定只讀套接字

 

int SSL_set_wfd(SSL *ssl,int fd);

//綁定只寫套接字

 

(5) 完成SSL握手

在成功創建SSL套接字後,客戶端應使用函數SSL_connect( )替代傳統的函數connect( )來完成握手過程:

int SSL_connect(SSL *ssl);

 

而對服務器來講,則應使用函數SSL_ accept ( )替代傳統的函數accept ( )來完成握手過程:

int SSL_accept(SSL *ssl);

 

握手過程完成之後,通常需要詢問通信雙方的證書信息,以便進行相應的驗證,這可以藉助於下面的函數來實現:

 

X509 *SSL_get_peer_certificate(SSL *ssl);

該函數可以從SSL套接字中提取對方的證書信息,這些信息已經被SSL驗證過了。

 

X509_NAME *X509_get_subject_name(X509 *a);

該函數得到證書所用者的名字。

 

(6) 進行數據傳輸

當SSL握手完成之後,就可以進行安全的數據傳輸了,在數據傳輸階段,需要使用SSL_read( )和SSL_write( )來替代傳統的read( )和write( )函數,來完成對套接字的讀寫操作:

int SSL_read(SSL *ssl,void *buf,int num);

int SSL_write(SSL *ssl,const void *buf,int num);

 

(7 ) 結束SSL通信

當客戶端和服務器之間的數據通信完成之後,調用下面的函數來釋放已經申請的SSL資源:

 

int SSL_shutdown(SSL *ssl);

//關閉SSL套接字

 

void SSl_free(SSL *ssl);

//釋放SSL套接字

 

void SSL_CTX_free(SSL_CTX *ctx);

//釋放SSL會話環境

 

服務器代碼:

/*********************************************************************************

*      Copyright:  (C) 2018 lingyun

*                  All rights reserved.

*

*       Filename:  serve.c

*    Description:  This file

*                 

*        Version:  1.0.0(08/07/2018)

*         Author:  huangjy <[email protected]>

*      ChangeLog:  1, Release initial version on "08/07/2018 02:31:27 PM"

*                 

********************************************************************************/

#include <stdio.h>

#include <string.h>

#include <errno.h>

#include <stdlib.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <resolv.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <openssl/ssl.h>

#include <openssl/err.h>

 

#define MAXBUF 1024

#define CACERT "./ca/ca.crt"

#define MYKEY "./serve_key/serve.key"

#define MYCERT "./serve_key/serve.crt"

 

void ShowCerts(SSL *ssl)

{

    X509 *cert;

    char *line;

 

    cert = SSL_get_peer_certificate(ssl);

    

    if(cert != NULL)

    {

        line = X509_NAME_oneline(X509_get_subject_name(cert),0,0);

        printf("cert: %s\n",line);

        free(line);

        //獲取證書擁有者

 

        line = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);

        printf("Issuer: %s\n",line);

        free(line);

        //獲取證書發佈者

 

        X509_free(cert);

    }

    else

    {

     

        printf("no cert message \n");

 

    }

}

 

int main(int argc,char **argv)

{

     int sock_fd,conn_fd;

     char buf[MAXBUF];

     unsigned int port;

     unsigned int recv_length;

 

     struct sockaddr_in peeraddr;

     struct sockaddr_in serveraddr;

     socklen_t peer_len = sizeof(peeraddr) ;

 

     SSL *ssl;

     SSL_CTX *ctx; //會話環境

 

     SSL_library_init();

     OpenSSL_add_all_algorithms();

     SSL_load_error_strings();

     ctx = SSL_CTX_new(SSLv23_server_method());

     memset(&serveraddr,0,sizeof(serveraddr));

 

     if(ctx == NULL)

     {

        ERR_print_errors_fp(stdout);

        exit(1);

     }

     

     if(argv[1])

     {

        serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

     }

     else

     {

         serveraddr.sin_addr.s_addr = htons(INADDR_ANY);

     }

 

     printf("serveraddr = %d\n",serveraddr.sin_addr.s_addr);

 

     if(argv[2])

     {

        port = atoi(argv[2]);

     }

     else

     {

        port = 8888;   

     }

 

     printf("port = %d\n",port);

 

      serveraddr.sin_family = AF_INET;

      serveraddr.sin_port = htons(port);

 

      if((sock_fd = socket(AF_INET,SOCK_STREAM,0))<0)

     {

       perror("Socket");

       exit(errno);

 

     }

             

       printf("Socket created! \n");

 

 

      if(bind(sock_fd,(const struct sockaddr *)&serveraddr,sizeof(serveraddr))<0)

      {

        perror("blind");

        exit(1);

      }

 

      if(listen(sock_fd,10)<0)

      {

         perror("listen");

         exit(1);

      }

       

      SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);  //設置驗證方式

      SSL_CTX_load_verify_locations(ctx,CACERT,NULL);  //加載CA證書

     

      if( SSL_CTX_use_certificate_file(ctx,MYCERT,SSL_FILETYPE_PEM)<0)//加載服務器證書

     {

        ERR_print_errors_fp(stdout);

        exit(1);

     }

      

     printf("加載服務器證書成功!\n");

 

     if( SSL_CTX_use_PrivateKey_file(ctx,MYKEY,SSL_FILETYPE_PEM)<0)//加載服務器私鑰

     {

          ERR_print_errors_fp(stdout);

          exit(1);

     }

      

      printf("加載服務器私鑰成功!\n");

 

     if(SSL_CTX_check_private_key(ctx)<0) //驗證密鑰與證書是否匹配

     {

      ERR_print_errors_fp(stdout);

       exit(1);

     }

     printf("證書與密鑰是匹配的\n");

    

    printf("blind and listen success 等待客戶端\n");

     while(1)

     {

         if((conn_fd = accept(sock_fd,(struct sockaddr *)&peeraddr,&peer_len))<0)

       {

        perror("accept");

        exit(errno);

       }

       else

       {

        printf("\n======客戶端連接成功=====\n");

        printf(" IP = %s ,PORT = %d \n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));

       }

       

       ssl = SSL_new(ctx); //申請套接字

       SSL_set_fd(ssl,conn_fd); //綁定讀寫套接字

        

        if(SSL_accept(ssl)<0)

        {

          ERR_print_errors_fp(stdout);

          close(conn_fd);

          break;

        }

        else

        {

          printf("Accepted with %s encryption\n",SSL_get_cipher(ssl));

          ShowCerts(ssl);

        }

        

        

        while(1)

        {

           memset(buf,0,sizeof(buf));

           printf("start read from client:");

           if((recv_length = SSL_read(ssl,buf,sizeof(buf)))<0)

           {

             printf("client has closed!\n");

             break;

           }

           else

           {

            printf("client say [%d] bytes:%s\n",recv_length,buf);

           }

 

           memset(buf,0,sizeof(buf));

           fgets(buf,sizeof(buf),stdin);

           SSL_write(ssl,buf,strlen(buf));

 

        }

 

 

        

         SSL_shutdown(ssl);

         SSL_free(ssl);

         close(conn_fd);

 

        

     }

       

         SSL_CTX_free(ctx);

         close(sock_fd);

         

         return 0;

}

 

客戶端代碼:

/*********************************************************************************

      Copyright:  (C) 2018 lingyun

*                  All rights reserved.

*

*       Filename:  client.c

*    Description:  This file

*                 

*        Version:  1.0.0(08/05/2018)

*         Author:  huangjy <[email protected]>

*      ChangeLog:  1, Release initial version on "08/05/2018 02:55:12 PM"

*                 

********************************************************************************/

#include <stdio.h>  

#include <string.h>

#include <sys/types.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>  

 

#define CACERT "./ca/ca.crt"     

#define MYKEY  "./client_key/client.key"

#define MYCERT "./client_key/client.crt"

#define MAXBUF 1024  

      

void ShowCerts(SSL * ssl)  

 

  {  

    X509 *cert;  

    char *line;  

    

    cert = SSL_get_peer_certificate(ssl);   //從SSL套接字提取證書信息

 

        if (cert != NULL)

       {

           

           printf("num cert messsage:\n");  

           line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);  

           printf("cert: %s\n", line); //獲取證書擁有者名字

           free(line);

 

           line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);  

           printf("Issuer: %s\n", line);  

           free(line);   //獲取證書頒佈者名字

 

           X509_free(cert);  

 

                                                                                   }

        else

        {

 

         printf("no cert message \n");

        }      

     }  

 

      

 

    int main(int argc, char **argv)  

 

        {  

 

            int sockfd, len;  

            struct sockaddr_in dest;  

            char buffer[MAXBUF];  

            const char *server_ip = argv[1];

 

            SSL_CTX *ctx;  

            SSL *ssl;  

 

                                          

 

            if (argc !=3)

          {  

 

            printf("err param :\n\t%s IPaddress port:\nexample:\t%s 127.0.0.1 80\n",argv[0], argv[0]);

            exit(0);  

 

          }  

 

                                              

 

            SSL_library_init();    // 初始化算法庫

            OpenSSL_add_all_algorithms();  

            SSL_load_error_strings();  

            ctx = SSL_CTX_new(SSLv23_client_method());  

 

              if (ctx == NULL)

             {  

                                                            

               ERR_print_errors_fp(stdout);  

               exit(1);  

                                                                            

 

              }  

 

                                                                  

 

            if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

            {  

 

                perror("Socket");  

                exit(errno);  

 

            }  

 

              printf("socket created\n");  

              

              SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);//設置通信驗證方式

              SSL_CTX_load_verify_locations(ctx,CACERT,NULL);  //加載CA證書

     

              if(SSL_CTX_use_certificate_file(ctx,MYCERT,SSL_FILETYPE_PEM)<=0)

              {

               ERR_print_errors_fp(stdout);

               exit(1);

              }

              

              printf("加載用戶證書成功!\n");

 

              if(SSL_CTX_use_PrivateKey_file(ctx,MYKEY,SSL_FILETYPE_PEM)<=0)

              {

               ERR_print_errors_fp(stdout);

               exit(1);

              }

              

              printf("加載用戶密鑰成功!\n");

 

              if(SSL_CTX_check_private_key(ctx)<0)

              {

                  ERR_print_errors_fp(stdout);

                  exit(1);

              }

           printf("證書密鑰是匹配的!\n");

 

            bzero(&dest, sizeof(dest));  

            dest.sin_family = AF_INET;  

            dest.sin_port = htons(atoi(argv[2]));  

             

            if((inet_pton(AF_INET,server_ip,(struct in_addr *)&dest.sin_addr.s_addr))<0)  //十進制的ip地址轉化爲用於網絡傳輸的數值格式

             {

               perror(argv[1]);

               exit(errno);

             }    

                printf("address created\n");  

 

            if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)

            {  

 

                perror("Connect ");  

 

                exit(errno);    

 

            }  

 

                printf("server connected\n");  

 

                ssl = SSL_new(ctx);  

                

                SSL_set_fd(ssl, sockfd);  

 

            if (SSL_connect(ssl) == -1)  

            {

             ERR_print_errors_fp(stderr);  

            }

            else

           {  

           printf("Connected with %s encryption\n", SSL_get_cipher(ssl));  

           ShowCerts(ssl);  

 

           }  

           

           bzero(buffer, MAXBUF);  

           printf("Read string from stin: \n");

 

           while( fgets(buffer,sizeof(buffer),stdin)!=NULL && strncmp(buffer,"quit", 4) )

           {

               printf("strcmp: %s strlen: %lu\n", buffer, strlen(buffer));

               SSL_write(ssl,buffer,strlen(buffer));

               printf("send success!\n");

 

               memset(buffer,0,sizeof(buffer));

 

               printf("star to read data from serve:\n");

 

               len = SSL_read(ssl,buffer,sizeof(buffer));

               printf("serve say %d byte:%s\n",len,buffer);

               memset(buffer,0,sizeof(buffer));

               printf("Read string from: \n");

               

           }

               

            printf("client will be closed.see you next time.\n");

             

            SSL_shutdown(ssl);

            close(sockfd);

            SSL_free(ssl);

            SSL_CTX_free(ctx);

           

            return 0;  

 

         }  

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章