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 []:771018493@qq.com

 

[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=771018493@qq.com

        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=771018493@qq.com

        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 []:1158410865@qq.com

打印密钥信息

[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=1158410865@qq.com

        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=1158410865@qq.com

        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 <771018493@qq.com>

*      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 <771018493@qq.com>

*      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;  

 

         }  

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页