【小木箱成长营】密码学系列教程:
Web3.0 · 基础层技术 ·密码学在移动端应用与实践
Hello,我是小木箱,欢迎来到小木箱成长营密码学系列教程,今天将分享 Web3.0 · 基础层技术 · SCQA 模型趣谈密码学。
SCQA 模型趣谈密码学主要分为四部分,第一部分是加解密算法,第二部分是 Hash 算法,第三部分是数据编解码,最后一部分是总结与展望。
其中,加解密算法主要是分为四部分,第一部分是对称加密,第二部分是 DH 交换算法,第三部分是非对称加密,第四部分是混合加密。
如果学完小木箱密码学系列教程,那么任何人完成高性能日志组件、网络接口组件和网络优化工作,甚至迎接 Web3.0 都会有巨大帮助。
SCQA 模型是麦肯锡芭芭拉·明托在《金字塔原理》中提出的“结构化表达”模板工具,常用于方案、文案、广告、演讲、讲故事、写作等。SCQA 是 Situation、Complication、Question 和 Answer4 个英文单词简称,分别是
如果你想把难以理解的密码学知识表达清楚,那么建议采用万能表达模板—SCQA。
加密技术⽆处不在但是容易忽略。更多的了解加密技术能做什么,不能做什么,对我们建⽴更加完善的信息安全意识是⾮常重要的。
加密技术的演进其实是⼀场没有硝烟的战争,在加密⽅和破解⽅惊⼼动魄的攻守战中不断完善。
接下来,我们⾸先利用⼀个⼩故事,然后通过业务背景、困境冲突、分析问题和解决问题的 SCQA 模型⽅式学习加密技术演进,最后达到活学活用的目的。
故事的主⼈公是小木箱和粉丝。他们通过微信公众平台进行传话,小木箱给粉丝发了⼀个消息:微信公众号小木箱成长营。
但是,消息被潜在黑粉窃听了!
于是黑粉⽴刻拦截了他们的信息,并把小木箱和粉丝交流关系链中断了。
当下面临的问题有三个。第一个窃听问题,小木箱和粉丝交流过程中,内容是明文传输的,内容被黑粉窃听,导致信息泄露风险。
第二个是密钥配送问题,通过对称加密算法,小木箱和粉丝好像解决了信息传递的问题,但是,实际操作过程中,发现了⼀个⼤问题:如何共享密钥?这就是密钥配送问题。
第三个问题是中间⼈攻击问题,小木箱和粉丝即使解决了密钥配送问题,不一定能确保密钥通信安全。
黑粉通过⾃⼰的聪明才智,想到了⼀个劫持+篡改的⽅案,再次攻破了 DH 密钥交换和⾮对称加密,即中间⼈攻击。
如何解决窃听问题呢?
针对窃听问题,为了避免被黑粉窃听,小木箱和粉丝想到了⼀个好办法:首先小木箱和粉丝共享了⼀个密钥,然后小木箱先把消息加密成密⽂,最后再发送给粉丝,粉丝拿到密⽂之后⽤密钥解密成明⽂。这就是对称加密。
如何解决密钥配送问题呢?
针对密钥配送问题,密钥配送问题的解决⽅案有很多,有两种解决密钥配送问题⽅案:DH 交换算法和⾮对称加密算法。
如何解决中间人攻击问题呢?
中间人攻击本质即篡改和伪装,解决方案有单向散列、消息验证码、数字签名和数字证书四种方式。
针对窃听问题,首先我们来说说对称加密,加密方式主要讲解四种加解密方式: 第一种是 DES 加密,第二种是 3DES 加密,第三种是 AES 加密,第四种是 ChaCha 加密。
下面小木箱将从简介、历史、现状、明文块长和密钥长和加解密流程等多方面维度讲解四种加解密算法。
加密处理方式有对称加密和非对称加密两种。
其中,对称加密指的是通信双⽅同时掌握⼀个密钥,加密解密都是由⼀个密钥完成的(即加密密钥等于解密密钥,加解密密钥可以相互推倒出来)。
对称加密双⽅通信前共同拟定⼀个密钥,不对第三⽅公开,解密时使用加密过程的完全逆过程配合密钥来进行解密。
其中,对称加密指的是加密和解密的密钥不同的加密算法。首先我们讲解一下第一种对称加密算法 DES 加密。
关于 DES 加密,DES,全称Data Encryption Standard,数据加密标准。
DES 是 1975 年 IBM 推出,并随后在国际上⼴泛使⽤开来。
随着计算机技术的发展,在 20 世纪末的时候,DES 算法已经可以被暴⼒破解。在 1999 年的 DES III ⽐赛中,只⽤了 22 ⼩时多就完成了破解。所以现在不要再使⽤ DES 算法。
DES 算法的明⽂块⻓度是 64 位。密钥总⻓度是 64 位,其中每隔 7 位会有⼀个⽐特的错误校验,所以有效密钥⻓度为 56 位。
DES 的加解密流程如下:
DES 解密和加密的结构是⼀样的(利⽤传统的换位、异或、置换等加密⽅法),具体可以参考下面的图解。
https://github.com/strich1991/DES/blob/master/src/DES.java
然后,我们讲解一下第二种对称加密算法 3DES 加密。
3DES 算法是将 DES 算法重复 3 次的⼀种算法。
3DES 是 DES 算法被攻破之后,被开发出来临时替代 DES 算法的。1999 年,NIST(National Institute of Standards and Technology,美国国家标准技术研究所)将 3DES 指定为过渡加密标准。
3DES 算法由于其处理效率不⾼,并且作为过渡标准,所以除了⼀些要向下兼容原有的 DES 算法的场景,现在很少被使⽤。
3DES 算法的明⽂块⻓度为 64 位,有效密钥⻓度为 56*3 位。
解密。
由于 DES 密码长度容易被暴力破解,增加 DES 的密钥长度可反破解,因此针对每个数据块进行三次 DES 加密,即 3DES。
组合分组方法设计的分组 3DES 加密算法,3DES 密钥相比 DES 长度更长,安全性更高。
https://github.com/songxiaoliang/EncryptionLib/blob/master/app/src/main/java/com/android/song/encryptionlib/TDESUtils.java
接着,我们讲解一下第三种对称加密算法 AES 加密。
AES 算法,Advanced Encryption Standard,⾼级加密标准,是取代 DES 算法的⼀种新的对称加密标准。
AES 是 NIST 公开竞选得到的⼀种加密标准。可以说是先有了 AES 的名字,然后竞选之后才确定的算法。最终被选中的算法叫做 Rijndael 算法。不过严格来讲,AES 是 Rijndael 的⼀种特殊情况。
AES 现在已经是最流⾏的对称密钥加密算法,⼴泛应⽤在各个领域。也是我们现在推荐使⽤的对称加密算法。
AES 的块⻓ 128 位,密钥⻓度⽀持 128 位、192 位、256 位三种⻓度。(这⾥注意,Rijndael 算法的块⻓和密钥⻓度,是在 128-256 之间的 32 倍数即可。
AES 加密过程分为两个过程,⼀是密钥扩展过程、⼀是明⽂加密过程。
A: 密钥扩展过程:
密钥扩展过程是基于⽤户密钥,通过 Rijndael 密钥编排算法⽣成多组 128 ⽐特⻓的轮密钥,根据密钥⻓度,分别⽣成 10 组、12 组和 14 组轮密钥。
B: 明文加密过程:
AES 加密与 DES 加密⼀样,是由多轮组成。根据密钥⻓度,分别是 10 轮、12 轮、14 轮。
每⼀轮由以下四种运算组合⽽成:
第⼀轮只有 AddRoundKey 运算。
最后⼀轮是 SubBytes->ShiftRows->AddRoundKey 运算。
中间轮都是 SubBytes->ShiftRows->MixColums->AddRoundKey 运算。
AES 解密过程就是 AES 加密逆过程,小木箱不做过多讲解。
https://github.com/ForTheDevelopers/JavaSecurity/tree/main/src/main/java/cc/devclub/ftd/aes
最后,我们讲解一下第四种对称加密算法 ChaCha20 加密
ChaCha20 算法是 Google 设计的一种新型流密码算法,ChaCha 命名来源于印度某漫画"Chacha Chaudhary",20 表示 ChaCha20 算法有 20 轮加密计算。
ChaCha20 带 Poly1305 消息认证码的 ChaCha20(即 ChaCha20-Poly1305),作为 OpenSSL 中 RC4 的替代品,用以完成互联网的安全通信。
Google 最初实现了 HTTPS(TLS/SSL)流量在 Chrome 浏览器(Android 手机版)与 Google 网站之间的通信。
2008 年,丹尼尔·J·伯恩斯坦发布“ChaCha”密码家族,目的是增加每一轮扩散以提升性能。
2013 年,Google 提出 ChaCha20-Poly1305 算法,11 月份在 Chrome31 版本启用了 ChaCha20-Poly1305 算法。
2014 年,所有的 IOS 设备和 Android 设备上 Chrome 启用了 ChaCha20-Poly1305 算法。
firfox 密码库用的是 NSS,NSS3.23 版本支持 ChaCha20/Poly1305。
ChaCha20 算法因为其轻量级、链路加密、密钥安全、IOT 安全特征,所以更适用于 ARM 设备。
ChaCha20-poly1305 比 AES-128-GCM 性能高,但因为 ChaCha20 IOT 安全特征,因此手机设备更适合 ChaCha20-poly1305 算法,电脑上更适合 AES-128-GCM 算法。
ChaCha20 矩阵输入一个 256 位的密钥长度、64 位随机数、64 位计数器值以及 4×32 位的常数,常数填充在 32 位整型数组中作为初始矩阵。
了解 ChaCha20 加密之前我们首先要熟悉三个新概念,第一个是 ChaCha20 的初始矩阵,第二个是初始矩阵置换,第三个是轮函数。
首先,聊聊 ChaCha20 的初始矩阵,ChaCha20 的初始矩阵是指输入一个 256 位的密钥、64 位随机数、64 位计数器值以及 4×32 位的常数,它们均填充在 32 位整型数组中作为初始矩阵。
其次,聊聊初始矩阵置换,初始矩阵置换是指 ChaCha20 算法有 20 轮运算,其中奇数轮次与偶数轮次在执行轮函数前需要分别经过行置换和列置换,故 20 轮运算可以看作 10 次迭代,每次迭代先做行置换再做列置换。
接着,聊聊轮函数,轮函数是指在矩阵每次完成置换后,都需要执行一次轮函数 QUARTERROUND,该函数输入为 4 个 32 位串,即 4 个数组中的元素,输出同样也为 4 个 32 位串,这样执行完轮函数后除了数据以外,矩阵结构未发生任何变化。
最后,聊聊 ChaCha20 加密过程,ChaCha20 加密其实是将需要加密的信息(明文)与密钥流按位异或即得到密文,明文大于 512 位时,对轮函数进行 20 轮周期(10 轮行周期+10 轮列周期),初始矩阵已经变成了一个新的 4×4 矩阵,此时将新矩阵与初始矩阵矢量相加,得到的矩阵再拆分倒序序列化处理后即得到一个 512 位的密钥比特流。
由于计数器是 32 位,理论上可以生成 2^512bit(256GB)的密钥流,所以一般长度的信息加密完全足够,这就是 ChaCha20 加密过程。
聊完 ChaCha20 加密过程,我们再聊聊 ChaCha20 解密过程,接收方使用发送方传输过来的初始密钥、随机数以及协商好的常数和依次递增的计数器值按照 ChaCha20 的密钥流生成规则产生与加密相同的密钥流,按位与密文异或即可得到明文,这就是 ChaCha20 解密过程。
https://github.com/kitsook/ChaCha20/tree/master/ChaCha20/src/net/clarenceho/util
说完四种加解密算法,小木箱说说对称加密算法的破解思路。
对称加密算法破解比较简单,首先拿到⼀组或多组原文-密文对,然后设法找到⼀个密钥,这个密钥可以将这些原文-密文对中的原文加密为密文,以及将密文解密为原⽂的组合,即成功破解。
对称加密如何反破解呢?
⼀种优秀的对称加密算法的标准是让破解者找不到⽐穷举法(暴力破解法)更有效地破解⼿段,并且穷举法的破解时间⾜够长(例如数千年)。
对称加密算法的优点很明显,效率高,性能高。缺点是有密钥泄露风险,只能在安全网络上传输密钥,一旦密钥泄露则加密通信失败。
对称加密算法的作用在于加密通信,防⽌信息在不安全网络上被截获后,信息被人读取或篡改。
从上⾯的算法,我们可以看到,输⼊的明⽂都是固定⼤⼩的块。那么对于⼀个任意⻓度的明⽂,要如何加密呢?
答案就是分组。下面简单介绍 CBC、CTR 和 CFB 三种分组模式。
CBC 模式的全称是 Cipher Block Chaining 模式,密文分组链接模式。名字中也展示 CBC 的实质,像链条一样相互链接在一起。
CBC 加密“链条”起始于一个初始化向量 IV,这个初始化向量 IV 是一个随机的比特序列。
CBC 加密必须是从“链条”头开始加密,所以中间任何一个分组都无法单独生成密文。
CBC 解密的时候,如果解密“链条”中间有一环“断”了,会出现什么问题呢?
CBC 解密过程中如果有一环出现了问题,硬盘等问题出现了,但是整个链条长度没变,如上图的情况,那么一个坏的环会影响 2 个分组的解密。
如果链条长度也发生变化了,或者某个分组中的 1 个比特位在网络传输过程中缺失了。那么影响的解密分组可能就不止 2 个分组了。
因为会引起整个链条上重新分组,这样一来导致原文无法解密(因为位数少于分组要求,解密的时候不会填充末尾分组不足的比特位)。
这一点算是 CBC 链式的一个“小缺点” 。一个比特位的缺失会导致整个密文无法解析。
由于 CBC 是链式的,所以攻击者可以考虑从“头”开始攻击,即攻击初始化向量 IV,例如把初始化向量中的某些比特位进行 0,1 反转。
这样的话,消息接收者在解密消息的时候,明文 1 分组会受到初始化向量的影响,出现错误。
还有一种攻击办法是直接攻击密文,例如密文分组中的某个分组 n 被改变了,那么就会影响到明文分组 n+1 的解密。
CTR 模式的全称是 CounTeR 模式(计数器模式)。CTR 模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。
注意上图中解密过程,中间是加密而不是解密!因为需要保证明文和密文之间异或的对象不变。不变才能异或两次还原明文。
计数器每次都会生成不同的 nonce 来作为计数器的初始值。这样保证每次的值都不同。
这种方法就是用分组密码来模拟生成随机的比特序列。
在被攻击方面 CTR 和 OFB 是差不多的。CTR 模式的密文分组中有一个比特被反转了,则解密后明文分组中仅有与之对应的比特会被反转,这个错误不会被放大。
不过 CTR 模式比 OFB 模式相比有一个更好的优点在于,如果 OFB 模式某次密钥流的一个分组进行加密后生成的结果和前一次一样,那么这个分组之后的每次密钥流都不变了。
CTR 模式就不会存在这一问题。
CFB 模式的全称是 Cipher FeedBack 模式(密文反馈模式)。在 CFB 模式中,前一个密文分组会被送到密码算法的输入端。反馈即返回输入端。
注意上图中解密过程,中间是加密而不是解密!因为需要保证明文和密文之间异或的对象不变。不变才能异或两次还原明文。
CFB 整个过程很像一次性密码本,如果把明文分组前的加密部分全部都看成一个随机比特序列,那么就和一次性密码本的流程一样了。由算法生成的比特序列称为密钥流。
在 CFB 模式中,密码算法就相当于用来生成密钥流的伪随机数生成器,初始化向量相当于是伪随机数生成器的种子。
也因为它是伪随机数,所以 CFB 不具备一次性密码本绝对无法被破译的性质的。
所以说,CFB 是一种使用分组密码来实现流密码的方式之一。
可以对 CFB 实施重放攻击(replay attack) 。
例如攻击者可以把上一次会话中的部分分组截取出来放进下次会话随机位置。
这样消息接收者在拿到密文后进行解密,会导致其中一个分组出现错误(上图中是明文分组 2 解密失败),这个时候无法判断是通信出错还是被人攻击所致。(想要判断需要用到消息认证码才行,而此处只是单纯的 CFB)
在分组的前提下,如果明⽂的⻓度不能被块⻓整除的情况,就需要填充。主要有三种填充⽅式:
最后,简单总结一下六种分组模式和三种填充方式
填充方式 | 特点 |
---|---|
NoPadding | 不填充,很少⽤。需要使⽤者保证明⽂⻓度是块⻓的整数倍 |
ZeroPadding | 缺字节填 0,不缺字节填⼀个块的 0 |
PKCS7Padding | 缺⼏个字节就每字节都填⼏。不缺填⼀个块的块⻓ |
关于对称加密小木箱讲解到这里了,对称加密小木箱讲解了四种加密方式、对称加密破解思路、对称加密反破解、对称加密优缺点、对称加密作用和对称加密分组和填充方式。
其中对称加密的四种加密方式分为 DES 加密、3DES 加密、AES 加密和 ChaCha20 加密等。
如果想了解更多密码学知识内容,那么小木箱建议大家通过《图解密码技术》一书进行深度学习。下面我们将进入 DH 交换算法的讲解。
小木箱在加解密算法开篇说了,对称加密算法存在的困境冲突有密钥配送问题,解决密钥配送问题的解决⽅案有很多,下⾯介绍两种常⻅的解决密钥配送问题的⽅案:DH交换算法和⾮对称加密算法。
解决密钥配送问题第一种⽅案是DH交换算法,DH 交换算法是由 Diffie 和 Hellman 在 1976 年发明的。
DH 算法作为⾮对称密码技术的⿐祖,⼀⽅⾯将加密算法的基本⼯具从代换和置换扩展到数学函数,另⼀⽅⾯⾮对称的⽅式对密钥配送、数字认证等都有极其重要的作⽤。DH 算法可以说是密码学史上最重要的⼀次⾰命。
⽬前 DH 算法被⼴泛应⽤在安全领域,⽐如 Https 中的 TLS 握⼿协议等。
DH 交换算法求模公式$$ ( a x m o d p ) y m o d p = a x y m o d p (a^x mod p)^y mod p = a^{xy} mod p (axmod p)y mod p = axy mod p , D H 交换算法离散对数难题 ,DH交换算法离散对数难题 ,DH交换算法离散对数难题 b = a x m o d p b = a^x mod p b = ax mod p ,对于给定的素数 a 和 p ,已知 x 求 b 很容易,但是已知 b 求 x 很难,难度大概是 ,对于给定的素数a和p,已知x求b很容易,但是已知b求x很难,难度大概是 ,对于给定的素数a和p,已知x求b很容易,但是已知b求x很难,难度大概是 O ( 2 n ) O(2^n) O(2n)$$,其中 n 是 p 的位数。
小木箱选定公共参数 a 和 p
小木箱选择一个私密的参数 x,并且计算出\$\$$$Y_a $$\$$
小木箱将 a、p、$$ Y a Y_a Ya$$ 一起发送给粉丝
粉丝选定一个私密的参数 y,并且计算出\$\$$$Y_b $$\$$
粉丝将$$ Y b Y_b Yb$$ 发送给小木箱
小木箱算出$$ K = Y b y m o d p K = Y_b^y mod p K = Yby mod p ,粉丝算出 ,粉丝算出 ,粉丝算出 K = Y a y m o d p K = Y_a^y mod p K = Yay mod p$$,K 就是二者共享的秘钥
公开参数 | a、p、 \$\$$$Y_a $$$$ 和 $$$$Y_b $$\$$ |
---|---|
私有参数 | x 和 y |
https://github.com/kelvinylon/DHTest/blob/master/src/main/java/com/kelvin/dhtest/DHTest.java
说完解决密钥配送问题第一种⽅案是DH 交换算法,我们再说说解决密钥配送问题第二种⽅案非对称加密。
⾮对称加密就是加密和解密的密钥不同的加密算法。
非对称加密手段常见的手段有 RSA、DSA、ECC 和 ECDHE。
首先,我们来说一下 RSA 算法
1976 年,两位美国计算机学家 Whitfield Diffie 和 Martin Hellman,提出了⼀种崭新构思, 可以在不直接传递密钥的情况下,完成解密。
这被称为"Diffie-Hellman 密钥交换算法"。这个算法启发了其他科学家。⼈们认识到,加密和解密可以使⽤不同的规则,只要这两种规则之间存在某种对应关系即可,这样就避免了直接传递密钥。
这种新的加密模式被称为"⾮对称加密算法"。
1977 年,三位数学家 Rivest、Shamir 和 Adleman 在 DH 算法的启发下,设计了⼀种⾮对称加密算法,命名为 RSA 算法。
RSA 算法是使⽤最为⼴泛的⾮对称加密算法,可以说,有计算机⽹络的地⽅,就有 RSA 算法。
涉及到互质关系、欧拉函数、欧拉定理、模反元素等数学知识。具体可以参考阮⼀峰的 RSA 算法原理
密文 = $$ 明 文 E m o d N 明文^E mod N 明文E mod N$$,公钥为(E,N)
明文 = $$ 密 文 D m o d N 密文^D mod N 密文D mod N$$,公钥为(D,N)
https://github.com/ForTheDevelopers/JavaSecurity/tree/main/src/main/java/cc/devclub/ftd/rsa
然后,我们来说一下 DSA 数字签名
DSA 数字签名可以理解为是单向加密的升级,不仅校验数据完整性,还校验发送者身份,同时还由于使用了非对称的密钥来保证密钥的安全,所以相比消息 Hash 算法更安全。
DSA 是美国 NIST 作为 DSS(DigitalSignature Standard)数字签名的标准。
DSA 只是一种算法,和 RSA 不同之处在于它不能用作加密和解密,也不能进行密钥交换,只用于签名,DSA 比 RSA 要快很多。
DSA 是一种更高级的验证方式,DSA 是一种公开密钥算法,不能用来加密数据,一般用于数字签名和认证。
DSA 不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。
在 DSA 数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性,包括数据的完整性以及数据发送者的身份。
如果数据和签名不匹配则认为验证失败!DSA 数字签名的作用就是校验数据在传输过程中不被修改。
https://github.com/ForTheDevelopers/JavaSecurity/tree/main/src/main/java/cc/devclub/ftd/dsa
其次,我们来说一下 ECC 算法
ECC 是 Elliptic Curves Cryptography 的缩写,意为椭圆曲线密码编码学。和 RSA 算法一样,ECC 算法也属于公开密钥算法。
1985 年, Koblitz 和 Miller 提出 ECC 算法,ECC 算法的数学基础是利用椭圆曲线上的有理点构成 Abel 加法群上椭圆离散对数的计算困难性
因为在安全性、加解密性能、网络消耗方面有较大优势,ECC 加密算法大有取代 RSA 成为下一代主流加密算法的趋势。如今 ECC 应用范围很广,在 TLS、区块链(比特币、以太坊等等)、SM2 国密算法、证书、银行政府机构等许多方面都有大量应用。
ECC 加密算法的密钥长度很短(256 位),意味着占用更少的存储空间,更低的 CPU 开销和占用更少的带宽。
目前手机已经越来越普及,随着越来越多的用户使用移动设备来完成各种网上活动,ECC 加密算法为移动互联网安全提供更好的客户体验。
ECC 加密算法提供更强的保护,比目前的其他加密算法能更好的防止攻击,使你的网站和基础设施比用传统的加密方法更安全,为移动互联网安全提供更好的保障。
ECC 加密算法。密钥短,所以相应的计算量、消耗的内存和带宽也就少。可以用较短的密钥长度来提供更好的安全
例如,256 位的 ECC 密钥加密强度等同于 3072 位 RSA 密钥的水平(目前普通使用的 RSA 密钥长度是 2048 位)。其结果是你以更低的计算能力代价得到了更高的安全性。
经国外有关权威机构测试,在 Apache 和 IIS 服务器采用 ECC 算法,Web 服务器响应时间比 RSA 快十几倍。
ECC 可帮助保护您的基础设施的投资,提供更高的安全性,并快速处理爆炸增长的移动设备的安全连接。
ECC 的密钥长度增加速度比其他的加密方法都慢(一般按 128 位增长,而 RSA 则是倍数增长,如:1024 – 2048 - 4096),将延长您现有硬件的使用寿命,让您的投资带来更大的回报。
ECC 算法的可以用较少的计算能力提供比 RSA 加密算法更高的安全强度,有效地解决了“提高安全强度必须增加密钥长度”的工程实现问题。
描述一条 Fp 上的椭圆曲线,常用到六个参量:T=(p,a,b,n,x,y)。
(p 、a 、b) 用来确定一条椭圆曲线;
p 为素数域内点的个数;
a 和 b 是其内的两个大数;
x,y 为 G 基点的坐标,也是两个大数;
n 为点 G 基点的阶;
以上六个量就可以描述一条椭圆曲线,有时候我们还会用到 h(椭圆曲线上所有点的个数 p 与 n 相除的整数部分)。
M + rQ - k(rP) = M + r(kP) - k(rP) = M
https://github.com/ForTheDevelopers/JavaSecurity/tree/main/src/main/java/cc/devclub/ftd/ecc
最后,我们来说一下 ECDHE 算法
HTTPS 常用的密钥交换算法有两种,分别是 RSA 和 ECDHE 算法。
ECDHE
全称是椭圆曲线迪菲-赫尔曼秘钥交换(Elliptic Curve Diffie–Hellman key Exchange),主要是用来在一个不安全的通道中建立起安全的共有加密资料,一般来说交换的都是私钥
,这个密钥一般作为“对称加密”的密钥而被双方在后续数据传输中使用。
ECDHE 密钥协商算法是 DH 算法演进过来的,学好 ECDHE 密钥协商算法前必须学好 DH 算法,上文我们说了 DH 算法,DH 算法是非对称加密算法, 用于密钥交换,而 ECDHE 密钥协商算法的核心数学思想是离散对数。
当多个参与方共享同一个链路的时候,联盟链各参与方想要在同一个链路上实现通性的隔离,使用ECDHE
算法既可以实现 TCP 链接的复用,避免频繁建立大量链接,又可以保证同一链接间的通信隔离。
Alice 想要用私钥签名一个数据,Bob 想要使用 Alice 的公钥验证这个签名;只有 Alice 能够进行计算签名然后得到签名,每个人都能验证签名值。
首先,Alice 和 Bob 拥有相同的椭圆曲线参数,算法被签名称之为 ECDSA,是 DSA 算法的一个变体。
然后,ECDSA 签名算法的输入是数据的 hash 值,而不是数据的本身,hash 算法可自选且 hash 值会被截断。
最后,Alice 使用私钥 da 对 z 进行签名,生成二元组(r, s)。Bob 使用 Alice 的公钥对(r, s)和 Z 进行验证。
Bob 的验证过程为:
需要注意的是:
当我们使用 ECDSA 进行签名的时候,k 的保密性非常重要。如果我们对所有的签名操作都用一样的 k 或者我们的随机数生成器存在可预测性,一个攻击者可能会猜出私钥。
首先,双方约定使用ECDHE
秘钥交换算法,这个时候双方也知道了ECDHE
算法里的一个大素数P
,这个P
可以看做是一个算法中的常量。
P
的位数决定了攻击者破解的难度
还有一个整数g
用来辅助整个秘钥交换,g
不用很大,一般是 2 或者 5,双方知道g
和p
之后就开始了ECDH
E 交换秘钥的过程了。
Alice 知道了共用参数p
和g
,生成私有整数a
作为私钥,公钥算法一般是公钥加密,私钥解密,公钥给对方来加密数据,拿到密文后私钥解密查看内容正确性,这个时候 Alice 直接通过线路告诉 Bob 自己的私钥a
显然既不合理也是一件风险很大的事。
这个时候 Alice 需要利用p
,g
,a
通过公式g^a mod p = A
生成 A 作为公钥传递。
Bob 通过链路收到 Alice 发来的p
,g
,A
,知道了 Alice 的公钥A
。这个时候 Bob 也生成自己的私钥b
,然后通过公式g^b mod p = B
生成自己公钥B
。
在发送公钥B
前,Bob 通过A^b mod p = K
生成 K 作为公共秘钥,但是并不发送给 Alice,只通过链路发送B
。
Alice 收到 Bob 发来的公钥B
以后,同样通过B^a mod p = K
生成公共秘钥 K,这样 Alice 和 Bob 就通过不传递私钥a
和b
完成了对公共秘钥 K 的协商。
https://github.com/akhawaja/java-ecdhe-sample/tree/master/src/main/java/com/amirkhawaja/ecdhe
我们还是从小木箱和粉丝来看⼀下这个过程。
非对称加密的优点是什么?
可以在不安全⽹络上传输密钥,可以解决“密钥交换”的问题。网站秘密保管私钥,在网上任意分发公钥,你想要登录网站只要用公钥加密就行了,密文只能由私钥持有者才能解密。
为什么不直接使用非对称加密?
非对称加密由于使用了复杂了数学原理,因此计算相当复杂,如果完全使用⾮对称加密来加密通信内容,会严重影响⽹络通信的性能。非对称加密的缺点是计算复杂,因此性能相⽐对称加密差很多。
和对称加密不同之处在于,⾮对称加密的公钥很容易获得,因此制造原文-密文对是没有困难的事。
⾮对称加密的关键只在于,如何找到⼀个正确的私钥,可以解密所有经过公钥加密过的密⽂文。找到这样的私钥即为成功破解。
⾮对称加密的自身特性,怎样通过公钥来推断出私钥通常是一种思路(例如 RSA),但往最佳手段依然是穷举法。
只是和对称加密破解的区别在于,对称加密破解是不断尝试自己的新密钥是否可以将⾃⼰拿到的原文-密文对进行加密和解密,而非对称加密时不断尝试自己的新私钥是否和公钥互相可解。
⾮对称加密算法优秀的标准同样在于,让破解者找不到⽐穷举法更有效的破解手段,并且穷举法的破解时间足够长。
说完非对称加密,我们再聊聊混合加密
混合加密是结合对称加密和非对称加密各自优点的一种加密方式。
混合加密具体的实现思路是首先使用对称加密算法对数据进行加密,然后使用非对称加密算法对 对称加密的密钥进行非对称加密,最后再把加密后的密钥和加密后的数据发送给接收方。
在对数据安全要求较高的场景或传输敏感数据时,我们就可以考虑利用前面的混合加密方案对提交到服务端的数据进行混合加密,当服务端接收到对应的加密数据时,再使用对应的解密算法对加密的数据进行解密,从而进一步进行数据处理。
但是如果服务端也要返回敏感数据时,应该怎么办呢?可以参考下面的混合加密技术方案
① 生成一个唯一的 reqId(请求 ID),用于标识当前请求;
② 分别生成一个随机的 AES Key 和 AES IV(采用 AES CBC 模式);
③ 采用 RSA 非对称加密算法,分别对 AES Key 和 AES IV 进行 RSA 非对称加密;
④ 采用随机生成的 AES Key 和 AES IV 对敏感数据进行 AES 对称加密;
⑤ 把 reqId 作为 key,AES Key 和 AES IV 组成的对象作为 value 保存到 Map 或 {} 对象中;
⑥ 把 reqId、加密后的 AES Key、AES IV 和加密后的数据保存到对象中提交到服务端;
⑦ 当服务端接收到数据后,对接收的数据进行解密,然后使用客户端传过来的解密后的 AES Key 和 AES IV 对响应数据进行 AES 对称加密;
⑧ 服务端在完成数据加密后,把 reqId 和加密后的数据包装成响应对象,返回给客户端;
⑨ 当客户端成功接收服务端的响应后,先获取 reqId,进而从保存 AES Key 和 IV 的 Map 获取该 reqId 对应的 AES 加密信息;
⑩ 客户端使用当前 reqId 对应的加密信息,对服务端返回的数据进行解密,当完成解密之后,从 Map 或 {} 对象中删除已有记录。
我们来对上述流程做个简单分析,首先 AES 加密信息都是随机生成的且根据每个请求独立地保存到内存中,把 AES 加密信息中的 Key 和 IV 提交到服务端的时候都会使用 RSA 非对称加密算法进行加密。
在服务端返回数据的时候,会使用当前请求对应的 AES 加密信息对返回的结果进行加密,同时返回当前请求对应的 reqId(请求 ID)。即服务端不需要再生成新的 AES 加密信息,来对响应数据进行加密,这样就不需要在响应对象中传递 AES 加密信息。
https://github.com/Ccapton/android-aes-rsa/tree/master/aes_rsa/src/main/java/com/ccapton/aesrsa
说完混合加密,我们说说 Hash 算法,Hash 算法又叫摘要算法
Hash 算法本质数据结构是散列函数、Hash 函数,Hash 算法用来实现完整性,能够为数据生成独一无二的“指纹”,因为不可逆性, Hash 算法不是加密算法,不能用于加密(因为无法通过 Hash 反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令
Hash 算法的作用有三个,第一个是防止直接看到明文,比如 TLS 安全通信防中间人攻击,第二个是防抵赖(数字签名),第三个是大文件比较。下面我们着重讲解防中间人攻击流程
小木箱和粉丝解决了密钥配送问题,这下可以秘密通信了吧?没那么容易,黑粉通过⾃⼰的聪明才智,想到了⼀个劫持+篡改的⽅案,再次攻破了 DH 密钥交换和⾮对称加密。这个就是⼤名⿍⿍的中间⼈攻击。
中间⼈攻击,反应了加密世界中的两⼤问题:篡改和伪装。这个难题如何解决呢?
⾸先介绍我们的第⼀个武器:单向散列,简称Hash
单向散列函数也叫 Hash 函数、摘要函数、杂凑函数等,是单向的把任意⻓的输⼊消息串变化成固定⻓的⼀种函数。
单向散列函数可以把任意数据转换成指定大小范围(通常很小,例如 256 字节以内)的数据。
第⼀个是输⼊不定⻓,输出定⻓。
抗碰撞性。
单向性,⽆法通过输出反向计算出输⼊。
计算快速。
MD5 算法。已经被攻破,不再推荐使⽤。
CRC32 算法。在数据压缩领域被作为完整性校验
SHA1 算法。已经被攻破,不再推荐使⽤。
SHA2 算法。包括常⻅的 SHA256、SHA512 算法等。
SHA3 算法。是 NIST 选拔的算法,推荐使⽤。
MAC 算法。 MD5 和 SHA 结晶,安全程度高。 商业银行中 POS 终端加密常用算法。
Hash 相当于从数据中提出摘要信息,因此最主要⽤途是数字指纹。
Hash 是编码吗?
不是,Hash 是单向过程,往往是不可逆的,⽆法进行逆向恢复操作,因此 Hash 不属于编码。
Hash 是加密吗?
不是,Hash 是单向过程,⽆法进行逆向回复操作,因此 Hash 不属于加密。(记住,MD5 不是加密)
Hash 实际用途有四个,第一个是唯一性验证,第二个是数据完整性验证,第三个是快速查找,最后一个是隐私保护
唯⼀性验证
数据完整性验证
快速查找
隐私保护
说完了单向散列,我们会发现,篡改和伪装的问题都没有完全解决。这个难题如何优化呢?
在单向散列算法中引⼊⼀个共享秘钥,这样散列+加密的结果就是我们的消息验证码。
由于消息验证码只有拥有共享密钥的⼈才能算出。这样就解决了伪装和篡改的问题。
如果初始消息是很长的句子,那么初始消息被篡改后意思会变得很奇怪,所以接收者有可能会发现初始消息是被篡改过的。
但是,如果初始消息就是商品编号等无法被人们直接理解的内容,那么解密后接收者便很难判断初始消息是否被篡改。由于密码本身无法告诉人们消息是否被篡改,所以就需要使用消息认证码来检测初始消息是否真伪。也就是我们说的 MAC
HMAC 是 Keyed-Hashing for Message Authentication 的缩写。
HMAC 的 MAC 算法是 hash 算法,HMAC 可以是 MD5、SHA-1 或 SHA-256。
他们分别被称为 HMAC-MD5、HMAC-SHA1 或 HMAC-SHA256。
消息验证码在⼀定程度上解决了篡改和伪装的问题。那么如何进一步优化呢?
消息验证码是在单向散列的基础上引⼊了⼀个共享秘钥。
而数字签名则是在单向散列的基础上引⼊了⼀对公私钥
数据签名场景是如果想要将数据更小,那么首先,对原数据 hash,然后,hash 签名附加在原数据后⾯,最后,作为签名即可。流程图参考如下:
对⽐消息验证码,数字签名可以额外解决“⽆法向第三⽅证明” 和 “否认” 两个问题。
数字签名技术是利用算法(一般是非对称算法)通过 Hash(仅个人所有)加密,生成数字签名,与原文一起传送给接收者。
接收者只有用发送者的公钥才能解密被加密的信息,然后对内容执行 Hash 运算得到 Hash 值,与解密得到的数字签名 Hash 值比对。
如果比对结果一致,则说明收到的信息是完整的,在传输过程中没有被篡修改的,否则信息一定被修改过
数字签名也是⾮对称加密算法的应⽤,对⽅⽤公钥加密,我⽤私钥解密,就是⾮对称加密在消息传递上的应⽤。我⽤私钥加密,对⽅⽤公钥解密,就是数字签名。
数字签名还是要有⼀对公私钥,所以还是存在中间⼈攻击的问题的。那么我们该如何治理这个问题呢?
我们引⼊了数字证书来解决中间⼈攻击的问题。
数字证书,涉及到⼀个第三⽅的公正机构,Certificate Authority,也就是 CA。公钥的分发需要使用数字证书,必须由 CA 的信任链来验证,否则就是不可信的;
服务器向 CA 中心申请证书,CA 负责核实服务器的真实性,并在证书里内置服务器域名证书使用者信息客户浏览器或操作系统里内置有权威 CA 的公钥信息(即 CA 公钥不需要在网络中传输)
客户请求服务时,服务器把自己申请的数字证书和内容的数字签名一同发给客户
客户收到信息后在本地 CA 列表里查找 CA 公钥对数字证书解密,拿到真正的签名公钥,再用该公钥去解密数字签名,拿到 HASH 值去对比完整性
检验基本信息
校验 CA 机构
解密证书
比对 hash 值
最后,对比结果一致,则证明服务器发来的证书合法,没有被冒充,浏览器可以读取证书中的公钥,用于后续加密
好了,利⽤上⾯这些武器,小木箱和粉丝终于可以联系和信任了。
Hash 算法的风险在于碰撞,伪造证书攻击, 作为信任链的源头 CA 有时也会不可信,解决办法有 CRL、OCSP,还有终止信任,可能两个不同的数据通过某个 Hash 算法得到了相同的摘要
说完 Hash 算法,我们再聊聊数据编解码,根据 5W2H 分析法,我们将从编解码价值、编解码原因、编解码时机和编解码形式和使用五个维度讲解数据编解码
编码是信息从一种形式转换为另一种形式的过程。不同字符对应的二进制数的规则。
解码是编码的逆过程,解码将存储在计算机的二进制转换为可以看到的字符。
数据编解码是可逆的,数据编解码核心价值有两个。第一个是保证数据的准确性,一定程度上防止数据传输过程中被任意改造。第二个是为了方便数据存储和压缩。
未压缩的数据量非常大,存储困难,同时也不便于在网络中传输。
因此,数据编解码的最主要作用是将非标准格式的数据转为某种协议的标准格式,使这种协议可以传输、处理这种数据。
当我们在存储和传输数据的过程中,如果不改变数据空间结构加密数据,或不隐藏数据空间结构解密,那么我们可以考虑对数据进行编解码。数据加解密和数据编解码本质区别是数据加解密是对数据隐藏和保护,数据编解码是方便存储和传输数据。
数据编解码有四种形式,第一种形式是 Base64 编解码,第二种形式是 Hex 编解码,第三种形式是 URL 编解码,第四种形式是压缩与解压。
首先我们说说第一种形式 Base64 编解码
Base64 是将⼆进制数据转换成由 64 个字符组成的字符串的编码算法
Base64 将⼆进制数据扩充了储存和传输途径(例如可以把数据保存到⽂本文件、可以通过聊天对话框或短信形式发送⼆进制数据、可以在 URL 中加入简单的二进制数据)。
普通的字符串在经过 Base64 编码后的结果会变得⾁眼不可读,因此可以适用于一定条件下的防偷窥(较少用)。
因为⾃身的原理(6 位变 8 位),因此每次 Base64 编码之后,数据都会增大约 1/3,所以会影响存储和传输性能。
Base64 加密图⽚传输更安全和高效?
Base64 并不是加密, 另外,Base64 会导致数据增大 1/3,降低⽹网络性能,增⼤用户流量开销,是画蛇添⾜的手段。
Base64 对图⽚进行编码用处在于有时需要使用文本形式来传输图片。除此之外,完全没必要使用 Base64 对图片进行额外处理。
然后我们说说第二种形式 Hex 编解码,
Hex 编码(十六进制编码)就是使用数字 0 到 9 加上字母 A 到 F 一共 16 个字符表示的一种编码解码方式,其中 A 到 F 相当于十进制的 10 到 15,总计 16 个,这些称作十六进制数字。
常见的编解码方式如下:
# abc这3个英文字符会被echo编码为3个字节,然后被xxd编码为6个hex字符
$ echo -n abc|xxd -ps
616263
# 解码hex数据
$ echo 616263|xxd -ps -r
abc
Hex 编码最常用于二进制文件查看时展示的编码,如 Hex Fiend 就可以支持查看二进制文件。
在 XML、JSON 等文本中包含不可见数据(二进制数据)时使用
/**
* 将字节数组转换为字符串
* 一个字节会形成两个字符,最终长度是原始数据的2倍
* @param data
* @return
*/
public static String toHex(byte[] data){
String ret = null;
//TODO 将字节数组转换为字符串
if (data != null && data.length>0) {
StringBuilder sb = new StringBuilder();
for (byte b: data){
//分别获取高四位,低四位的内容,将两个数值,转为字符15
int h = (b>>4)&0x0f;
int l = b&0x0f;
char ch ,cl;
if( h > 9 ){
ch = (char) ('A'+(h-10));
}else{ //0--9
ch = (char) ('0'+h);
}
if(l>9){
cl = (char) ('A'+(l-10));
}
sb.append(ch).append(cl);
}
ret = sb.toString();
}
return ret;
}
public static byte[] fromHex(String str) {
byte[] ret = null;
//TODO 将Hex编码的字符串,还原为 原始的字节数组
if (str != null) {
int len = str.length();
if (len > 0 && len % 2 == 0) {
char[] chs = str.toCharArray();
ret = new byte[len / 2];
for (int i = 0, j = 0; i < len; i += 2, j++) {
char ch = chs[i]; char cl = chs[i + 1];
int ih = 0, il = 0, v = 0;
if (ch >= 'A' && ch <= 'F') {
ih = 10 + (ch - 'A');
} else if (ch >= 'a' && ch <= 'f') {
ih = 10 + (ch - 'a');
} else if (ch >= '0' && ch <= '9') {
ih = ch - '0';
}
if (cl >= 'A' && cl <= 'F') {
il = 10 + (cl - 'A');
} else if (cl >= 'a' && cl <= 'f') {
il = 10 + (cl - 'a');
} else if (cl >= '0' && cl <= '9') {
il = cl - '0';
}
v = ((ih & 0x0f) << 4) | (il & 0x0f);
//赋值
ret[j] = (byte) v;
} }
return ret;
}
接着我们说说第三种形式 URL 编解码。
URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称 URL
Url 的编码格式采用的是 ASCII 码,ASCII 的用途就是使用安全的字符(没有特殊用途或者特殊意义的可打印字符)去表示那些不安全的字符。
对于非 ASCII 字符,需要使用 ASCII 字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码。
如"中文"使用 UTF-8 字符集得到的字节为 0xE4 0xB8 0xAD 0xE6 0x96 0x87,经过 Url 编码之后得到"%E4%B8%AD%E6%96%87"。
如果某个字节对应着 ASCII 字符集中的某个非保留字符,则此字节无需使用百分号表示。
对于 Unicode 字符,RFC 文档建议使用 utf-8 对其进行编码得到相应的字节,然后对每个字节执行百分号编码。
其次我们说说第四种压缩与解压。
压缩是指将数据使用更具有存储优势的编码算法进⾏编码。而解压是指将压缩数据解码还原成原来的形式,以方便使用。
压缩与解压⽬的是减小数据占用的存储空间。
问题 1: 压缩是编码吗?解压是解密过程吗?
是,所谓编码,即把数据从一种形式转换为另一种形式。压缩过程属于编码过程,解压缩过程属于解码过程。
问题 2: 常见的压缩算法有哪些?
常见的压缩算法有五种,分别是 Gzip、Deflate、Brotli、JPEG 和 MP3
Gzip
Deflate
Brotli
Jpeg
MP3
问题 3: 什么是图⽚、音频与视频编解码?
图⽚、音频和视频编解码是指将图像、音频、视频数据通过编码来转换成存档形式(编码),以及从存档形式转换回来(解码)。
问题 4: 图⽚、音频与视频编解码有什么用?
图⽚、音频与视频编解码目的是存储和压缩媒体数据(⼤多数媒体编码算法会压缩数据,但不是全部)。
问题 5: 什么是图⽚压缩粗暴算法?
图⽚压缩粗暴算法常见案例是将⼀张纯⽩(⽩⾊的 16 进制数值为 0xffffff)的 64x64 不透明像素图⽚,原数据格式⼤致为:
width:64;height:64;ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.......f
fffff
使用某种算法后:
width:64;height:64;ffffff:[0,0]-[63,63]
这就是图⽚压缩粗暴算法,通过字符集测量方式减少图片体积。
加解密算法快结束了,同学可能会将序列化和编解码混淆,下面就简单比较一下序列化和编解码简单的差异。
序列化是指把数据对象(⼀般是内存中的,例如 JVM 中的对象)转换成字节序列的过程。
对象在程序内存里的存放形式是散乱的(存放在不同的内存区域、并且由引用进行连接),通过序列化可以把内存中的对象转换成⼀个字节序列,从而使用 byte[] 等形式进行本地存储或网络传输,在需要的时候重新组装(反序列化)来使⽤。
序列化目的是让内存中的对象可以被储存和传输。
序列化不是编码,编码是把数据由一种数据格式转换成另一种数据格式,而序列化是把数据由内存中的对象(而不是某种具体的格式)转换成字节序列。
这一节,主要分为四部分,第一部分是加解密算法,第二部分是 Hash 算法, 第三部分是数据编解码,最后一部分是总结与展望。其中,加解密算法主要是分为四部分,第一部分是对称加密,第二部分是 DH 交换算法,第三部分是非对称加密,第四部分是混合加密。
熟悉密码学对于移动端开发者自定义数据格式、字符编解码和高性能日志库实现尤为重要,如果想成为一名不被时代遗弃的 Android 开发者,那么深层次的了解加解密,并在合适的场景使用加解密,不仅能全方面构建全链路技术栈,而且为即将到来的 Web3.0 技术风口奠定了理论基础。
下一节,小木箱将带大家从安卓签名机制、TLS 握手协议、支付中台建设和日志安全等维度讲解密码学在移动端应用与实践。
如果大家对小木箱的文章感兴趣,那么欢迎关注小木箱微信公众号小木箱成长营。小木箱成长营,一个专注移动端基础技术的互联网成长社区。