目录
第九章
9.1 羊妈妈的认证
大灰狼把黑色的爪子伸进门缝,说道:
“我是你们的妈妈,快快开门吧!”
七只小羊回答道:
“不是不是,妈妈的手是白色的,你的手是黑色的,你不是我们的妈妈!”
听了小羊们的话,大灰狼把它的爪子染成了白色,于是小羊们就被大灰狼的白爪子给骗了,便打开了门。
小羊们想要依靠手的颜色来进行认证,但却被大灰狼成功地进行了“伪装”。这是因为小羊们用来认证的“白色的手”是大灰狼也能够模仿出来的。
如果有一种“只有羊妈妈才能生成的信息”,那就可以实现更可靠的认证了吧。
9.2 本章学习的内容
本章中我们将学习数字签名的相关知识。数字签名是一种将相当于现实世界中的盖章、签字的功能在计算机世界中进行实现的技术。使用数字签名可以识别篡改和伪装,还可以防止否认。
9.3数字签名
9.3.1 Alice的借条
我们还是像以前一样,通过Alice和 Bob的故事来了解一下需要使用数字签名的场景。
假设 Alice 需要向 Bob 借 100 万元。不过,Alice 和 Bob 离得很远,无法直接见面。通过银行汇款, Alice可以立刻从Bob那里收到钱,但是Alice的借条应该怎样发送给Bob呢?可以用挂号信寄过去,不过那样需要花上一段时间,能不能用电子邮件来发送借条呢?比如:
“Bob,我向你借款 100 万元。——Alice”
显然,这样的邮件无法代替借条,Bob看到这封邮件也不会轻易相信,因为电子邮件是很容易被伪造的。Alice 写的邮件有可能被篡改,也有可能是有人伪装成 Alice 发送了这封邮件,或者Alice也可以事后以“我不知道这张借条”为理由来进行否认。
9.3.2 从消息认证码到数字签名
消息认证码的局限性
通过使用第 8 章中介绍的消息认证码,我们可以识别消息是否被篡改或者发送者身份是否被伪装,也就是可以校验消息的完整性,还可以对消息进行认证。然而,在出具借条的场景中却无法使用消息认证码,因为消息认证码无法防止否认。
消息认证码之所以无法防止否认,是因为消息认证码需要在发送者Alice和接收者Bob两者之间共享同一个密钥。正是因为密钥是共享的,所以能够使用消息认证码计算出正确MAC值的并不只有发送者Alice,接收者Bob也可以计算出正确的MAC 值。由于Alice和Bob双方都能够计算出正确的MAC值,因此对于第三方来说,我们无法证明这条消息的确是由Alice生成的。上述内容我们在第8章中已经讲解过了,这里算是复习一下吧。
通过数字签名解决问题
下面请大家开动一下脑筋。假设发送者 Alice 和接收者 Bob 不需要共享一个密钥,也就是说, Alice和Bob各自使用不同的密钥。
我们假设Alice使用的密钥是一个只有Alice自己才知道的私钥。当Alice发送消息时,她用私钥生成一个“签名”。相对地,接收者Bob则使用一个和Alice不同的密钥对签名进行验证。使用Bob的密钥无法根据消息生成签名,但是用Bob的密钥却可以对Alice所计算的签名进行验证,也就是说可以知道这个签名是否是通过Alice的密钥计算出来的。如果真有这么一种方法的话,那么不管是识别篡改、伪装还是防止否认就都可以实现了吧?
实际上,这种看似很神奇的技术早就已经问世了,这就是数字签名( digital signature) 0。
---------------------------------------------------------------------------------------------------------------------------------专栏:不要和邮件末尾的签名搞混我们经常会在邮件的末尾附上一段文字来表明自己的名字和所在的公司等信息,这些文字也称为“签名”。邮件的签名和本章所说的数字签名(签名)是完全不同的两码事。在邮件末尾添加的签名是一串固定的文字,而本章所说的数字签名(签名)则是根据消息内容生成的一串“只有自己才能计算出来的数值",因此数字签名(签名)的内容是随消息的改变而改变的。
---------------------------------------------------------------------------------------------------------------------------------
9.3.3 签名的生成和验证
让我们来稍微整理一下。
在数字签名技术中,出现了下面两种行为。
⚪生成消息签名的行为
⚪验证消息签名的行为
生成消息签名这一行为是由消息的发送者Alice来完成的,也称为“对消息签名”。生成签名就是根据消息内容计算数字签名的值,这个行为意味着“我认可该消息的内容”。
验证数字签名这一行为一般是由消息的接收者 Bob 来完成的,但也可以由需要验证消息的第三方来完成,这里的第三方在本书中被命名为验证者 Victor。验证签名就是检查该消息的签名是否真的属于 Alice,验证的结果可以是成功或者失败,成功就意味着这个签名是属于Alice的,失败则意味着这个签名不是属于 Alice的。
在数字签名中,生成签名和验证签名这两个行为需要使用各自专用的密钥来完成。
Alice使用“签名密钥”来生成消息的签名,而Bob 和 Victor 则使用“验证密钥”来验证消息的签名。数字签名对签名密钥和验证密钥进行了区分,使用验证密钥是无法生成签名的。这一点非常重要。此外,签名密钥只能由签名的人持有,而验证密钥则是任何需要验证签名的人都可以持有。
刚才讲的这部分内容,是不是觉得似曾相识呢?
没错,这就是我们在第 5章中讲过的公钥密码嘛。公钥密码和上面讲的数字签名的结构非常相似。在公钥密码中,密钥分为加密密钥和解密密钥,用加密密钥无法进行解密。此外,解密密钥只能由需要解密的人持有,而加密密钥则是任何需要加密的人都可以持有。你看,数字签名和公钥密码是不是很像呢?
实际上,数字签名和公钥密码有着非常紧密的联系,简而言之,数字签名就是通过将公钥密码“反过来用”而实现的。下面我们来将密钥的使用方式总结成一张表(表9-1)。
9.3.4 公钥密码与数字签名
好像我们讲得有点快,下面我们再来详细讲一讲公钥密码与数字签名之间的关系。要实现数字签名,我们可以使用第5章中介绍的公钥密码机制。公钥密码包括一个由公钥和私钥组成的密钥对,其中公钥用于加密,私钥用于解密(图 9-1)。
数字签名中也同样会使用公钥和私钥组成的密钥对,不过这两个密钥的用法和公钥密码是相反的,即用私钥加密相当于生成签名,而用公钥解密则相当于验证签名①。请大家通过比较两张图示来理解一下“反过来用”到底是什么样的情形(图 9-2)。 那么为什么加密相当于生成签名,而解密相当于验证签名呢?要理解这个问题,我们需要回想一下公钥密码中讲过的知识,即组成密钥对的两个密钥之间存在严密的数学关系,它们是一对无法拆散的伙伴。
用公钥加密所得到的密文,只能用与该公钥配对的私钥才能解密;同样地,用私钥加密所得到的密文,也只能用与该私钥配对的公钥才能解密。也就是说,如果用某个公钥成功解密了密文,那么就能够证明这段密文是用与该公钥配对的私钥进行加密所得到的。
用私钥进行加密这一行为只能由持有私钥的人完成,正是基于这一事实,我们才可以将用私钥加密的密文作为签名来对待(图 9-3、图 9-4)。由于公钥是对外公开的,因此任何人都能够用公钥进行解密,这就产生了一个很大的好处,即任何人都能够对签名进行验证。
9.4 数字签名的方法
下面我们来具体介绍两种生成和验证数字签名的方法。
⚪直接对消息签名的方法
⚪对消息的散列值签名的方法
直接对消息签名的方法比较容易理解,但实际上并不会使用;对消息的散列值签名的方法稍微复杂一点,但实际中我们一般都使用这种方法。
9.4.1 直接对消息签名的方法
本节中,发送者Alice要对消息签名,而接收者Bob要对签名进行验证,我们来讲一下具体的方法。Alice需要事先生成一个包括公钥和私钥的密钥对,而需要验证签名的Bob则需要得到Alice的公钥。在此基础上,签名和验证的过程如下(图 9-5)。
(1) Alice用自己的私钥对消息进行加密。
用私钥加密得到的密文就是Alice对这条消息的签名,由于只有Alice才持有自己的私钥,因此除了Alice以外,其他人是无法生成相同的签名(密文)的。
(2) Alice将消息和签名发送给Bob。
(3) Bob 用Alice的公钥对收到的签名进行解密。
如果收到的签名确实是用Alice的私钥进行加密得到的密文(签名),那么用Alice的公钥应该能够正确解密。如果收到的签名不是用Alice的私钥进行加密得到的密文,那么就无法用Alice的公钥正确解密(解密后得到的数据看起来是随机的)。
(4) Bob将签名解密后得到的消息与Alice直接发送的消息进行对比。如果两者一致,则签名验证成功;如果两者不一致,则签名验证失败。
看了上述讲解,各位读者的脑海中一定会冒出很多问号。为了讲解方便,我们稍后(9.5节)再集中回答这些疑问,请大家稍安勿躁。
9.4.2 对消息的散列值签名的方法
上一节中我们讲过了直接对消息签名的方法,但这种方法需要对整个消息进行加密,非常耗时,这是因为公钥密码算法本来就非常慢。那么,我们能不能生成一条很短的数据来代替消息本身呢?从密码学家的工具箱里面找找看,果然找到了一个跟我们的目的十分契合的工具,它就是第 7 章中介绍的单向散列函数。
于是我们不必再对整个消息进行加密(即对消息签名),而是只要先用单向散列函数求出消息的散列值,然后再将散列值进行加密(对散列值签名)就可以了。无论消息有多长,散列值永远都是这么短,因此对其进行加密(签名)是非常轻松的。
(1) Alice用单向散列函数计算消息的散列值。
(2) Alice用自己的私钥对散列值进行加密。
用私钥加密散列值所得到的密文就是 Alice对这条散列值的签名,由于只有Alice才持有自己的私钥,因此除了Alice以外,其他人是无法生成相同的签名(密文)的。
(3) Alice将消息和签名发送给Bob。
(4) Bob 用 Alice的公钥对收到的签名进行解密。
如果收到的签名确实是用Alice的私钥进行加密而得到的密文(签名),那么用Alice的公钥应该能够正确解密,解密的结果应该等于消息的散列值。如果收到的签名不是用Alice的私钥进行加密而得到的密文,那么就无法用Alice的公钥正确解密(解密后得到的数据看起来是随机的)。
(5) Bob将签名解密后得到的散列值与Alice直接发送的消息的散列值进行对比。
如果两者一致,则签名验证成功;如果两者不一致,则签名验证失败。
整个过程如图 9-6 所示,请大家和上一节中的图 9-5 比较一下,看看在哪里用到了单向散列函数。
我们将数字签名中生成签名和验证签名的过程整理成一张时间流程图(图9-7)。
/
9.5 对数字签名的疑问
上面我们对数字签名进行了基本的讲解,但恐怕很多读者并不十分认同,至少我在刚听到数字签名这个话题时,心中也产生了不少疑问。下面我们就设想一些读者可能会产生的疑问并进行解答。
9.5.1密文为什么能作为签名使用
疑问
用私钥加密消息得到签名,然后再用公钥解密消息并验证签名,这个过程我理解了,但是密文为什么能够具备签名的意义呢?
解答
虽说实际进行的处理内容是用私钥进行加密,但这里的加密并非是为了保证机密性而进行的。
数字签名是利用了“没有私钥的人事实上无法生成使用该私钥所生成的密文”这一性质来实现的。这里所生成的密文并非被用于保证机密性,而是被用于代表一种只有持有该密钥的人才能够生成的信息。
这样的信息一般称为认证符号(authenticator),消息认证码也是认证符号的一种,数字签名也是一样。数字签名是通过使用私钥进行加密来产生认证符号的。
另外,七只小羊用来认证羊妈妈所使用的手的颜色也是一种认证符号(只是这个认证符号被大灰狼伪造了)。
9.5.2 数字签名不能保证机密性吗
疑问
从图 9-6 来看,消息没有经过加密就发送了,这样不就无法保证消息的机密性了吗?
解答
的确如此,数字签名的作用本来就不是保证机密性。如果需要保证机密性,则可以不直接发送消息,而是将消息进行加密之后再发送。关于密码和签名的组合方法,我们会在第13章中详细介绍。
9.5.3 这种签名可以随意复制吗
疑问
数字签名只不过是计算机上的一种数据,貌似很容易被复制。但如果可以轻易复制出相同的内容,那还能用作签名吗?
解答
的确,虽然叫作签名,但它也仅仅是计算机上的一种普通的数据而已。数字签名可以附加在消息的末尾,也可以和消息分离,单独作为文件来发送,但无论如何,我们都可以像复制普通的文件一样,很容易地复制出任意个内容相同的副本。
但是,签名可以被复制,并不意味着签名就没有意义,因为签名所表达的意义是特定的签名者对特定的消息进行了签名,即便签名被复制,也并不会改变签名者和消息的内容。
在现实世界中,签名的原件是独一无二的,用复印机复印出来的副本和原件是有区别的,但在计算机中文件的副本与原件之间是无法区别的,这也许就是这一疑问产生的原因吧。然而,签名是不是原件并不重要,真正重要的是特定的签名者与特定的消息绑定在了一起这一事实。
无论将签名复制多少份,“是谁对这条消息进行了签名”这一事实是不会发生任何改变的。总之,签名可以被复制,但这并不代表签名会失去意义。
9.5.4 消息内容会不会被任意修改
疑问
数字签名只不过是普通的数据,消息和签名两者都是可以任意修改的,这样的签名还有意义吗?
解答
的确,签名之后也可以对消息和签名进行修改,但是这样修改之后,验证签名就会失败,进行验证的人就能够发现这一修改行为。数字签名所要实现的并不是防止修改,而是识别修改。修改没问题,但验证签名会失败。
追问
能不能同时修改消息和签名,使得验证签名能够成功呢?
解答
事实上是做不到的。
以对散列值签名为例,只要消息被修改 1 比特,重新计算的散列值就会发生很大的变化,要拼凑出合法的签名,必须在不知道私钥的前提下对新产生的散列值进行加密,事实上这是无法做到的,因为不知道私钥就无法生成用该私钥才能生成的密文。
这个问题相当于对数字签名的攻击,我们稍后会更加详细地讲解。
9.5.5 签名会不会被重复使用
疑问
如果得到了某人的数字签名,应该就可以把签名的部分提取出来附加在别的消息后面,这样的签名还有效吗?
解答
的确,可以将签名部分提取出来并附加到别的消息后面,但是验证签名会失败。
将签名提取出来这一行为,就好像是现实世界中把纸质合同上的签名拓下来一样。然而在数字签名中,签名和消息之间是具有对应关系的,消息不同签名内容也会不同,因此事实上是无法做到将签名提取出来重复使用的。
总之,将一份签名附加在别的消息后面,验证签名会失败。
9.5.6 删除签名也无法“作废合同”吗
疑问
如果是纸质的借据,只要将原件撕毁就可以作废。但是带有数字签名的借据只是计算机文件,将其删除也无法保证确实已经作废,因为不知道其他地方是否还留有副本。无法作废的签名是不是非常不方便呢?
解答
的确,带有数字签名的借据即便删除掉也无法作废,要作废带有数字签名的借据,可以重新创建一份相当于收据的文书,并让对方在这份文书上加上数字签名。
拿一个具体的例子来说,如果我们想要将过去使用过的公钥作废,就可以创建一份声明该公钥已作废的文书并另外加上数字签名。
也可以在消息中声明该消息的有效期并加上数字签名,例如公钥的证书就属于这种情况,关于这一点,我们将在第10章中详细介绍。
9.5.7 如何防止否认
疑问
消息认证码无法防止否认,为什么数字签名就能够防止否认呢?
解答
防止否认与“谁持有密钥”这一问题密切相关。
在消息认证码中,能够计算MAC值的密钥(共享密钥)是由发送者和接收者双方共同持有的,因此发送者和接收者中的任何一方都能够计算MAC值,发送者也就可以声称“这个MAC值不是我计算的,而是接收者计算的”。
相对地,在数字签名中,能够生成签名的密钥(私钥)是只有发送者才持有的,只有发送者才能够生成签名,因此发送者也就没办法说“这个签名不是我生成的”了。
当然,严格来说,如果数字签名的生成者说“我的私钥被别人窃取了”,也是有可能进行否认的,关于这个问题我们会在第10章中进行探讨。
9.5.8 数字签名真的能够代替签名吗
疑问
纸质借据上如果不签名盖章的话,总是觉得不太放心。数字签名真的能够有效代替现实世界中的签名和盖章吗?
解答
这个疑问应该说非常合理。
数字签名技术有很多优点,例如不需要物理交换文书就能够签订合同,以及可以对计算机上的任意数据进行签名等。然而,对于实际上能不能代替签名这个问题还是有一些不安的因素。其中一个很大的原因恐怕是签订合同、进行认证这样的行为是一种社会性的行为。
我们在对文件进行数字签名时,没有人会自己亲手去实际计算数字签名的算法,而只是在阅读软件给出的提示信息后按下按钮或者输入口令。
然而,这个软件真的值得信任吗?软件虽然会提示用户说“请对该文件签名”,但软件所提示的消息真的是经过这个软件本身进行签名的吗?有没有可能是这个软件本身感染了病毒,而病毒实际上对另外一份文件进行了签名呢?这样的危险我们还能够想到很多,而实际上它们确实有可能发生。
美国于 2000 年颁布了 E-SIGN 法案,日本也于 2001 年颁布了电子签名及其认证业务的相关法律(电子签名法)。这些法律为将电子手段实现的签名与手写的签名和盖章同等处理提供了法律基础。然而在实际应用中,很有可能会产生与数字签名相关的问题以及围绕数字签名有效性的诉讼。
数字签名技术在未来将发挥重要的作用,但是单纯认为数字签名比普通的印章或手写签名更可信是很危险的。一种新技术只有先被人们广泛地认知,并对各种问题制定相应的解决办法之后,才能被社会真正地接受。
9.6 数字签名的应用实例
下面我们来介绍一些数字签名的具体应用实例。
9.6.1 安全信息公告
一些信息安全方面的组织会在其网站上发布一些关于安全漏洞的警告,那么这些警告信息是否真的是该组织所发布的呢?我们如何确认发布这些信息的网页没有被第三方篡改呢?
在这样的情况下就可以使用数字签名,即该组织可以对警告信息的文件施加数字签名,这样一来世界上的所有人就都可以验证警告信息的发布者是否合法。
信息发布的目的是尽量让更多的人知道,因此我们没有必要对消息进行加密,但是必须排除有人恶意伪装成该组织来发布假消息的风险。因此,我们不加密消息,而只是对消息加上数字签名,这种对明文消息所施加的签名,一般称为明文签名(clearsign)。
9.6.2 软件下载
我们经常会从网上下载软件,有时候是我们自己决定去下载某个软件,有时候则是我们所使用的软件自动去下载了另外一些软件。
无论哪种情况,我们都需要判断所下载的软件是否可以安全运行,因为下载的软件有可能被主动攻击者Mallory篡改,从而执行一些恶意的操作。例如,明明是下载了一个游戏软件,结果却可能是一个会删除硬盘上所有数据的程序,又或者可能是一个会将带有病毒的邮件发送给所有联系人的程序。
为了防止出现这样的问题,软件的作者可以对软件加上数字签名,而我们只要在下载之后验证数字签名,就可以识别出软件是否遭到了主动攻击者Mallory的篡改。
一种名为带签名的Applet的软件就是一个具体的例子。这种软件是用Java编写的(一种浏览器进行下载并执行的软件),并加上了作者的签名,而浏览器会在下载之后对签名进行验证。
此外,智能手机上广泛使用的Android操作系统中是无法安装没有数字签名的应用软件的。在签署数字签名时,为了识别应用开发者的身份,需要使用第10章中将要介绍的“证书”。不过这个证书只被用来识别应用开发者的身份等信息,并不是经过认证机构(Certificate Authority)签名的。
不过,数字签名只是能够检测软件是否被篡改过,而并不能保证软件本身不会做出恶意的行为。如果软件的作者本身具有恶意的话,那么再怎么加上数字签名也是无法防范这种风险的。
9.6.3 公钥证书
在验证数字签名时我们需要合法的公钥,那么怎么才能知道自己得到的公钥是否合法呢?我们可以将公钥当作消息,对它加上数字签名。像这样对公钥施加数字签名所得到的就是公钥证书,关于证书我们会在第10章详细介绍。
9.6.4 SSL/TLS
SSL/TLS 在认证服务器身份是否合法时会使用服务器证书,它就是加上了数字签名的服务器公钥。相对地,服务器为了对客户端(用户)进行认证也会使用客户端证书。关于SSL/TLS我们会在第 14 章详细介绍。
9.7 通过 RSA实现数字签名
下面我们来用RSA的数字签名算法实际尝试一下签名的过程吧。RSA公钥密码算法已经在第 5 章中详细介绍过了,因此在这里我们只讲解一下生成和验证签名的过程。此外,为了简单起见,我们不使用单向散列函数,而是直接对消息进行签名。
关于将 RSA 和单向散列函数相结合来进行数字签名的详细说明,请参见 RFC 3447 (PublicKey Cryptography Standards ( PKCS) #1 )。
9.7.1 用 RSA 生成签名
在 RSA 中,被签名的消息、密钥以及最终生成的签名都是以数字形式表示的。在对文本进行签名时,需要事先将文本编码成数字。用RSA生成签名的过程可用下列公式来表述:
这里所使用的 D 和 N 就是签名者的私钥。签名就是对消息的 D 次方求 mod N 的结果,也就是说将消息和自己相乘D次,然后再除以N求余数,最后求得的余数就是签名。
生成签名后,发送者就可以将消息和签名发送给接收者了。
9.7.2 用RSA验证签名
RSA 的签名验证过程可用下列公式来表述:
这里所使用的 E和 N 就是签名者的公钥。接收者计算签名的 E 次方并求 mod N,得到“由签名求得的消息”,并将其与发送者直接发送过来的“消息”内容进行对比。如果两者一致则签名验证成功,否则签名验证失败。
我们把刚才讲解的内容整理成表9-2。关于这里出现的私钥(D、N)和公钥(E、N)的生成方法,请参见 5.6.4 节的内容。
9.7.3 具体实践一下吧
下面我们来通过具体的数字,用RSA来实际生成和验证签名。在这里我们使用5.6.5节中生成的密钥对:
公钥:E=5;N=323
私钥:D=29;N=323
由于N为323,因此消息需要为-0~322这个范围内的整数。在这里假设我们需要对123这个消息进行签名。
生成签名下面我们用私钥(D, N)=(29, 323)来生成消息123的签名。
消息^D mod N= 123^29 mod 323
=157
签名是157。向接收者发送的内容为:
(消息,签名)=(123,157)
这两个数字。
验证签名
接受者收到的内容为:
(消息,签名)=(123,157)
我们用公钥(E,N)=(5,323)来计算由签名求得的消息。
签名^E mod N= 157^5 mod 323
= 123
我们得到的消息 123 与发送者直接发送过来的消息 123 是一致的,因此签名验证成功。
9.8 其他的数字签名
除了RSA之外还存在其他的数字签名算法,下面我们简单介绍一下ElGamal、DSA、ECDSA 和 Rabin 这几种方式。
9.8.1 ElGamal 方式
ElGamal方式是由Taher ElGamal设计的公钥算法,利用了在mod N中求离散对数的困难度。ElGamal方式可以被用于公钥密码和数字签名。密码软件GnuPG中也曾使用过ElGamal方式,但由于1.0.2版本中数字签名的实现上存在漏洞,因此现在在GnuPG中ElGamal仅被用于公钥密码。
9.8.2 DSA
DSA ( Digital Signature Algorithm)是一种数字签名算法,是由 NIST ( National Institute ofStandards and Technology,美国国家标准技术研究所)于1991年制定的数字签名规范(DSS)。DSA 是 Schnorr 算法与 ElGammal 方式的变体,只能被用于数字签名。
9.8.3 ECDSA
ECDSA ( Elliptic Curve Digital Signature Algorithm )是一种利用椭圆曲线密码来实现的数字签名算法( NIST FIPS 186-3)。关于椭圆曲线密码的介绍请参见附录。
9.8.4 Rabin 方式
Rabin方式是由M. O. Rabin设计的公钥算法,利用了在 mod N中求平方根的困难度。Rabin方式可以被用于公钥密码和数字签名。
9.9 对数字签名的攻击
9.9.1中间人攻击
针对公钥密码的中间人攻击(man-in-the-middle attack ) (5.7.4节)对于数字签名来说也颇具威胁。
对数字签名的中间人攻击,具体来说就是主动攻击者 Mallory介入发送者和接收者的中间,对发送者伪装成接收者,对接收者伪装成发送者,从而能够在无需破解数字签名算法的前提下完成攻击。
要防止中间人攻击,就需要确认自己所得到的公钥是否真的属于自己的通信对象。例如,我们假设Bob需要确认自己所得到的是否真的是Alice的公钥, Bob可以给Alice打个电话,问一下自己手上的公钥是不是真的(如果电话通信也被Mallory控制,那这个方法就不行了)。
要在电话中把公钥的内容都念一遍实在是太难了,这里有一个简单的方法,即 Alice 和 Bob分别用单向散列函数计算出散列值,然后在电话中相互确认散列值的内容即可。实际上,涉及公钥密码的软件都可以显示公钥的散列值,这个散列值称为指纹(fingerprint)。指纹的内容就是像下面这样的一串字节序列。
85 74 EC 5E BE DA ЗE D3 24 ЗE 08 22 9C 30 BA 4B 7B B4 A3
上面介绍的内容是关于人与人之间如何对公钥进行认证的,实际上大多数情况下都是计算机程序之间来进行公钥的认证,这个时候就需要使用公钥的“证书”。
9.9.2 对单向散列函数的攻击
数字签名中所使用的单向散列函数必须具有抗碰撞性,否则攻击者就可以生成另外一条不同的消息,使其与签名所绑定的消息具有相同的散列值。
9.9.3 利用数字签名攻击公钥密码
在 RSA 中,生成签名的公式是:
签名 = 消息^D mod N
这个公式和公钥密码中解密的操作是等同的,也就是说可以将“请对消息签名”这一请求理解为“请解密消息”。利用这一点,攻击者可以发动一种巧妙的攻击,即利用数字签名来破译密文。我们假设现在Alice和 Bob正在进行通信,主动攻击者Mallory 正在窃听。Alice用Bob 的公钥加密消息后发送给 Bob,发送的密文是用下面的公式计算出来的。
密文 = 消息^D mod N
Mallory窃听到Alice发送的密文并将其保存下来,由于 Mallory想要破译这段消息,因此他给Bob写了这样一封邮件。
Mallory 将刚刚窃听到的密文作为上述邮件的附件一起发送给 Bob,即:
附件数据 = 密文
Bob看到了Mallory的邮件,发现附件数据的确只是随机数据(但其实这是Alice用Bob的公钥加密的密文)。
于是 Bob 对附件数据进行签名,具体情形如下。
签名 = 附件数据 ^D mod N (RSA 生成签名)
= 密文 P mod N (附件数据实际上是密文)
= 消息 (进行了解密操作)
Bob 的本意是对随机的附件数据施加数字签名,但结果却无意中解密了密文。如果不小心将上述签名的内容(=消息)发送给了Mallory,那么Mallory不费吹灰之力就可以破译密文了。这种诱使接收者本人来进行解密的方法实在是非常大胆。
在上面的例子中,Bob可能会察觉到签名的操作实际上是在对消息进行解密(如果使用混合密码系统的话,签名的结果也是随机数据,因此 Bob 可能不会察觉)。
对于这样的攻击,我们应该采取怎样的对策呢?首先,不要直接对消息进行签名,对散列值进行签名比较安全;其次,公钥密码和数字签名最好分别使用不同的密钥对。实际上,GnuPG和PGP都可以生成多个密钥对。
然而,最重要的就是绝对不要对意思不清楚的消息进行签名,尤其是不要对看起来只是随机数据的消息进行签名。从签名的目的来说,这一点应该是理所当然的,因为谁都不会在自己看不懂的合同上签字盖章的。
9.9.4 潜在伪造
上面我们提到了随机消息,借这个话题我们来说一说潜在伪造。如果一个没有私钥的攻击者能够对有意义的消息生成合法的数字签名,那么这个数字签名算法一定是不安全的,因为这样的签名是可以被伪造的。
然而,即使签名的对象是无意义的消息(例如随机比特序列),如果攻击者能够生成合法的数字签名(即攻击者生成的签名能够正常通过校验),我们也应该将其当成是对这种签名算法的一种潜在威胁。这种情况称为对数字签名的潜在伪造。
在用 RSA 来解密消息的数字签名算法中,潜在伪造是可能的。因为我们只要将随机比特序列S用RSA的公钥加密生成密文M,那么S就是M的合法数字签名(这与上一节中所提到的内容正好相反)。由于攻击者是可以获取公钥的,因此对数字签名进行潜在伪造也就可以实现了。
为了应对潜在伪造,人们在改良RSA 的基础上开发出了一种签名算法,叫作RSA-PSS( Probabilistic Signature Scheme)。RSA-PSS并不是对消息本身进行签名,而是对其散列值进行签名。另外,为了提高安全性,在计算散列值的时候还要对消息加盐(salt)。关于RSA-PSS的技术规范请参见2001年的RFC3447 ( Public-Key Cryptography Standards (PKCS) #1: RSACryptography Specifications Version 2.1 )。
9.9.5 其他攻击
针对公钥密码的攻击方法大部分都能够被用于攻击数字签名,例如用暴力破解来找出私钥,或者尝试对 RSA 的 N 进行质因数分解等。
9.10 各种密码技术的对比
下面我们将数字签名技术与其他的密码技术进行一下比较。
9.10.1 消息认证码与数字签名
第8章中我们介绍了消息认证码,它和本章介绍的数字签名很相似,都是用来校验完整性和进行认证的技术。可以通过对对称密码和公钥密码的对比来理解消息认证码与数字签名的区别。我们把对比的过程整理成表 9-3。
9.10.2 混合密码系统与对散列值签名
在混合密码系统中,消息本身是用对称密码加密的,而只有对称密码的密钥是用公钥密码加密的,即在这里对称密码的密钥就相当于消息。
另一方面,数字签名中也使用了同样的方法,即将消息本身输入单向散列函数求散列值,然后再对散列值进行签名,在这里散列值就相当于消息。
如果将两者的特点总结成简短的标语,我们可以说:对称密码的密钥是机密性的精华,单向散列函数的散列值是完整性的精华。关于上述思想,我们会在最后一章进行探讨。
9.11 数字签名无法解决的问题
用数字签名既可以识别出篡改和伪装,还可以防止否认。也就是说,我们同时实现了确认消息的完整性、进行认证以及否认防止。现代社会中的计算机通信从这一技术中获益匪浅。
然而,要正确使用数字签名,有一个大前提,那就是用于验证签名的公钥必须属于真正的发送者。即便数字签名算法再强大,如果你得到的公钥是伪造的,那么数字签名也会完全失效。
现在我们发现自己陷入了一个死循环——数字签名是用来识别消息篡改、伪装以及否认的,但是为此我们又必须从没有被伪装的发送者得到没有被篡改的公钥才行。
为了能够确认自己得到的公钥是否合法,我们需要使用证书。所谓证书,就是将公钥当作一条消息,由一个可信的第三方对其签名后所得到的公钥。
当然,这样的方法只是把问题转移了而已。为了对证书上施加的数字签名进行验证,我们必定需要另一个公钥,那么如何才能构筑一个可信的数字签名链条呢?又由谁来颁发可信的证书呢?到这一步,我们就已经踏入了社会学的领域。我们需要让公钥以及数字签名技术成为一种社会性的基础设施,即公钥基础设施( Public Key Infrastructure ),简称PKI。关于证书和PKI我们将在第 10 章中介绍。
9.12 本章小结
本章中我们介绍了数字签名的相关知识,学习了如何逆向使用公钥密码来实现数字签名,并使用RSA具体实践了数字签名的生成和验证。此外,我们还探讨了针对数字签名的攻击方法以及数字签名与消息认证码之间的关系。通过数字签名我们可以识别篡改和伪装,还可以防止否认。数字签名是一种非常重要的认证技术,但前提是用于验证签名的发送者的公钥没有被伪造。要确认公钥是否合法,可以对公钥施加数字签名,这就是证书。下一章我们将学习证书的相关知识。
------------------------------------------
感谢您阅读本文!如果您对文章内容有任何疑问或者想要提出任何问题,欢迎在评论区留言。我会尽力回答您的疑问,并与您交流。期待与您互动