您当前的位置:首页 > 计算机 > 安全防护

身份验证、中间人攻击和数字签名:浅谈密码学(下)

时间:12-05来源:作者:点击数:

前面我们看到,签名具有不可伪造性,因此签名者可以很好的证明自己的身份。事实上,由于签名是不可伪造的,因此签名还有一个重要的功用。当一个人对某份文件进行签名后,这份文件便具有了法律效应:你今后无法再否认你签过这份文件,因为别人是不能伪造签名文件的。签名的这种性质叫做“抗抵赖性”。

在实际生活中,签名的抗抵赖性用的最多的场合就是签合同了。为了防止一方今后毁约,双方可以要求对方在合同上签名。此后,合同签署人的行为将受到这个签名文件的限制。一旦签名者想毁约,利益受到侵害的人便可以拿着这份合同大闹法庭,高举这份文件大叫“当初他签过名的”。

当签名用于抗抵赖时,新的问题产生了。签名的法律效应是如此之高,以至于人们往往不敢随意签名。一份合同往往同时规定了双方的权利和义务,并需要双方都在上面签名。第一个在上面签字的人就会觉得很亏:万一我签了字后对方突然翻脸耍赖不签了咋办?即使合同上规定“合同仅在双方均签署之后才有效”,这个问题仍然存在,因为后签名者将具有绝对的主动权,他想什么时候签就什么时候签,而只有他的签名才具有决定意义。因此很多时候,双方都希望能够在对方签名之后自己再签名,从而获得一些安全感。这里我们来探讨一个有趣的问题:有没有什么办法能够让双方同时签约,使得双方签名时都能确保自己的利益安全?

如果我们谈论的是传统意义上的签名,同时签名当然是有可能办到的:双方只需要拿起各自的笔,同时在文件上写下自己的名字即可。当然,事实上肯定不会有人这么做。试想这样一个荒唐的画面:两个西装笔挺的人挤在一起,两只手臂磕磕碰碰地交错在一起,然后双方同时喊“三、二、一”并一起开始写字……比起自己丢掉的脸面,自己先签名所带来的忧虑还算个屁啊。

有没有体面一些的,不那么荒唐的同时签字法呢?这里有一个很有启发性的办法。合同双方面对面地坐在桌子的两端。其中一个人在合同上写下自己姓名的第一个字母,然后传给坐在对面的第二个人;第二个人写下他自己名字的第一个字母,然后又回递给第一个人;第一个人签下自己名字的第二个字母,又交给对方要求对方写下自己的第二个字母……以此类推,直到双方都签署完自己的名字为止。为了让双方能够“同时”签完,名字较长的人偶尔可能需要连续写下两三个字母。

双方都愿意履行这一协议,因为在这个协议下双方是一点一点地签完整个文件的。第一个写字的人不会觉得自己很亏,因为写下一个字母是远不具备法律效应的;如果对方拒绝签他的第一个字母,我可以当即撕毁合同。虽然他们都不知道,究竟要写多少个字母才算签字,但大家都保持自己签下的名字长度与对方基本相当,因此不会担心对方突然放弃协议。就在这种互动的心理过程中,签名的法律效应一点一点地增强,直到最后双方写完自己的名字。

但是,这个办法不能用于数字签名。利用RSA算法进行签名是一个整体的过程,不能分成一部分一部分地进行。能不能把合同拆成若干份,然后双方一份一份地逐个签名呢?当然不行。如果某一份合同里有一个至关重要的义务性条款,后签名的人等对方签到这里后便可以立即终止签名,从而谋取利益。那么,能不能规定“仅当你把所有n个部份的文件都签过了才算签”呢?这意味着最后一次签名才具有最终的决定意义,说穿了不过是把安全问题转移到了“谁签最后这一下”,问题实质上并未改变。其实,我们的解决办法相当简单。我们可以耍一个小花招,从本质上模拟上面的“逐字母签名法”。

