密码学基础下篇
本文首发自虫洞社区,转载请注明出处。
秘钥管理
秘钥长度与安全性
所谓安全密码,不是说不能被破解出来,而是说不能在安全期限的时间内破解出来或者破解出来的所需代价远大于得到的破解结果。
下面以秘钥强度112位的3DES破解为例计算所需的代价:
1百亿亿次/秒需要100w台100年才能破解出。新一代超级计算机:百亿亿次级运算,也需要100w台破解1年。
数量级概念:以256位秘钥为例2^256>2^(10*25)>10^(3*25)=10^75>>>3×10^23
3×10^23等于宇宙中恒星的数量,大于地球中沙子的数量。
如果是1秒钟尝试20亿亿次不同的组合:10^75/(20*10^8*10^8*86400*365)=10^58/86400/365/2>10^50
年。这么长时间远远大于太阳的寿命,6*10^9亿年。
秘钥安全长度:
秘钥管理系统
密钥管理指的是自密钥的产生到密钥的销毁,整个过程大致包括密钥的生成,存储,分配,启用,停用,控制,更新,撤销和销毁,其中密钥的分配和存储最为关键。
从上一节我们可以知道无论对称密码体制内还是非对称密码体制,其安全性实际取决于对密钥的长度和秘钥的安全保护。现代密码学把数据加密保护全部系于密钥体质上,因此密钥的安全管理是保证密码系统安全性的重要因素。密钥的管理目的是维护系统与各个实体之间的密钥关系,以抵抗各种可能的威胁,密钥管理要借助加密,认证,签名,协议和公正技术。对于对称密码而言,在通信的过程中应当遵循一次一密的加密方式。
秘钥分类
基本密钥或初始密钥:基本密钥是用户选出的由系统分配给用户的可能在较长时间内用户所专用的密钥,又称用户密钥。基本密钥和会话密钥一起启动和控制由某种算法所构成的密钥产生器,依次产生用于加密数据的密钥流。
会话密钥:会话密钥是两个通信终端用户在一次交换数据时所采用的密钥,当用其保护传输密钥时称为数据加密密钥,当用其保护文件时,称之为文件密钥。会话密钥可以由通信双方互相约定,也可由系统动态地赋予通信双方,故又称专用密钥。
密钥加密密钥:密钥加密密钥是对通信中传送的会话或文件密钥进行加密时所采用的密钥,通信网中每一个结点都有这样的一个密钥。
主机主密钥:主机主密钥是对密钥加密密钥进行加密的密钥,存储于主处理器中。
秘钥产生到销毁
秘钥的产生:应当采用足够安全的方法来产生密钥,对一个密钥的基本要求是随机性,而真正的随机性是不可再现的,任何人都不会再次产生它。关于随意数将在下一章节讲解。
秘钥分配:密钥的分配是密钥管理系统中最为复杂的问题,依据分配手段可以分为人工分发和密钥交换协议的动态分发,从密钥分配技术来看,有基于对称密码体制的密钥分配与基于公钥密码体制的密钥分配,从密钥的交换方式来说,可以分为人工密钥分发,基于中心的密钥分发和基于认证的密钥分发。
秘钥交换:本质上也是一种加密算法,一般有两种实现方式。
a) 公钥加密实现:发送方用接收方的公钥加密自己的密钥,接收方用自己的私钥解密得到发送方的密钥,逆过来亦然,从而实现密钥交换。
b) 使用DH算法:前提发送方和接受方协商使用同一个大素数P和生成数g,各自产生的随机数X和Y。发送方将g的X次方mod P产生的数值发送给接收方,接受方将g的Y次方mod P产生的数值发送给发送方,发送方再对接收的结果做X次方运算,接受方对接收的结果做Y次方运算,最终密码形成,密钥交换完成。
秘钥分配:密钥的分配技术解决的是在网络环境中需要进行安全通信的端实体之间建立共享的对称密钥问题。目前最为流行的方案是使用密钥分配中心方式KDC(Key Distribution Center)。每个节点或用户只需保管与KDC之间使用的密钥加密密钥,而KDC为每个用户保管一个互不相同的密钥加密密钥。当两个用户需要通信时,需向KDC申请,KDC将工作密钥(也称会话密钥)用这两个用户的密钥加密密钥分别进行加密后送给这两个用户。在这种方式下,用户不用保存大量的工作密钥,而且可以实现一报一密,但缺点是通信量大,而且需要有较好的鉴别功能,以识别KDC和用户。KDC方式还可以变形为电话号码本方式,适用于非对称密码体制。通过建立用户的公开密码表,在密钥的连通范围内进行散发,也可以采用目录方式进行动态查询,用户在进行保密通信前,首先产生一个工作密钥并使用对方的公开密钥加密传输,对方获悉这个工作密钥后,使用对称密码体制与其进行保密通信。
秘钥存储:有关公私钥的存储方法如下。
a) 公钥存储:公钥通常被分发或提供给其他用户,但还必需经过中央权威机构(通常是证书管理机构CA)认证以便证明它属于密钥对的拥有者
b) 私钥存储:比较高级的存储方法例如有银行的U盾,U盾内自带CPU,可以使私钥不出卡,所有的运算都是在硬件内完成,从根本上保证安全,杜绝了密钥被用户截取的可能性。
秘钥的更换与撤销:密钥是有寿命的,一旦到了有效期就必需消除原密码存储区,用随机产生的噪声重写,为了保证加密设备能够连续正常工作,也可以在新密钥产生之后,旧密钥仍然保留一段时间,以防止密钥更换期间出现不能解密的问题。
随机数
随机数是专门的随机试验的结果。例如以下的摸球案例就可以生成一个真正的随机数。
真随机数
要产生取值为 0-9 的离散型均匀分布的随机数:通常的操作方法是把 10 个完全相同的乒乓球分别标上 0,1,2,….,9 然后放在一个不透明的袋中,搅拦均匀后从中摸出一球记号码后放回袋中, 接着仍将袋中的球搅拌均匀后从袋中再摸出一球记下号码后再放回袋中,依次放下去,就得到随机序列.通常称类似这种摸球的方法产生的随机数为真正的随机数。真随机数是不可重复产生的,即:用完全相同的输入对序列发生器操作两次(至少与人所能做到的最精确的一样),得到两个不相关的随机序列
伪随机数
计算机是用某种数学方法产生的随机数,实际上是按照一定的计算方法得到的一串数,它们具有类似随机数的性质,但是它们是依照确定算法产生的, 便不可能是真正的随机数,所以称计算机产生的随机数为伪随机数.
随机数周期
用某种递推公式按一定程序产生伪随机数,一般从某一初始值起步产生伪随机数列X1,X2…..;若存在m对任意n,有Xn+m=Xn(n=1,2…..),则满足上述关系的最小m称做伪随机数的周期。周期大于2256位的随机数序列,就能大量应用。
伪随机数要求
在计算机上用数学方法产生随机数的一般要求如下:
1) 产生的随机数列要有均匀性、抽样的随机性、试验的独立性和前后的一致性。
2) 产生的随机数列要有足够长的周期,以满足模拟实际问题的要求。
3) 产生随机数的速度要快,占用的内存少。
伪随机数算法
伪随机数产生的方法叫做伪随机数发生器。伪随机数产生器中最基础的思想是均匀分布(不是唯一的思路)。一般来说,如今主流的编程语言中使用的随机数函数基本采用这种均匀分布思想,而其中最常用的算法就是”线性同余法”。
线性同余法基于如下线性同余方程组
ax + by = m
用于产生均匀型伪随机数的线性同余产生器(与上面的方程符号没有对应关系)
Xn = (an-1+b)mod(n)
其中,a为”乘数”,b为”增量”,m为”模数”,x0为”种子数”。
如果产生的是区间实在(0,1)之间的,则只需要每个数都除以m即可,即取
£n = Xn/m
物理设备产生的随机数
Linux操作系统的/dev/random和/dev/urandom是提供的随机伪设备,这两个设备的任务,是提供永不为空的随机字节数据流。为了获得真正意义上的随机数,需要一个外部的噪声源。Linux内核找到了一个完美的噪声源产生者—就是使用计算机的人。我们在使用计算机时敲击键盘的时间间隔,移动鼠标的距离与间隔,特定中断的时间间隔等等,这些对于计算机来讲都是属于非确定的和不可预测的。虽然计算机本身的行为完全由编程所控制,但人对外设硬件的操作具有很大的不确定性,而这些不确定性可以通过驱动程序中注册的中断处理例程(ISR)获取。内核根据这些非确定性的设备事件维护着一个熵池,池中的数据是完全随机的。当有新的设备事件到来,内核会估计新加入的数据的随机性,当我们从熵池中取出数据时,内核会减少熵的估计值。
熵来源:开机起时钟tick数,当前时间,用户名计算机名等MD4的哈希值,cpu运行情况,系统io情况,系统中断信息……
/dev/random和/dev/urandom这两个特殊设备都是字符型设备。dev/random的random pool依赖于系统中断,因此在系统的中断数不足时,/dev/random设备会一直封锁,尝试读取的进程就会进入等待状态,直到系统的中断数充分够用, /dev/random设备可以保证数据的随机性。/dev/urandom不依赖系统的中断,也就不会造成进程忙等待,但是数据的随机性也不高。
rand()和srand()函数
各类语言中常用生成随机数的两个函数rand()和srand()。因为rand的内部是用线性同余法做的,不是真的随机数,只不过因为其周期特别长,所以在一定范围内可以看成是随机的,rand()会返回一随机值,范围在0到RAND_MAX间,在调用此函数产生随机数前,必须利用srand()设好随机数种子,若没有设随机数种子,rand()在调用时会自动设随机数种子为1,也就是可以算出初始值,下一次生成的随机数也就可以预测了。所以rand()函数的安全性依赖srand()函数生成的初始值了。srand()函数一般是用机器中的实时时间来启动的,因为实时时间的值每时每刻都在变化,这样启动的rand()函数产生的伪随机数序列就能达到以假乱真的效果。
c 语言生成100以内随机数的代码案例:
#include <stdlib.h> #include <stdio.h> #include <time.h> main() { int i,k; srand( (unsigned)time( NULL ) ); //一般是使用时间初始化种子 for( i = 0; i < 10;i++ ) { k=rand()%100+1; //rand()%100表示取100以内的随机数,即取了随机数后再对100取余 x=rand()%(Y-X+1)+X printf( " k=%d\n", k ); } }
现在总结下,rand()函数的安全问题如下:
- 线性同余方法是可预测的(最主要原因)
- 通过时间设置随机种子
a) 不同进程在同一时间调用,生成的随机数序列是相同的
b) 同一秒内调用两次或以上rand函数,生成的随机数序列仍然是相同的
零知识证明
零知识的证明过程晦涩难懂,所以我这边将以最简洁的语言概括下零知识证明的核心思想。
什么是零知识证明
零知识证明满足三个属性:
- 如果语句为真,诚实的验证者(即,正确遵循协议的验证者)将由诚实的证明者确信这一事实。
- 如果语句为假,不排除有概率欺骗者可以说服诚实的验证者它是真的。
- 如果语句为真,证明者的目的就是向验证者证明并使验证者相信自己知道或拥有某一消息,而在证明过程中不可向验证者泄漏任何有关被证明消息的内容。
零知识证明实质上是一种涉及两方或更多方的协议,即两方或更多方完成一项任务所需采取的一系列步骤。 实际上这是一种概率游戏,对方不直接告诉你答案,而是采用另一种表达方式来让向你证明,直到你认为对方确实知道答案为止。
下面用一个数独案例简单说明零知识证明(参考自知乎,侵删)。
背景
小明,小红,小刚三个好朋友很喜欢玩数独。平日里他们三个也会互相出题给对方做。有时候他们会出一些非常变态的数独题互相挑战。他们会挑一个人在纸上画出一个9×9的格子,填上谜面(Constraint),然后交给另外两人去解。
难题
有一天,小明出了一道非常难的数独题,小红花了很长时间尝试去解开这个数独,但是怎么都解不出结果。
证明过程
小明拿出81(9×9)张空白的卡片放在桌上,在每张纸上写上1-9中的一个数字,他让小红转过身闭上眼,然后把这81张卡片小心翼翼地按照解的排列放在桌上,代表谜底的卡片,数字面朝下放在桌上;代表谜面的卡片,则数字面朝上放在桌上。
小红很困惑,然后告诉小明她决定选择按照行的方法来验证,小明接着把每一行的9张卡片收起来单独放到一个麻布袋里。所有卡片都被收完放在了9个麻布袋里。小明接着摇了摇每个麻布袋,把里面的卡片顺序都打散。最后把这9个麻布袋交给小红。
重复过程:
小红还是不服气。觉得小明仍然有可能在骗她,所以要求小明再把卡片复原,按照原来的方法,重新选。这样接连试了几次,小红每次都选一个不一样的试验方法。试了好多次都是一样的结果。小红这下不得不承认,小明要么运气非常非常好,每次都能押中小红会选择哪种试验方式,要么就是他确实知道题解,(或者小明会读心术能预先知道小红会选什么试验方式)。小红很失望,这么多次试验下来,她还是不知道真正的题解,她只知道每次小明放置卡片的排列里很大几率每行每列每个九宫格确实都是没有重复的1-9,这就说明很大几率这题是有解的,而且小明很大几率确实知道这题的解。
总结
-
零知识证明的本质就是在不揭晓我所知道或拥有的某样东西的前提下,向别人证明我有很大几率(这点很重要,零知识证明说到底是一个概率上的证明)确实知道或拥有这个东西。
-
小明和小红之间最开始那种互动式的证明方法暗指的是交互式零知识证明(interactive zero-knowledge proof)。交互式零知识证明需要验证方(小红)在证明方(小明)放好答案(commitment)后,不断的发送随机试验。如果验证和证明双方事先串通好,那么他们就可以在不知道真实答案的情况下开挂(simulate/forge a proof)。
-
非交互式的证明则不需要这种互动。但是会额外需要一些机器或者程序,并且需要一串试验序列,这个试验序列不能被任何人知道。有了这么一个程序和试验序列,证明机就能自动算出一个证明,并且能防止任何一方作假。
zcash中的零知识证明
Zcash是一种去中心化、开源的加密互联网货币。与比特币相比,其更注重于隐私,以及对交易透明的可控性。具体体现为:公有区块链加密了交易记录中的发送人、接收人、交易量;用户可裁量选择是否向其他人提供查看密钥,仅拥有此密钥的人才能看到交易的内容。
Zcash代币ZEC的匿名交易过程
假设alice要给bob转1个ZEC,流程大概如下:
- alice用自己创私钥对这个ZEC的交易单子进行签名,证明自己有对这个ZEC的使用权;
- 此时这个ZEC的交易单子上多了一串随机字符串,该字符串具有唯一性,也就是单号;
- 系统为bob也创建1个ZEC的交易单子,且该单子也有一个单号,也是唯一的;
要确认bob的单子上有一个ZEC的使用权时,需要将alice的单子上的ZEC销毁,但是ZCash不是直接销毁的,而是将alice的交易单号放到一个作废列表中,这时bob才能拥有这一个ZEC的使用权。 - 在确认bob资产所有权的时候,需要读取这笔资产的单号是否在作废列表中,如果不在,bob才有这个ZEC的使用权。在这个交易过程中,bob是不需要看到alice对这个ZEC的使用权的,但是交易却达成了。因为alice只需要向矿工们提供自己的单号,矿工把这个单号放入作废列表中即可。
同态加密
什么是同态加密
同态加密也是个很有趣的密码学算法,下面也要案例通俗易懂解释下什么是同态加密(依然参考自知乎,侵删)
一般的加密方案关注的都是数据存储安全。即,我要给其他人发个加密的东西,或者要在计算机或者其他服务器上存一个东西,我要对数据进行加密后在发送或者存储。没有密钥的用户,不可能从加密结果中得到有关原始数据的任何信息。只有拥有密钥的用户才能够正确解密,得到原始的内容。我们注意到,这个过程中用户是不能对加密结果做任何操作的,只能进行存储、传输。对加密结果做任何操作,都将会导致错误的解密,甚至解密失败。同态加密方案最有趣的地方在于,其关注的是数据处理安全。同态加密提供了一种对加密数据进行处理的功能。也就是说,其他人可以对加密数据进行处理,但是处理过程不会泄露任何原始内容。同时,拥有密钥的用户对处理过的数据进行解密后,得到的正好是处理后的结果。我们举个实际生活中的例子。有个叫Alice的用户买到了一大块金子,她想让工人把这块金子打造成一个项链。但是工人在打造的过程中有可能会偷金子啊,毕竟就是一克金子也值很多钱的说… 因此能不能有一种方法,让工人可以对金块进行加工,但是不能得到任何金子?当然有办法啦,Alice可以这么做:Alice将金子锁在一个密闭的盒子里面,这个盒子安装了一个手套。工人可以带着这个手套,对盒子内部的金子进行处理。但是盒子是锁着的,所以工人不仅拿不到金块,连处理过程中掉下的任何金子都拿不到。加工完成后。Alice拿回这个盒子,把锁打开,就得到了金子。这个盒子的样子大概是这样的:
这里面的对应关系是:
- 盒子:加密算法
- 盒子上的锁:用户密钥
- 将金块放在盒子里面并且用锁锁上:将数据用同态加密方案进行加密
- 加工:应用同态特性,在无法取得数据的条件下直接对加密结果进行处理
- 开锁:对结果进行解密,直接得到处理后的结果
应用场景
近几年云计算越来越火热,而同态加密简直是云计算的完美搭档。我们考虑以下的情景:一个用户想要处理一个数据,但是他的计算机计算能力较弱。这个用户可以使用云计算的概念,让云来帮助他进行处理而得到结果。但是如果直接将数据交给云,无法保证安全性啊!于是,他可以使用同态加密,然后让云来对加密数据进行直接处理,并将处理结果返回给他。
- 这样一来:用户向云服务商付款,得到了处理的结果;
- 云服务商挣到了费用,并在不知道用户数据的前提下正确处理了数据;
这方法简直完美啊!但是,这么好的特性肯定会带来一些缺点。同态加密现在最需要解决的问题在于:效率。效率一词包含两个方面,一个是加密数据的处理速度,一个是这个加密方案的数据存储量。我们可以直观地想一想这个问题:
- 工人戴着手套加工金子,肯定没有直接加工来得快。也就是说,隔着手套处理,精准度会变差(现有构造会有误差传递问题),加工的时间也会变得更长(密文的操作花费更长的时间),工人需要隔着操作,因此也需要更专业(会正确调用算法)。
- 金子放在盒子里面,为了操作,总得做一个稍微大一点的盒子吧,要不然手操作不开啊(存储空间问题)。里面也要放各种工具吧,什么电钻啦,锉刀啦,也需要空间吧?
上面说了同态加密的基本概念,让大家有基本的了解,事实上同态加密目前依然是业内的难题,哪家云计算公司如果能攻克这个难题,那这家公司目前肯定是一家独大的。有兴趣的同学可以尝试着去研究更深层的算法原理,号称入门都要3个月(加油,有志之士的青年们)。