« 上一篇下一篇 »

普通程序员的角度来看密码学

说起密码,说起密码学,第一时间想到的就是各种非常复杂的数学模型,种类繁多的各种标准,似懂非懂的各种专业术语.我相信有相当一部分程序员有这样的感受,我就是典型的代表.我不是数学特别好的那种,读书的时候也没专门学过密码学,所以当后来做与之相关的工作的时候(比如通信安全)总是不得要领,于是决定好好啃啃这块硬骨头,好在努力没白费,在学习和实践中慢慢的对密码学的应用有了些了解,决定把一些心得写出来,给后来的一些新手一点点帮助, ,不敢谈什么数学原理,只是站在一个普通程序员的应用角度来谈谈心得.
先看看密码学对于计算机实际应用到底有什么帮助,至少包括以下4个方面:

一. 保密性
   保密性是指隐藏信息所要表达的真实含义和目的.比如A有个很重要的文件,想通过网络传给B,但是他又不想让B以外的任何其他人知道,那么他首先和B约定好一个密钥,然后在通过网络传输前先把文件内容通过某种算法用约定好的密钥加密,将明文变成密文,然后再传给B,B接收到文件后再用约定好的密钥解密,将密文变成明文.在网络传输过程中就算被其他人窃取,窃取到的也是加密后的文件.从而达到保密的效果.有一种最常见的加密算法称之为AES算法.

二. 完整性
   完整性是指(假定数据内容不会被攻击者非法修改的前提下)确保数据和信息的正确性.比如A有个文件,在传给B之前先通过散列算法算出一个散列值,然后把文件和散列值一起传给B,B收到文件后通过同样的散列算法算出这个文件的散列值,然后和A发过来的散列值做比较,如果一样就证明这个文件是完整的,没被修改. 讲到这里,我想强调的是也许大家注意到了前面提到的一个前提条件(括号中的内容)会很奇怪,呵呵,关于这点后面会详细解释.有几种比较常见的散列算法,比如MD5算法,SHA-1算法.

三. 认证
   举个例子.比如A有个重要信息,想通过网络传给B,假设在传输的过程中可能会被黑客攻击,修改内容,甚至将内容完全替换,那么B要如何才能确认这个重要信息是不是正确?是不是A发过来的呢? 我们采取这样一种方法: 首先A和B要商量好一个密钥, 然后A在发送信息前首先将要发送的重要信息和密钥通过某种算法计算出一串值,这串值称为消息标记,然后把重要信息和计算出来的消息标记一起发给B,然后B将收到的重要信息和密钥通过同样的算法算出消息标记,然后和收到的消息标记做比较,如果相同就意味着数据正确并且完整,同时也能够确认这个信息确实是A发过来的,从而达到认证的目的.如果不相同就意味着可能被黑客给攻击了. 刚才使用的那个算法,我们通常称为 消息认证码(Message Authentication Code,MAC).看到这里,大家心里是不是会有一个疑问,这里提到的方法也可以验证数据的完整性,那么这里与上面所讲的 第二点:完整性 又有什么联系和区别呢? 呵呵 等一下会详细解释这个问题.

四. 不可否认
   什么叫不可否认?举个现实中的例子,比如你和人家签了份合同,当你在合同上签了字后那么这个合同就产生了法律效应,以后你做事就不能违背合同上的规定,你也不能否认你没有签过这个合同,因为合同上有你的签名. 那么在计算机的世界要如何达到这个目的呢?我们可以采取数字签名技术来达到这个目标,那么到底什么又是数字签名呢?文章下面会讲到 .

    前面说了密码学在计算机应用上的四个方面,下面就大概介绍一下密码学中的一些常用算法及其使用.
首先要谈到的是 对称密钥算法  所谓对称的含义是指一个加密算法的加密密钥和解密密钥相同,或者虽然不相同,但是可由其中的任意一个很容易的推导出另一个,即密钥是双方共享的. 这种类型的算法的典型代表是AES(Advanced Encryption Standard)高级加密标准算法,也叫Rijndael算法.它是一种分组密码(block cipher)算法.它是美国国家标准与技术协会(NIST)所认可的一个标准.AES算法的出现是用来替代一个古老的算法:DES(Data Encryption Standar)数据加密标准算法的.DES算法已经被多次证明是不安全的!

按此在新窗口打开图片

   不过这种类型的算法都有个明显的不足, 就是它的密钥是共享的, 只要有一个人泄露了密码,那么整个体系就都不安全了.

    接下来我们要谈到的是散列函数,散列函数也称为(Pseudo Random Function, PRF)伪随机函数.散列函数是指的这样一种算法: 它可以把一个任意大小的输入数据通过一种压缩(compression)的处理过程转换为一个固定大小的不会重复(极少重复)的输出.而且这种压缩处理是不可逆的,意味着你没有办法将输出还原成输入.这种算法的输出有个专业称呼,称之为摘要(digest) 如图:

按此在新窗口打开图片

   比较常见的散列算法有MD5,SHA-1等.先看看MD5,我想这个大家应该太熟悉不过了,MD5的全称是(message-digest algorithm 5)信息-摘要算法,经MD2、MD3和MD4发展而来,MD5算法能够将任意长度的输入数据映射成一个256位(32个字节)长的输出(摘要). 不过MD5算法现在已经被证明是不安全的了. 再看看SHA-1算法,它是SHS(Secure Hash Standard)安全散列标准家族中的一员,而SHS又隶属于美国国家标准与技术协会(NIST).SHA-1能够将任意长度的输入数据映射成一个160位(20个字节)长的输出(摘要). 下面我们来看看散列算法如何来保证数据的完整性,举个实际的例子:BT下载软件大家想必都用过,它首先需要一个种子文件,然后打开这个种子文件才能下载到实际的数据,其实这个种子文件中就包含了要下载的数据的摘要信息,这个摘要是通过SHA-1算法算出来的,当你打开种子文件的时候会通过SHA-1算法计算出已经下载过的数据的数据摘要,和种子文件中纪录的摘要做比较,如果全部相同就证明数据已经全部下完.如果不同就意味着数据还没下完或者是被非法修改过. 
    不过通过散列函数保证数据完整性有时候确会碰上麻烦,不信我们看个例子:A想把一个重要信息通过网络传给B,A在传送之前首先通过散列算法比如SHA-1算法算出这个信息的摘要,然后把摘要附加到信息的尾部一起传送给B,假设这个时候黑客C在传送途中把数据包给拦截下来并且把信息内容进行修改,然后重新用SHA-1算法算出修改后的信息摘要替换原来的摘要,再转发给B,这个时候B收到了信息,B用SHA-1校验信息后发现一切OK,他以为他收到了正确的信息数据,殊不知这个重要信息早已经在网络传输的途中被C给改掉了. 如图:

按此在新窗口打开图片

   可见散列算法虽然能保证数据完整性,但是却有个前提条件,正如文章前面说讲的一样,(假定数据内容不会被攻击者非法修改的前提下), 大家是不是觉得很矛盾, 但事实确实是这样 . 通过前面这个例子可以看出: 散列算法虽然能够对数据完整性提供保证,但是却无法对数据的来源进行认证(B没有办法确认文件就是A给他发送的那个). 那么如果想达到认证的目的要怎么办了,文章前面谈到过MAC算法,那么什么是MAC算法,我们接着看.

    消息认证码(Message Authentication Code,MAC)算法存在的主要目的就是为了认证, 美国国家标准与技术协会(NIST)制定二种MAC标准.
第一种是分组消息认证码(Cipher Message Authentication Code, CMAC)算法. 
第二种散列消息认证码(Hash Message Authentication Code, HMAC)算法.
    CMAC算法是基于分组密码算法的,很多情况下CMAC算法内部使用了AES算法. 而HMAC算法是基于散列算法的,很多情况下HMAC算法内部使用了SHA-1算法. 不管是哪种MAC算法 它们的外部表现形式都是一样的: 它们都接受一个任意大小的输入和一个密钥输入,然后通过计算得出一个固定大小的输出,我们一般把这个输出称为MAC标记.如图:

按此在新窗口打开图片

   我们来看例子: A想把一个重要信息通过网络传给B,首先A和B要先约定好一个密钥,A在传送之前首先利用密钥通过MAC算法算出这个信息的MAC标记,然后把MAC标记附加到信息的尾部一起传送给B,假设这个时候黑客C在传送途中把数据包给拦截下来并且也把信息内容进行了修改,但是他因为不知道密钥,所以他无法重新计算MAC标记,所以攻击失败. 当B收到信息以后,他用约定好的密钥通过MAC算法算出收到的信息的MAC标记和收到的MAC标记做比较, 如果一样就意味着数据是A发来的并且完好无损. 把这个例子和上面讲散列算法的那个例子做比较, 会发现MAC算法比散列算法多了一个输入参数, 那就是密钥 , 正是因为多出的这个密钥参数才保证了MAC算法能够达到认证的目的, 当然同时也能够实现完整性验证.

    前面讲了MAC算法,你会发现它只是提供了完整性和认证的功能, 却没有提供对数据的内容进行加密的功能. 所以实际的应用中很可能要和某种加密算法配合使用,比如我传输数据之前首先用AES算法对数据加密,然后用MAC算法实现数据的认证.  那有什么办法可以把加密和认证的功能合到一起,至少我们写程序的时候可以一步到位 . 答案是有的, 接下来就介绍.

    加密和认证模式(encrypt and authenticate modes) 指的是把加密和认证的任务封装到一个单独的处理过程中,下面是一个非常简化的过程图:

按此在新窗口打开图片