首先,第一个人签署这样一份文件:“我愿意以1%的概率接受该合同”。第二个人检查第一个人的签名,然后在上面附加一句“我愿意以2%的概率接受这份合同”,并进行签名,回交给第一个人。第一个人检查对方已经签名,然后继续将这个条文升级为“我愿意以3%的概率签署该合同”并签名。双方来来回回签100次,直到最后第一个人签“我愿意以99%的概率签署这份合同”,然后轮到第二个人签署“我接受该合同”,最后再轮到第一个人签署“我接受该合同”。

注意,这个“接受概率”是有实际意义的。如果在第一个人第一次签完文件后,第二个人立即放弃继续签署,法官可能会要求双方进行一次公开抽签测试,选取一个不超过100的正整数。如果这个数恰好为1,那么签署这句话的人就相当于签署了这份合同。类似地,我们也可以约定,当一人声称将以百分之p-1的概率接受此合同,另一人声明以百分之p的概率接受时,法官可以要求双方共同生成一个1至100的整数:如果它不超过p-1则双方都接受,如果它的值比p大则双方都不接受,若它的值正好等于p则合同仅被后者接受。因此,这种协议实质上是用概率法再现了“逐字母签名法”的核心思想,将法律效应的问题进行量化,使得率先签名的潜在危险减小到了原来的百分之一。

这个协议看似有些可笑,但实际上是可行的。0.01是一个很小的数,双方都能接受,心理上都有保障。况且,签合同的双方通常并没有抵赖的企图,他们只是希望双方能够同时签署协议罢了。但是从密码学协议的角度来看,利用概率进行声明签署的做法确实不那么美观,它让人感觉有些跳出了密码学的理论范围,无法用密码学的语言符号进行规范。有人可能希望我们利用一些密码学手段来实现这种“小步进签名”。比如,双方可以把自己签名后的文件加一个密,然后两人一位一位地轮流宣读自己的密钥。具体地说,A可以想一个大数a,把自己签名后的文件异或a之后传给B;B也可以生成一个同样长的数b,异或自己签名后的合同后传给A。然后,A把a的第一位告诉给B,B把b的第一位告诉A;A再把a的第二位告诉B,同时B宣布b的第二位……直到A、B两人获得对方的全部密钥。事实上,即使你不知道对方的密钥,你也可以枚举对方的密钥进行暴力破解,只不过这个难度是密钥长度的指数级别。双方逐位交换密钥,目的就在于同步减小暴力破解所需的人力、物力和时间。这样,如果中途有人退出协议,两人枚举出对方签名后的文件的难度是相当的,这对双方都很公平。不过,这个协议并不能阻止某人撒谎。其中一方发送的可能根本就不是自己的合同签名,或者宣布自己的密钥时是随便乱叫的;但在整个协议完成之前,对方完全无法察觉出来。协议的思路是好的,不过在我们进一步完善这个协议之前,该协议没有任何使用价值。

回想起前面提到的恐怖分子一例。那个例子是很有启发性的,其思想可以广泛用于实现各种“反作弊”协议。我们可以把上面的协议稍作修改:A把合同拆成两份,然后分别进行签名,并声明“仅当某人可以同时提供签名合同的两部份才能证明我签署了这份合同”。然后,A生成两个大随机数a1和a2,把前一半签名合同异或a1,把后一半签名合同异或a2。A把两份干扰后的签名合同都传给B。B随机要求查看其中一部分合同的内容,向A索取a1或a2。A把B索要的密钥传给B,向B证明他之前传过去的是真实的合同内容。类似地,B也把自己的合同拆成前后两半并分别签名,然后异或两个不同的大数传给A,并按照A的要求宣布其中一个大数。此时,双方都只拥有对方一半的签名合同,并相信另一半(自己无法解开的)合同是真实的。但根据声明,只有一半合同是不算数的,对方必须同时拥有两部份签名合同才行。接下来,双方又像刚才那样逐位报出剩下那一半合同所对应的大随机数,直到对方获得全部密钥为止。

合同内容的真实性是保证了,但这仍然不能防止某个人在协议后期虚报自己的密钥。因此,我们还需要想一个办法,让双方在逐位报数阶段中不能作假。为此,我们希望双方都能知道对方密钥的其中一部分信息,以便验证对方宣读的密钥的真实性。上述协议中,我们之所以会被对方欺骗,原因就在于对方知道我现在知道了什么不知道什么。要是有办法能够收到对方的其中一个大数,却不让对方知道我收到了哪个大数的话就好了。这样的话,对方不得不老老实实地宣布这两个大数各是多少,因为他不知道我手里有哪一个数。

