图解程序员需要懂的密码技术

Internet最初出现时,被预期成一个友好、和谐的信息交换网络,因此HTTP协议中最初的身份验证是使用Base Auth那类几乎是透明的鉴权方式。然而工程师们显然还是太单纯了,有利益之处,就有人性的最黑暗面,非法获取、篡改信息从来没有停止过。
不过好消息是人类的智慧是无穷的,只要有问题,就会衍生出各种解决方案。“密码技术”,并不仅仅应用于加密信息让黑客无法获取原始信息,还用于身份鉴别、防否认、防篡改。密码技术就是用人类的无限智慧去对抗人性的无限黑暗。

一、加密算法与密钥

最早的加密方式之一是古罗马的凯撒密码,是通过将密文的字母按照字母表向后平移N位,来让敌军即使截获信件也不知道在表达什么意思。

向后平移3位

凯撒密码的加密方式描述为:将原文按照字母表顺序,向后平移N位。其中N是一个可变量,敌军在不知道N的值的情况下,是无法破译密文的,即使他们知道凯撒的加密方式就是将原文平移。将原文按字母表顺序平移加密算法,只有算法被敌军知道他们也是无法解密的,N密钥,需要将N带入加密算法才能正确解密。因此,保密的核心,不在算法,而在密钥。相反,算法应该公之于众,鼓励大家破解,只有无数精英都难以破解的算法,才是相对安全的算法。如果算法仅仅自己保密,想依靠对加密算法的保密,来确保安全性的行为称为“隐蔽式安全”,是愚蠢且不可靠的,因为未经检验的算法,健壮性不够,在专业破译人员手中很容易破解,但是自己却觉得算法从未公开过,一定非常安全。事实上,这近似于掩耳盗铃。黑客可以通过各种方式获取、破解你的算法,其中最弱的一环,就是人。
因为凯撒密码的加密算法过于简单,因此即使不知道密钥,只需要试遍25次,就一定能暴力破解出密钥,解出正确密文。不过现代的加密算法已经越来越完善和复杂,建立在严密的数学基础之上,例如目前主流的非对称加密RSA算法,只要保管好密钥,可以考虑为现实维度的绝对安全。

二、对称加密与密钥配送问题

对称加密是指加密与解密使用同一个密钥,之前说的凯撒密码,就是典型的对称加密,用于加密的变量N,也是用于解密的变量。

对称加密:加解密使用同一把密钥

现在流行的对称加密算法有DES、三重DES、AES等,具体算法就不在这里说了,但是都非常复杂,已经不是凯撒密码能比的了,而且安全性也非常高。但是问题来了,A与B要通讯,就必须两方都知道密钥,否则无法完成加解密,密钥一定不能让第三者知道,否则一切的加密都是没有意义的,该怎么保证密钥的保密性呢?
当面交接密钥是比较稳妥的方式,但是在互联网的加密中就不可行了,如果为了交换密钥,需要双方面对面,那么互联网的意义就不存在了,因此需要寻找一种远程安全交换密钥的方式。但是,只要信息在互联网中传输,就存在被窃听的风险,所以,对称加密是无法解决密钥配送问题的。
转机出现在1976年,斯坦福大学的两个教授Diffie和Hellman研究出了解决密钥交换的数学模型:

Diffie-Hellman密钥交换算法

首先,Alice需要生成两个超大的素数g、p,并且g要是p的primitive root module p,这里并不用理解是什么意思,只需要知道g、p是满足某一条件的超大素数。Alice生成一个随机整数a,并做g的a次方对p取模的运算,计算出A,最后将g、p、A通过网络共享给Bob,Bob生成一个随机整数b,并做g的b次方对p取模的运算求出B,然后将B回传给Alice,Alice做B的a次方对p取模的操作求出密钥,同时Bob通过A的b次方对p取模,也能求得同样的值,也就是密钥。整个过程,网络中只交换了大素数g、p以及求模值A、B,两个随机数a、b一直处于保密状态,虽然理论上可以通过g/p/A/B这四个值求出a、b,最终求出密钥,但是这可能需要超级长的时间,g、p越大,所需时间越长,可能是上百年,这就是数学中的离散对数难题。如果一个密钥需要上百年才能破解,在现实维度上,可以考虑为绝对安全。

三、非对称加密

