Chinaunix首页 | 论坛 | 博客
  • 博客访问: 167405
  • 博文数量: 39
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 214
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-21 11:10
文章分类

全部博文(39)

文章存档

2017年(6)

2016年(1)

2015年(6)

2014年(26)

我的朋友

分类: C/C++

2017-07-12 13:57:51




一、加密和算法

1.1   散列(hash)

Hash,一般翻译做"",也有直接音译为""的,就是把任意长度的输入,通过,变换成固定长度的输出,该输出就是。常用的散列函数是SHA1MD5

哈希是单向的,不可通过散列值得到原文;

不同的内容做散列计算,计算出的散列值为相同的概率几乎等于0

 

哈希主要用在:文件校验、数字签名、快速查找等。

例如,文件校验:


1.2   对称加密(Symmetric Cryptography)

需要对加密和解密使用相同。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。但是管理密钥不方便,要求共享密钥。如果N个人相互之间都要用对称加密进行通讯,则每个人维护的密钥是N-1个。

 

目前流行的对称加密算法有AESRC2RC4等等。

 

1.3   非对称加密(Asymmetric Cryptography)

与对称加密不同,需要两个publickey)和私有密钥(privatekey)。与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的,所以这种算法叫作非对称加密算法的优点是密钥管理很方便,缺点是速度慢。

通常来讲,公钥是每个人都能得到的,私钥是只有自己持有;

AB之间进行通信,安全的方式:

1.       AB发送消息,使用B的公钥进行加密,然后发给B,只有B用私钥才能解密成明文;

2.       BA发送消息,使用A的公钥进行加密,然后发给A,只有A用私钥才能解密成明文;

A获取B的公钥,必须是安全的方式;B获取A的公钥,也必须是安全的方式;

  


1.4  
数字签名(Digital Signature)

现实生活中,我们用签名来证明某个东西是与签名者相关的,是不可否认的,不可伪造的;

在虚拟世界里,我们有数字签名来帮助证明某个文档是你创建的,或者是你认可的。数字签名所用的技术是散列和非对称加密。

基于前面非对称加密的介绍,对签名的信息进行散列计算,用私钥对这个散列值进行加密;这样就得到一个签名。

当用户B比对散列值3与散列值2是否相同,如果相同则认为是A签名的,否则不是。

 

二、数字证书

如果现实生活中是用身份证来证明身份,那么在虚拟世界中,则会使用数字证书来证明身份。并不是每个人都需要用数字证书来证明身份,那么什么情况下,会使用数字证书来证明身份呢?普通用户一般情况下,不需要证明自己的身份,大部分网站不关心是谁访问了网站,现在的网站只关心流量啊~反过来,网站就需要证明自己的身份了。比如你想要提交信用卡信息给预定航班的网站,那么你如何确定你正在访问的网站就是你所想要访问的那个呢?现在很多的。比如你想访问的是“”,但其实你访问的是“”,所以在提交自己的信息之前你需要验证一下网站的身份,要求网站出示数字证书。一般正常的网站都会主动出示自己的数字证书。由于证书在网页浏览中最为常见,所以我下面举的例子都是基于浏览器的。

2.1   数字证书的构成

我们的身份证是由公安机关颁发的,并加有很多防伪技术,不能伪造(或者说很难)。同样的,数字证书也有专门的发证机关(Certificate Authority,简称CA,其实是一些商业公司啦)。比较常见的发证机关是。数字证书的发证机关会对自己发放的证书加上自己的数字签名,以保证证书不能被伪造。那数字证书到底包含了些什么呢?