其中最常见的二个标准: 
一个是美国电气及电子工程师学会 IEEE(Institute of Electrical and Electronics Engineers)的GCM(Galois Counter Mode), 它常用于各种无线标准,比如802.16. 
另一个是 NIST的CCM(Counter mode with Cipher-block chaining Message authentication code).

    其实不管是加密也好认证也罢,有个问题都无法直接解决,这个问题就是重放保护问题.那什么是重放保护呢? 举个例子,看看如果没有重放保护会发生什么大问题. 比如我使用网上银行转帐,在这个过程中, 网上银行客户端程序向银行服务器发送了一个数据包,这个数据包里面包含了转帐的请求和金额,当服务器收到这个数据包就会根据请求进行转帐操作, 假如在通讯过程中这个数据包被黑客给拦截了,虽然黑客没办法把数据包解密或是修改,但是他可以把这个数据包保存下来,然后把数据包原封不动的不停的给银行服务器发,那最终我账户上的资金就会被全部转走 , 那可就惨啦! 当然这仅仅只是个假设,但是它反应出重放保护是多么的重要! 为了做到重放保护我们必须做点额外的工作,最常用的方法就是在通讯数据包中加入时间戳或者是计数器. 如果包含了时间戳,接收到数据包后可以对比时间差,如果时间间隔太久就丢弃数据包. 如果包含了计数器,接收到数据包后可以查看包的编号,比如收到的第一个包编号是2, 那么收到的第二个包的编号至少比2要大.不然就丢弃数据包.这样就可以比较好的解决重放保护问题.

    上面介绍的不管是AES算法还是MAC算法它们都是对称算法, 通讯双方的密钥是共享. 所以一但任何一方泄露了密钥, 就再没有安全可言.  而非对称密钥算法就能很好的解决这个问题.

    非对称密钥(asymmetric key)算法,也叫做公钥算法. 它的出现解决了对称加密算法很难解决的两个问题: 密钥分发(key distribution) 和 不可否认(nonrepudiation). 而RSA(Rivest Shamir Adleman)算法是一种最常见的公钥算法. 公钥算法的加密和解密所使用的密钥是不同的,其中公钥是可以公开的,而私钥必须保密.用公钥加的密必须用对应的私钥才能解密,用私钥加的密必须用对应的公钥才能解密. 而且无法由公钥推导出私钥.
    先看看公钥算法如何解决密钥分发的问题.举个例子: A要通过网络传一个文件给B,那A首先问B要一个公钥,B自己持有相应的私钥,然后A用这个公钥加密另一个用于对称加密算法的密钥(用K表示),然后把加了密的密钥发给B,B收到后用自己的私钥解密,得到用于对称加密算法的密钥(K),这个时候A和B就都知道了用于对称加密算法的这个密钥(K),然后A把文件用对称加密算法(比如AES)加密 传给B, B收到后用密钥(K)将收到的文件解密. 在整个过程中,就算黑客拦截了传输密钥(K)的数据包,但是他没有私钥无法解密,所以他不知道密钥(K)是什么,所以他也无法对传输的文件进行解密. 

按此在新窗口打开图片

    大家会发现上面的例子中是混合使用了对称密钥算法非对称密钥算法,这种混合模式在实际的应用当中经常被采用. 那为什么不直接采用非对称密钥算法完成所有的工作呢? 那是因为一般而言,非对称密钥算法加密的速度都比对称密钥算法的多,所以采用这种混合模式可以充分发挥对称密钥算法的速度优势.
    前面在说不可否认那节时说到了数字签名,那什么是数字签名呢? 所谓数字签名就是附加在数据单元上的一些验证数据.接收者利用这些数据确认数据单元的来源和数据单元的正确性并防止别人伪造. 数字签名一般是利用散列算法和公钥算法来完成的. 如图:

按此在新窗口打开图片

    把将要签名的数据用散列函数算出散列值(摘要),再把散列值(摘要)传给公钥算法,最后算出的结果就是这个数据的数字签名.  举个例子: A有一个文件要传给B,A用SHA-1算法算出文件的摘要,再把摘要用自己的私钥通过RSA算法加密,得出数字签名后连同文件本身一起发给B, B收到后,首先用A的公钥通过RSA算法将数字签名解密,解出摘要.然后他把收到的文件也通过SHA-1算法算出摘要, 然后把解密出来的摘要和自己算出的摘要做比较,如果相同就意味着数字签名验证通过. 这样就可以确认文件是完整的而且确实是A发过来的,A也无法抵赖说文件不是他的,因为B是用A的公钥解密的. 

    文章上面讲到了几种典型的算法和几种典型的应用,在实际应用当中可以根据自己项目的需要自由搭配各种加密算法,组合出最适合自己项目的一种安全解决方案. 还有就是在实际应用中, PKI(Public Key Infrastructure)公钥基础设施,是一种遵循既定标准的密钥管理平台.应用范围非常的广,值得我们大家一起学习. http://www.infosecurity.org.cn 这个网站可以看看. 像<<现代密码学>>, <<程序员密码学>>,<<密码学导论>>等书也可以看看. 下面是开源社区的几个加密算法库,比较著名. 可以在自己的项目中使用. 研究这些代码也是学习的好途径.

OpenSSL     http://www.openssl.org 
LIBTOM       http://www.libtom.org 
Crypto++     http://www.cryptopp.com