我们称这种特殊的数据传送方式叫做“不经意传输”(oblivious transfer),意即我自己也不知道传过去了什么。上面这种传输需求有一个更确切的名字,叫做“1-2不经意传输”。在1-2不经意传输中,信息发送者可以准备两个不同的消息m1和m2(比方说,签名合同的前一半和后一半),接收人可以索要并获取m1和m2中的其中一个,但信息发送者不知道他要的是哪一个。实现不经意传输的方式非常巧妙。算法需要再一次用到RSA公钥加密术。首先,发送者生成两对不同的公私钥,并公开两个公钥。不妨称这两个公钥分别为公钥1和公钥2。假设接收人希望知道m1,但不希望发送人知道他想要的是m1。接收人生成一个随机数k,再用公钥1对k进行加密,传给发送者。发送者用他的两个私钥对这个加密后的k进行解密,用私钥1解密得到k1,用私钥2解密得到k2。显然,只有k1是和k相等的,k2则是一串毫无意义的数。但发送者不知道接收人加密时用的哪个公钥,因此他不知道他算出来的哪个k才是真的k。发送人把m1和k1进行异或,把m2和k2进行异或,把两个异或值传给接收人。显然,接收人只能算出m1而无法推测出m2(因为他不知道私钥2,从而推不出k2的值),同时发送人也不知道他能算出哪一个。发送人有一种办法可以作弊:他可以只发送其中一个真实的异或值(而编造另一个异或值),或者用k1和k2对同一个消息进行异或。不过这需要发送者能够猜出信息接收者最初选的是公钥1还是公钥2。如果接收人用公钥1对k加密,但最后得到的却是m2(或者什么都没得到),那发送人的企图就被识破了。

有了1-2不经意传输协议,我们之前的同步签名问题就彻底解决了。为了让协议更安全,我们还可以让双方各生成n份合同的拷贝,使得成功欺骗的概率仅为1/2^n。一个完整的同步签名协议如下:

A、B双方各生成带编号的n份一模一样的合同,把每份合同拆成前后两半并分别签名。约定仅当对方同时获得了同一编号的两部分签名合同,合同才算被签署。A生成2n个大数,对他的2n份文件进行异或,然后全部传给B。B也做相同的事情。接下来,借助不经意传输,A向B索取其中的n个大数(对每个文件索要其中一个大数),类似地B也向A索取其中n个大数。此时,双方都能确定对方发来的文件是真实的,并且双方都不知道对方拥有了自己的每份文件的哪一半。接下来,A报出他自己的2n个大数中每一个数的第一位,然后B也报出他的每个数的第一位;然后,A报出每个数的第二位,B也报出每个数的第二位……直到双方报完所有数为止。

在上述协议的任一阶段里,A和B都不敢作假。发送虚假文件会在不经意传输完成后立即被发现,被欺骗者可以立即终止协议。虚报自己的大数也会穿帮,因为对方有其中一半的大数可用于核对,而你不知道他有哪一些大数。双方逐渐获得越来越多的大数位数,推测对方签名文件的难度同步减小,直到完全获得对方的签名文件。

我们以这个比较复杂的密码学协议来结束这篇一万多字的文章,目的是想展示一下密码学的科学性和复杂性。我们从身份验证说到了RSA算法,再谈到中间人攻击及其解决办法,最后讲了数字签名和两种特殊的签名方式——盲签名和同步签名。但这还远不到我想说的东西的一半。云风给我推荐了《应用密码学》,我当天晚上就在网上买了,第二天就抱到了文学史课上去看。从这本书里面我学到了好多好多科学的东西。还是那句话,密码学并不专注于数字层面的加密解密方法,而是专注于解决各种安全问题的方法。就像信息学中的算法一样,密码学协议中的算法也相当有趣,有些算法简洁实用,巧妙得有如神来之笔。信息传输的算法,其牛B性不亚于信息处理的算法。接下来我还想更新一些有趣好玩的密码学协议,相信大家会感兴趣的。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门