在Diffie-Hellman算法提出的一年后,也就是1977年,MIT的三个教授提出了非对称加密算法,用他们名字的首字母命名——RSA。非对称加密又称公钥加密,这是密码学史上最重大的里程碑之一,奠定了现代密码学的基础,如今互联网中的加解密几乎都是使用公钥加密。
非对称加密,顾名思义,加密和解密不是使用同一个密钥,而是有两把密钥,一把负责加密,一把负责解密,我们计算出A、B两把密钥后,将一把作为私钥,自己保存,用于解密,另一把作为公钥,可以随意分配给任何人,用于加密。
甲要向乙发送消息,乙只需把自己的公钥传输给甲,然后发送方甲将明文用接收方乙的公钥加密后,传输给乙,乙收到密文,用自己的私钥解密。

非对称加密

有了公钥加密后,就不存在密钥配送问题了,因为用于解密的私钥,我们不需要在网络上传输,只需要自己保存就好(乙保留自己的私钥),公钥随意在网络上传输,因为公钥只能用于加密,不能用于解密:
非对称加解密原理

如图,RSA中明文对自己做E次方的乘法,然后再对N求模数,就是密文,解密需要将密文做D次方的乘法,再对N求模数,就是明文,其中(E,N)组成了加密密钥,(D,N)组成了解密密钥。至于E、D、N三个值是如何求出,原理也是类似于Diffie-Hellman算法,即利用理论可解,但现实难解的问题去计算:首先准备两个超大的质数p、q,大到起码是上百位的十进制数字,然后利用p、q去计算N、E、D,其中N是p、q的乘积,只知道E、N不知道p、q,是无法求出D的,既然知道乘积N,又怎么不能分解出p、q呢?理论上是可以的,但是目前还没有找到一个高效的质因数分解的方法,因此计算非常困难,越长的RSA密钥,越难分解,几十上百年分解出密钥,就不存在太大意义了。
从上图中的公式中,可以看出,如果只有公钥(E,N),没有私钥中的D是无法将密文解成明文的,所以用于加密的公钥可以随意在网络中传播而不必担心安全问题,而私钥是必须保存好的,一旦私钥泄露,所有基于这对秘钥的通讯都是不安全的通讯。
我们可以利用OpenSSL库轻松生成RSA密钥:

  1. 先来生成一个1024bit的RSA私钥:
    $ openssl genrsa -out private.pem 1024
    $ cat private.pem
    除了开头和结尾有几个单词,中间是一串不可读的base64编码密钥。使用 -text 参数可以打印出人大概能读的私钥文件:
    $ openssl rsa -text -in private.pem
    私钥文件

    可见私钥文件有RSA密钥对的所有元素。
    目前大多数私钥文件都不仅仅是包含私钥,还包含了计算RSA密钥对所需要的所有东西,如下:
    1. 模数 N
    2. 私有指数 D
    3. 公开指数 E (基本上都是3或者65537)
    4. 因子 p 、q
    5. p - 1 和 q - 1的最小公倍数 L
    6. q - 1 mod p 的值(系数)
    只要有以上的数据,可以轻松计算整个密钥对,也就是说,是可以通过私钥文件计算出公钥的。请注意是私钥文件而不是私钥,因为私钥仅仅由(D,N)组成,是无法推算出p、q的,而私钥文件中常常携带了完整信息,所以,私钥无法推算公钥,但是(大多数)私钥文件是可以的。
  2. 利用私钥文件,生成公钥:
    $ openssl rsa -in private.pem -out public.pem -outform PEM -pubout
  3. 下面我们可以用这对密钥来加解密了,先写一点有意义的内容:
    $ echo 'too many secrets' > file.txt
  4. 我们用公钥加密它:
    $ openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl
    加密后的文件
  5. 用私钥解密它:
    $ openssl rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt
    解密文件

由于RSA是利用分解质因数的难题来保证密钥不被破解的,那么越大的p、q,也就是越长的密钥,越难被破解,目前在投入非常大量的计算资源的情况下,已经能够分解长度为1024比特的整数了,对此,NIST SP800-57给出了如下方针:

  • 1024比特的RSA不应被用于新的用途
  • 2048比特的RSA可在2030年之前被用于新的用途
  • 4096比特的RSA在2031年之后仍可被用于新的用途