1. 持有者姓名(Common Name

2. 发证机关(Issuer

3. 有效日期(Validity

4. 证书持有人的公钥(Subjects Public Key Info

5. 扩展信息 (Extension

6. 用发证机关对该证书的数字签名(Certificate Signature

基本信息就这些了(这些信息会在后面的章节有所解释),为了更清晰的说明问题,来几张截图(火狐->工具->选项->高级->证书->查看证书):

 

从图4我们可以看到,Certificate(证书)和Signature(签名)是分开的,但其实这个Signature也是证书的一部分。可以这么理解,数字证书包含证书主体和数字签名。证书中的签名是对证书主体的签名。

 

2.2   如何验证数字证书

当浏览器拿到一个数字证书:

1.       先看发证机关(5),主要是看以下三个:Organization Name(O)Organizational Unit Name(OU)Common Name(CN)

2.       找到相应的发证机关的证书,获得发证机关的公钥,用此公钥解密被加密的SHA1,这样就获得了此证书的SHA1值,我们称它为Hash1(图6、图7)。

3.       浏览器用SHA1算法对此证书重新计算一遍SHA1,获得Hash2

4.       然后比较Hash1Hash2是否相等。如果相等就证明这张证书是由发证机关颁发的,并且没有被篡改过。


5.       验证证书持有者

经过上述的证书验证后,可以认为证书没问题,是可靠的。但是还需要做一步,需要查看下发送者是否是证书的持有者(也就是通常意义上的身份证上的照片是否是本人,看完证件,还要比对下脸)。

通过证书里的公钥加密一段信息发送给证书持有者,如果对方能发送回来(加密的或者明文),说明对方是证书的持有者(也即,对方有对应的私钥)。

 

6.       核对名字

通常对于用证书来做web服务器的应用来说,证书中的名字(Common name)很重要,一般与根域名相等。如果不相等,则认为证书存在问题。

 

2.3   证书的级联

所有的证书都是基于另一张证书(可信任证书)进行认证的。换句话说,用一张已知合法的证书来证明另一张未知的证书。发证机关的证书默认是可信任的,这些证书称之为“根证书”。

由于申请证书的人数众多,发证机关忙不过来,这时候需要一些代理来帮忙签发证书,代理可能也需要代理来帮忙签发证书。这样就存在了层级关系:

这里的”USERTrust Legacy Secure Server CA”是由根证书”AddTrust External CA Root”签发的。验证这张证书的时候,需要从下往上递归验证。

是否是CA证书,可以看下扩展信息中的基本约束,如下图:

 

三、SSL的基本原理

现在回到我们最原始的问题,由于Internet的架构问题,信息在网络上传输是很容易被别人获取的,那如何建立一个安全的传输网络呢?前面我们讨论了很多保证信息安全的技术,而SSL就是建立在这些技术的基础上的一套协议,用来保证通信的安全。SSL全称是 Secure Sockets Layer,它是一种间于传输层(比如TCP/IP)和应用层(比如HTTP)的协议。具体的SSL协议很复杂,我这里只讲一个大概。

最简单的方法来保证通信安全是用非对称加密。我们前面讲过数字证书的认证,如果双方都认证了对方的数字证书,那么每次传输信息的时候都用对方的公钥加密,这样就只有对方能解密,从而保证了信息的安全。但是对于日常应用(比如网页浏览)有两个问题:
1.
非对称加密速度缓慢,消耗资源

如果客户端和服务器之间传输文件用非对称加密的话,速度一定慢的忍无可忍。

2.不可能要求每个用户都去申请数字证书

申请数字证书是一个相当麻烦的过程,要求每个上网的用户都拥有证书是不可能的事情。

 

SSL通过“握手协议”和“传输协议”来解决上述问题。握手协议是基于非对称加密的,而传输协议是基于对称加密的。根据不同的应用,SSL对证书的要求也是不一样的,可以是单方认证(比如HTTP, FTP),也可以是双方认证(比如网上银行)。通常情况下,服务器端的证书是一定要具备的,客户端的证书不是必须的。


10 握手过程

Phase1

client_hello:包括一个随机数和一系列的可支持的加密组件(以性能高到低的方式排列)

server_hello: 包含一个随机数和一个从客户端发来的组件中选择的加密组件

加密组件:每个加密组件包含一个密钥交换算法、加密算法、MAC算法,例如:TLS_ RSA_WITH_AES_256_CBC_SHA256

密钥交换算法:DH算法、RSA(公钥/私钥)

Phase2(例如使用DH):


    server_key_exchange中包括 paS1

    client_key_exchange中包括C1

    最后生成K是共用密钥。

Phase3

客户端的认证:服务器或许会请求客户端发送证书,如果客户端有,则发送证书,如果没有则发送一个no_certificate的警告信息。

如果发送了证书才会有certificate_verify,包括了客户端的认证,包括了一个之前握手消息的散列值。

 

Phase4

         客户端发送Change Cipher Spec消息,通知SSL服务器后续报文将采用协商好的密钥和加密套件进行加密和MAC计算。

SSL客户端计算已交互的握手消息(除Change Cipher Spec消息外所有已交互的消息)的Hash值,利用协商好的密钥和加密套件处理Hash值(计算并添加MAC值、加密等),并通过Finished消息发送给SSL服务器。

SSL服务器利用同样的方法计算已交互的握手消息的Hash值,并与Finished消息的解密结果比较,如果二者相同,且MAC值验证成功,则证明密钥和加密套件协商成功。

同样地,SSL服务器发送Change Cipher Spec消息,通知SSL客户端后续报文将采用协商好的密钥和加密套件进行加密和MAC计算。

SSL服务器计算已交互的握手消息的Hash值,利用协商好的密钥和加密套件处理Hash值(计算并添加MAC值、加密等),并通过Finished消息发送给SSL客户端。SSL客户端利用同样的方法计算已交互的握手消息的Hash值,并与Finished消息的解密结果比较,如果二者相同,且MAC值验证成功,则证明密钥和加密套件协商成功。

 

四、openssl开源库的使用(客户端)

openssl开源库的网址,包括源码、文档等内容。这一节,我们主要使用的是ssl/tls库。

1.       初始化

作为一个客户端,首先要初始化一个Context 对象(一个SSL_CTX),这个对象是用来创建一个新的连接对象。Context初始化,包括四个主要的部分,如下:

SSL_METHOD *meth;

SSL_CTX *ctx;

 

SSL_library_init();               //初始化整个库,加载了一些openssl的算法等内容,只需要在程序启动的时候,初始化一次就可以了。

 

meth=SSLv23_method();            //SSL v2 或者V3兼容的方式
ctx=SSL_CTX_new(meth);          //
创建一个Context对象

 

/* Load our keys and certificates*/

if(!(SSL_CTX_use_certificate_chain_file(ctx, keyfile)))        //加载证书

berr_exit("Can’t read certificate file");

 

         SSL_CTX_set_default_passwd_cb(ctx, password_cb);//如果密码是加密过的,password_cb是个回调函数,可以还原密码

if(!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM)))//加载私有密钥

berr_exit("Can’t read key file");

        

         /* Load the CAs we trust*/

if(!(SSL_CTX_load_verify_locations(ctx,CA_LIST,0)))        //加载可信任的CA证书

berr_exit("Ca’t read CA list");

 

如果一个client要去执行认证,则需要加载自己的public/private key 对儿和相关证书。

SSL_CTX_use_certificate_chain_file用来加载CA证书;

SSL_CTX_use_PrivateKey_file用来加载私钥,通常密码是加密的,可以通过SSL_CTX_set_default_passwd_cb回调函数来获取密码。

加载根CA列表,如果你要认证对端,则需要知道CA,加载函数:SSL_CTX_load_verify_locations

 

 

2.       握手处理

SSL连接的第一步就是要执行SSL握手过程。握手认证server(可选的认证client),并且建立关键材料用来保护后续的数据。SSL_connect()函数用来执行SSL的握手协议。如果使用阻塞协议,SSL_connect会一直阻塞,直到握手完成,或者返回一个错误。SSL_connect返回1表示成功,0或者负数表示一个错误。代码如下:

sock=tcp_connect(host,port);  //假设tcp_connect 是用来建立一个tcp连接的函数

ssl = SSL_new(ctx);  //初始化TLS/SSL

sbio = BIO_new_socket(sock, BIO_NOCLOSE); //创建一个BIO对象,关联到TCP socket

SSL_set_bio(ssl,sbio,sbio);                   //ssl关联到BIO对象上

if(SSL_connect(ssl)<=0)                        //这里执行握手

berr_exit("SSL connect error");

 

3.       检查server的证书

X509 *peer;

char peer_CN[256];

 

if(SSL_get_verify_result(ssl)!=X509_V_OK)                //对端发来的证书验证,如果不是X509

berr_exit("Certificate doesn’t verify");

         /*Check the common name*/

peer=SSL_get_peer_certificate(ssl);                           //提取server的证书

X509_NAME_get_text_by_NID(X509_get_subject_name(peer),NID_commonName, peer_CN, 256);                  //获取Common Name

if(strcasecmp(peer_CN,host))

err_exit ("Common name doesn’t match host name");

4.       读写数据

SSL_write()函数用来发送数据到对端,像write()一样使用。只是传的参数是SSL对象而不是文件描述符。在阻塞的模式,SSL_write()不会返回,直到数据都发送完,或者一个错误返回,然而write可能只写部分数据。注意: SSL_MODE_ENABLE_PARTIAL_WRITE标记使能部分写的功能。代码如下:

r=SSL_write(ssl,request,request_len);  //写数据,数据在request中,长度是request_len

switch(SSL_get_error(ssl,r)){

case SSL_ERROR_NONE:

if(request_len!=r)

err_exit("Incomplete write!");

break;

default:

berr_exit("SSL write problem");

                   break

}

SSL_read() 用来读取数据,就像read一样,我们选择合适的参数传给SSL_read()SSL_read返回读到的数据长度。如果不存在数据可读,则SSL _read是阻塞的,会一直等待。

r=SSL_read(ssl,buf,BUFSIZZ);    //读取数据

switch(SSL_get_error(ssl,r)){

case SSL_ERROR_NONE:

len=r;

break;

case SSL_ERROR_ZERO_RETURN:

goto shutdown;

case SSL_ERROR_SYSCALL:

fprintf(stderr,"SSL Error: Premature close0);

goto done;

default:

berr_exit("SSL read problem");

}

//这里对数据进行处理

 

例子中使用函数SSL_get_error()来替代errnoSSL_get_error()检查返回值,并且指出是否错误发生了,并且错误是什么。

5.       关闭Closure/shutdown/cleanup

 

TCP使用FIN标记来说明发送者已经发完所有的数据。SSLv2简单的允许任何一方使用一个TCP FIN来终止SSL连接。这会导致“截断攻击”:攻击者通过简单地伪造TCP FIN来使看起来消息是短的。除非受害者有某种方式知道消息的长度。

为了避免这种情况,SSLv3引进了一个close_notify警告。close_notify是一个SSL消息(因此是安全的),但是不是数据流本身的一部分,不背应用所看到。当发送完close_notify后,不会再有数据会被发送。

因此,SSL_read返回0,说明socket被关闭了,已经收到了close_notify消息。如果client先收到一个FIN,则认为是一个“过早关闭”。

如果我们读到SSL_read返回0,没有任何错,则我们需要发送close_notifyserver端(通过SSL_shutdown()来发送)。

SSL_shutdown()返回1表示完全关闭,0表示不完全关闭,-1是出现一个错误。当收到服务器发来的close_notify,唯一可能出错的是发送我们的close_notify,否则SSL_shutdown总是会成功。

代码片段(client)

while(1)

{

r=SSL_read(ssl,buf,BUFSIZZ);

switch(SSL_get_error(ssl,r))

{

                  case SSL_ERROR_NONE:          //未出错

                            len=r;

                           break;

                  case SSL_ERROR_ZERO_RETURN:           //未出错,我们已经收到close_notify

                           goto shutdown;                                    //这里去发送我们的close_notify

                  case SSL_ERROR_SYSCALL:

                           fprintf(stderr, "SSL Error: Premature close\n");  //收到一个“过早关闭”的信息

                            goto done;

                  default:

                            berr_exit("SSL read problem");/*错误处理,这里是退出程序*/

}

/*数据处理*/

         }

shutdown:

         r=SSL_shutdown(ssl);                 //发送给server端一个close_nofity

         switch(r)

         {

                   case 1:

                            break; /* Success */

                   case 0:

                   case -1:

                   default:

                            berr_exit("Shutdown failed");

         }

done:

         SSL_free(ssl);             /*释放资源*/

         /*释放相关变量*/

五、openssl证书常用命令

x509证书一般会用到三类文,keycsrcrt

Key 是私用密钥openssl格,通常是rsa算法。

Csr 是证书请求文件,用于申请证书。在制作csr文件的时,必须使用自己的私钥来签署申,还可以设定一个密钥。

crtCA认证后的证书文,(windows下面的,其实是crt),签署人用自己的key给你签署的凭证。

pfx会同时包含公钥和私钥。

 

1.key的生成

openssl  genrsa  -des3  -out  server.key  2048

这样是生成rsa私钥,des3算法,openssl格式,2048位强度。server.key是密钥文件名。为了生成这样的密钥,需要一个至少四位的密码。

可以通过以下方法生成没有密码的key:

openssl  rsa  -in  server.key  -out  server.key

 server.key就是没有密码的版本了。这种没密码的私钥这种用法挺多,比如webserverlighttpd)这种方式,不可能每次启动server的时候,都要求用户输入密码,太过麻烦。

 

2. 生成CAcrt

openssl  req  -new  -x509  -key  server.key  -out  ca.crt  -days  3650

生成的ca.crt文件是用来签署下面的server.csr文件。需要输入一堆信息,有一个Common name 通常是CA机构的域名(这里,我们可以自己取一个名字)。

 

3. csr的生成方法

openssl  req  -new  -key  server.key  -out  server.csr

需要依次输入国家,地区,组织,email

最重要的是?有一个common name,可以写你的名字或者域名。

如果为了https申请,这个必须和域名吻合,否则会引发浏览器警报。

生成的csr文件交给CA签名后形成服务端自己的证书。

注意Common name  CACommon name 要不一样,否则会校验失败。

 

4. crt生成方法

CSR文件必须有CA的签名才可形成证书,可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢。

openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key -CAcreateserial -out server.crt

输入key的密钥后,完成证书生成。

-CA选项指明用于被签名的csr证书,

-CAkey选项指明用于签名的密钥,

-CAserial指明序列号文件

-CAcreateserial指明文件不存在时自动生成。

最后生成了私用密钥:server.key和自己认证的SSL证书:server.crt

证书合并:

cat server.key server.crt > server.pem

 

6.   常用命令

校验证书,是否正确: openssl verify -CAfile ca.crt server.crt

此命令的意思是 用CA证书ca.crt 来验证server的公钥,例如:


1.       X509转换pfx

openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt

 

2.        

查看证书

openssl  x509  -in  ca.crt  -text  -noout               

openssl  x509  -in  server.crt  -text  –noout

注意看证书的有效期时间:

 

 

openssl命令开一个server

openssl  s_server  -accept  4433  -cert  server.crt -key server.key -CAfile ca.crt -ssl3

上述的例子是:

-accept 4433  表示在本地4433 端口上开启ssl服务器

-cert server.crt 表示是服务器的公钥

-key server.key 表示是服务器的私钥

-CAfile ca.crt 表示是ca证书,服务器和客户端都有的

-ssl3 表示用sslv3

 

openssl命令做客户端,连接服务器测试:

openssl  s_client -connect localhost:443 -verify 1 -CAfile ca.crt

-connect localhost:4433 表示连接到本地端口4433

-CAfile ca.crt 表示CA证书是ca.crt

-ssl3 表示使用sslv3

-verify 1 表示要认证对端

CA的生成:

openssl  genrsa  -des3  -out  ca.key  2048

openssl  rsa  -in  ca.key  -out  ca.key

openssl  req  -new  -x509  -key  ca.key  -out  ca.crt  -days  3650

 

 

 

server端的生成:

openssl  genrsa  -des3  -out  server.key  2048

openssl  rsa  -in  server.key  -out  server.key

openssl  req  -new  -key  server.key  -out  server.csr

openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt

 

client端的生成:

openssl  genrsa  -des3  -out  client.key  2048

openssl  rsa  -in  client.key  -out  client.key

openssl  req  -new  -key  client.key  -out  client.csr

openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt


 

验证:

openssl verify -CAfile ca.crt server.crt

openssl verify -CAfile ca.crt client.crt

 


阅读(3925) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~