RSA巧妙地避开了密钥配送的问题,而缺点是其加密所需消耗的时间是对称加密的数百倍,如果用它来加密我们日常所传输的大量数据,效率会非常之低。因此在一般的通讯中,我们往往是将公钥加密与对称加密混合使用,使用公钥加密配送对称密钥,其后的通讯中使用对称密钥加解密信息。
尽管公钥加密解决了密钥配送问题,但针对公钥密码可以进行中间人攻击,要防御这种攻击,就要解决认证为题,也就是:“和我通讯的这个人,到底是不是我想要通讯的那个人”,后面会讲到解决认证问题的技术。

四、单向散列(Hash)

虽然利用强大的加密算法可以防止真实信息被获取,但是获取信息绝对不是唯一的攻击目的,有的攻击者并不需要获取你真实的信息,他们的目的是——篡改
想象你存在计算机中的合同,在某个深夜,被黑客篡改了条款,你却无从得知,因此,衍生出了一种需求:需要一种方式来判断信息是否被篡改。

篡改

单向散列函数解决了这个问题,它具有指纹功能,对于给定的任意长度的比特,都计算出等长的散列值。
单向散列函数可以理解为一种算法规范、协议,它并不是一种单纯的算法,满足单向散列函数规范的算法,都称为散列算法,比如MD4、MD5、SHA1等。单向散列函数都满足一种规范:输入任意长度的比特串,都为其生成固定长度的散列值,并且其值不可逆运算出原串
单向散列函数一般是将原串分组、填充后,进行取模、加法等多轮运算,最后挤出定长Hash值。
单向散列有以下几个非常重要的特点:

  • 正向快速: 即给定任意原串,都能利用现实有限的资源在很快的时间计算出Hash值。
  • 无法逆向: 因为Hash值是原串的所有比特糅合运算的结果,我们在只知道Hash值的情况下是无法推测出原串的,哪怕有足够强大的运算能力去遍历。例如我随机选定两个整数,只告诉你A、B两数进行加法运算结果为20,你也绝对不可能推测出A、B具体是哪个值,Hash也是同理。
  • 输入敏感: 每当修改原串1比特的数据,输出的Hash值都会产生变化。
  • 抗碰撞性: 抗碰撞性分为弱抗碰撞性与强抗碰撞性,弱抗碰撞性是指,给定一个原串,无法找出Hash值相同的其他原串,强抗碰撞性是指,不给任何限制,不能找出Hash值相同的两个不同原串。因此。

基于这些特点,单向散列非常适合作为验证数据原始性的工具,当我们在网上下载软件时,大多数网站都会在后面附加MD5、SHA1值,当你下载软件后,可以通过验证Hash值去验证软件是否有被修改,比如下载损坏、植入病毒。


软件下载网站提供散列值校验

目前,MD4、MD5、SHA1的抗碰撞性均在2004、2005先后被王小云教授击破,对于一个给定原串,已经能够产生与其Hash值相同的另一原串,因此建议转向相对更安全的SHA-3等算法。
Example-两个MD5碰撞的字节串:

// 十六进制表示的字节串,先转回为字节
const buffer1 = Buffer.from('0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef','hex'));
const buffer2 = Buffer.from('0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef','hex'));
let hash = crypto.createHash('md5');
hash.update(buffer1).digest('hex'); // cee9a457e790cf20d4bdaa6d69f01e41
hash = crypto.createHash('md5');
hash.update(buffer2).digest('hex'); // cee9a457e790cf20d4bdaa6d69f01e41

消息认证码(MAC)

我们从银行汇款说起:
客户A199申请了跨行转账后,A银行发消息给B银行:

从账户A199向账户B778汇款100元

金融是很严肃的事,稍有不慎,就是损失巨款,B银行怎么确定这是A银行发来的,而不是黑客伪造的呢?这就需要消息的认证技术。
消息认证码(Message Authentication Code),简称MAC,是一种确认完整性并进行认证的技术,MAC接收任意长度的消息和一个密钥作为输入,输出固定长度的数据,这个数据称为MAC值。这和单向散列函数非常像,同时我们目前使用的MAC技术主要是HMAC,H是Hash-Based,是基于单向散列的MAC技术,任何高强度的散列函数都可以被用于HMAC,例如SHA-1、SHA-256所构造的HMAC分别称为HMAC-SHA1、HMAC-SHA256。

MAC

简单地来说,HMAC就是糅合了密钥的散列函数,因此,计算出的MAC值,是带密钥特征的Hash值,只要B银行持有A银行的密钥(在对称加密中,双方一定持有互相的密钥,因为是同一把),A在发送消息时,使用HMAC计算好这条消息的MAC值一起发送给B,B只要将消息与A的密钥同时输入HMAC,将自己计算出的MAC值,与A传送过来的MAC值作对比,就知道这条消息是不是同样持有密钥的A所发送的消息。

数字签名

即使有了HMAC,我们同样会面临问题,那就是虽然可以验证是否是密钥持有者发来的信息,但由于A、B都持有同一把密钥,那么就存在A可能会否认或者B伪装成A的情况,毕竟双方都持有密钥,双方都能生成同样的消息同样的HMAC。
那么有没有一种方法,能够使B仅仅能验证A的HMAC,却不能产生A的HMAC?目前通过对称密钥的方式,明显是不可以的。公钥加密在这里就派上了大用场,根据公钥加密的原理,私钥和公钥的加解密是可以互换的,也就是使用公钥加密,可以使用私钥解密,如果使用私钥加密,那么公钥则可以解密。那么我们可以用A银行的私钥生成签名,B银行持有A银行的公钥用于验证签名
签名可能不大好理解,其实就是加密:

数字签名的应用

A银行在发送消息前计算消息的Hash值,再将Hash值用自己的私钥加密生成数字签名,将数字签名附加在消息正文的后面,B银行收到消息后,计算消息正文的Hash值,再将A传输过来的数字签名用A的公钥解密为Hash值,对比两个Hash值,如果相同,则代表消息正文既未被篡改同时又是A发来的消息,既保证了消息的完整性又做了身份认证。
数字签名为什么要对Hash值签名,而不是消息正文呢?因为消息正文的信息量往往比较大,自然是对Hash值签名,效率比较高。

应对重放攻击:

虽然通过HMAC以及数字签名,黑客无法冒充A银行向B银行发送消息,但是他可以去A银行申请让B银行汇款100元至自己的账户,然后中途窃听这条请求,保存请求,并重复发送1万次,B银行就会转账共100万元至黑客的账号。整个过程,黑客无需解密、篡改等任何手段,做的仅仅是将相同的请求发送一万次,这称为重放攻击。所以仅仅使用HMAC和数字签名却不增加随机数、递增消息版本号等其他补充性的安全手段,很容易遭受应对重放攻击。

数字证书

在前面的公钥加密中我们有说到,公钥加密无法防范中间人攻击,甚至可以说,有我们公钥的人,就能冒充另一个持有公钥的人,我们没法判断他是否是他声称的那个人。
A、B两方通讯时,需要先交换密钥,A将自己的公钥传送给B,B将自己的公钥传送给A,此时如果有黑客在中间截断他们的通讯,成为他们的中间节点,则黑客可以冒充A向B发送自己的公钥,再冒充B向A发送自己的公钥,他们通讯时都使用黑客的公钥加密,黑客均可解密。
B银行怎么能够验证自己所获取的公钥,是不是A银行的呢?我们的解决方式是引入数字证书,数字证书是有认证机构(Certification Authority,CA)签发的文件,其中带有申请签发者的公钥以及签发机构的签名,数字证书的格式普遍采用的是X.509V3国际标准,一个标准的X.509数字证书包含以下一些内容:

    证书的版本信息;
    证书的序列号,每个证书都有一个唯一的证书序列号;
    证书所使用的签名算法;
    证书的发行机构名称,命名规则一般采用X.500格式;
    证书的有效期,通用的证书一般采用UTC时间格式,它的计时范围为1950-2049;
    证书所有人的名称,命名规则一般采用X.500格式;
    证书所有人的公开密钥;  
    证书发行者对证书的签名。

B银行拿到证书后,可以向证书发行机构(CA)验证此证书的真实性,一旦符合,则代表此公钥就是A银行的。不过也有一个前提,那就是这个CA值得信任,CA有很多,每个人都可以建立自己的CA,因此如果你拿到的是黑客自建CA签发的证书,也是没有意义的,所以不要随意信任所有CA,应该选择性地信任,否则数字证书也是形同虚设。
我们使用的HTTPS就是在HTTP之下放置了一层安全套接层,其中有使用公钥加密,因此如果服务器要开始HTTPS,是需要提供自己的数字证书的,一般企业都会缴费向一些大的CA机构申请证书,大致流程如下:


申请数字证书的流程

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342