前言
日常工作需要支持客户的一些问题,但是又拿不到客户的
APP,所以有时候需要反编译找出一些关键信息,自己构建一个APK排查,或者替换已有工程的某些信息。趁此之际,再次回顾了下签名原理。例如APK打包耗时,就可以尝试实现新增文件以打补丁dex的形式,然后计算签名SHA-1补充到MANIFEST.MF文件中,还要写到CERT.CSF中,gank验签步骤。V2就更近一步,签名信息插入到了ZIP文件信息块和中央目录间, 新增文件签名写入实现方式更难了,具体内部文件签名还是类似。V3和V2类似,V2的签名块信息存放在ID = 0x7109871a的数据块中,而V3的签名信息存放在ID = 0xf05368c0的数据块中。当然本篇文章V1/V2/V3签名不是关键,笔者也没有实现过。主要还是日常开发和支持客户问题会涉及到数据安全,因此针对一些项目常用算法进行了了解。加上好久没写文章了,就把半年多前的笔记丢出来充充数吧。
参考资料
目录
一、AES算法
全称:高级加密标准,Advanced Encryption Standard,是一种最常见的对称加密算法。据说微信小程序加密传输就是用的这个加密算法。
对称加密算法还有DES、3DES,前者被证实不安全,提示使用3DES替代,3DES也是基于DES,对一块数据用三个不同的密钥进行加密,安全更高。但是你见平时使用的多吗?不多,为什么不多,因为有AES了呀。
(DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。
这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行“异或”运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用 16 个循环,使用异或,置换,代换,移位操作四种基本运算)
具体流程
说白了,秘钥一样K = K' 。这种加密方式速度很快,适合经常发送数据的场合。但是秘钥不能明文互相传输吧?一般会利用非对称加密算法来操作得出数据通信加密的秘钥。这个后面章节会介绍非对称的一些算法。
既然知道了AES的加密流程,那内部是如何加密的呢?
加密原理
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文数据。
在AES的规范中,分组长度只能是128位,也就是每个分组为16字节(如果最后一个明文组不够16字节,那么AES会提供选择填充模式),但是密钥成都可以使用128位、192位或者256位。
我们平时说的AES-128,就是指密钥的长度是128位,加密轮数是10轮(AES192 是12轮,AES256是14轮),可以理解为一个明文分组会被加密10次,那么每轮加密又做了哪些操作呢?
**右侧的密钥是啥?**我们称为扩展密钥,AES源码中用长度 4 *4 *(10+1) 字节的数组W来存储所有轮的密钥,其中W{0-15}是等同于原始密钥的值,用于为初始轮加密用。后续每一个元素W[i]都是由W[i-4] 和 W[i-1]计算得来。
加密10次中,第0轮只用到了加轮密钥(初始轮算不算加密轮次,你说了算),而第10次比第1-9次,少了一个列混淆加密方式。所有这些加密,你都可以简单理解为映射,a字符通过算法映射成了b。
- 字节替代
16个字节的明文块在每一个处理步骤中都被排列成 4 * 4的二维数组,每个元素栈了一个字节,16位。例如一个数组元素是4A(两位16进制),那么去S盒里找对应S[4[10]的 元素替换。
- 行移位
前面说到16字节明文块会被排列成4 * 4 的二维数组矩阵。这里就是对数组元素进行位偏移,规则是第一行不变,第二行循环左移一个字节,也就是数组元素左移一个,第一个放到数组这一行末尾,第三行循环左移2个字节,第四行循环左移3个字节。
- 列混淆
和一个修补矩阵,进行矩阵相乘。
- 加轮密钥
128bit密钥会被排列成4*4的矩阵,让上一个步骤得出的二维数组矩阵的每一个字节与密钥对应位置异或一次,输出的内容就是密文了。
这是加密流程,解密流程轮次顺序相反。
你肯定会问,这是AES-128,那和AES-192与AES-256有什么区别,AES-128它的扩展密钥W{0-15}是原始密钥,那AES-192呢?
AES填充模式
填充模式有点多,如果不确定数据长度是不是16bit的整数倍,那可能一般会选用ZEROBYTEPADDING,个人喜欢用的是将数据md5成128bit或者利用SHA256,然后配合NOPADDING 使用。
AES工作模式
工作模式主要体现在明文块和明文块之间的关联上。下面介绍AES的七种工作模式。
- ECB模式
Electronic Code Book Mode 电子密码本
每一个明文块的加密都是完全独立的,互不干涉。优点就是简单,有利于并行计算。但是相同的明文块经过加密很有可能会变成相同的密文块,安全性比较低。
- CBC模式
Cipher Block Chaining Mode 密码分组链接
引入了一个初始向量IV的概念,目的是防止同样的明文块始终加密成同样的密文块。
那他是怎么做的呢?
首先IV作为初始化变量,参与第一个明文块的异或,然后再去加密。后续每一个明文块和它前一个明文块所加密出来的密文块异或,然后再进入加密过程。
这样就能保证相同明文块加密出来的密文块不是一样的。安全性相比ECB更高,但是每一个明文块都依赖于前一个密文块,无法并行计算,速度肯定是比ECB慢,引入IV,也增加了计算的复杂度。加解密所需要的IV需要双方共同知晓。
- CFB模式
Cipher Feedback Mode 密文反馈
利用初始向量IV,加密后得到IV0,IV0和明文块异或得到密文块。然后利用前一个密文块加密得到IV1,IV1和明文块异或得到下一个密文块,以此类推。
在ECB模式和CBC模式下,明文分组都是通过密码算法进行加密的,而在CFB模式下,明文分组没有通过密码算法来直接加密,明文分组和密文之间仅仅有一个异或XOR。
图中的IV0、IV1我们称为密钥流,CFB模式也可以看作是一种使用分组密码来实现流密码的加密方式。
对CFB模式可以实施重放攻击,因为密码块我是可以替换的,解密也是用前一个密文块加密得到IV进行异或。例如,我给你发送的数据内部是三个密文块,第三者将消息中的后两个密文块保存下来了。然后我又给你发送了三个密文块,第三者,替换了后连个密文块,你收到的消息中,第一个密文块可以解密成功,第二个解密失败,第三个解密成功(前一个密文块加密得到IV,IV异或密文块得到明文块)。至于第二个为什么解密失败?根本无法判断。
- OFB模式
Output Feedback Mode 输出反馈
这个模式和CFB模式差不多,主要是IV生成规则不一样了,之前是密文块加密后得到密钥流IV,现在是通过前一个IV加密后得到后一个密钥流IV
- CTR 模式
Counter Mode 计数器模式
这种加密方式简单快速,安全可靠,可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次,因为在CTR模式中, 有一个自增的算子,这个算子用于确定计数器中累加部分的大小,一般取块的大小一般,初始的计数器count是长度固定的任意一个随机字节序列。
- PCBC模式
Propagatin Ciper Block Chaining Mode 填充密码分组链接模式
这种模式,看着和CBC是不是很像?都利用到了明文异或规则。
我们知道CBC、CFB、OFB 、PCBC四种模式可以解决 ECB 模式中相同明文生成相同密文的缺陷,CTR 又可以在此基础上提供多分组并行加密特性,但是它们都不能提供密文消息完整性校验功能,所有就有了 下面的GCM 模式。
- GCM模式
Galois Counter Mode
首先我们需要了解一下,密文消息完整性校验是如何工作的,学习过Https的工作原理,应该了解
上面的mac algo算法是两端约定好的,包括shared_key。mac算法主要是来进行消息认证的。GCM模式引入了一个GMAC概念,全称Galois Message Authentication Code,就是利用伽罗华域乘法来计算消息的MAC值。
开始进入主题
GCM模式其实是在CTR模式上增加了GMAC的特性,来进行消息的完整性校验
二、DH算法
RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)属于非对称算法。使用最广泛的当时是RSA算法,为啥这里先介绍DH算法,因为我想记ECDH算法笔记。
DH算法是啥?
Diffie-Hellman密钥交换算法是1976年由这两人发明的算法,它可以在不安全的网络上,通过交换公开的信息协商出共享密钥,通过该共享密钥来建立安全通讯。全称并没有直接交换密钥,而是通过数据计算得出。
原理是 : g^a mod P = A,根据g、a、p求A不难,但是求解a很难,特别是当p很大的时候(p可以为1024、2048等无穷尽也)。
DH协商流程
首先双方需要约定密钥交换算法,知道使用的最大素数 P,还有一个正数g来辅助整个密钥交换,一般g为2和5就够了,上图中的私钥a是一个位于1~P-2之间的整数,计算出A后,将A发送给你对象,总不能直接将私钥a发送给你对象,万一分手了呢?不就泄密了。你对象同时也会生成自己的私钥b,计算出自己的公钥B,同时也计算出后续通信要是用的密钥K,将B发送给你,你收到后,计算出通信的密钥K。
你可能会问?密钥协商过程中,我对象哪里知道是不是我给他发送的公钥啊!
对,没错,不知道。这里就引发了一个新问题,没有验证公钥发送者的身份,可能会被中间人攻击。这块一般会用到数字证书签名,就例如我们TLS双向认证过程中,会用证书公钥签名,私钥验签来确定身份。
DH算法分类
基于密钥
- 静态DH算法
通信双方中,有一方的私有秘钥是固定的,另一方是临时生成的,(C-S结构中,一般S是固定的)。这种私钥是固定的,那肯定会在某一刻被破解,一旦被破解,整个链路就不安全了,之前通讯数据也能被破解。
- DHE算法
双方不固定私钥,私钥生成都采用临时的方式生成。(E == Ephemeral临时的)
基于计算方式
- 模指数DH算法
指数运算+取模运算,看之前介绍的DH协商流程,就是这种计算模式,性能也算是可观的。
- 椭圆曲线DH算法
这种算法就是我们常见的ECDH算法,ECDH全称是椭圆曲线迪菲-赫尔曼秘钥交换(Elliptic Curve Diffie–Hellman key Exchange)。ECDH建立的前提是,给定椭圆曲线上一个点P,一个正数K,求 Q = KP简单,但是通过Q、P求K难。
三、RSA算法
前奏
质数又称为素数,指大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数。
如果两个或两个以上的整数的最大公约数是1,那么它们为互质。
-
两个数互质的判断方法
-
不同的质数
-
相邻的两个奇数
-
相邻的两个自然数
-
一个素数,另一个不是它的倍数
至于公约数,利用欧几里得算法,平时称为辗转相除法,可以求出。
还有一些欧拉函数啥的,早就忘光了,你暂时只需要记住这些都和质数、因式分解有关
- 欧拉函数
φ(n)=n * (1-1/p1) * (1-1/p2) * (1-1/p3) ... * (1-1/pn)
其中p1、p2.... pn都是质数。
密钥生成
随机选择两个不相等的大质数 p、q,计算出 p和 q的乘积n, n的二进制长度是密钥长度,一般为1024
计算出欧拉函数 φ(n)的值(小于或等于n的正整数中与n互质的数目)
再计算出随机整数 e , 条件 1 < e < φ(n), 且 e与 φ(n) 互质。
利用公式 e * d = 1(mod φ(n)) 计算出 d。
(e,n) 封装成公钥, (d,n)封装成私钥。
加密
明文M,密文C
公式 C = M ^ e mod n
内部处理细节太复杂,如果密钥长度是1024,还需要将M切成多块,还涉及Padding的计算
解密
密文C,明文M
公式 M = C ^d mod n
签名算法
明文M ,签名 S
公式 S = M ^d mod n
验证签名
M' = S ^ d mod n
M' == M 则验签成功
算法安全
上述证明,大家自己搜索吧,涉及高等代数知识。
我们知道(e,n)是公钥,私钥是(d, n),公钥可以传输,私钥不行,所以d是否泄漏,决定算法是否安全。
d的计算和 φ(n) 有关, 欧拉函数通式为 φ(n) =n * (1-1/p1) * (1-1/p2) * (1-1/p3) ... * (1-1/pn),如果暴力破解,复杂度指数增长,当一个数的因子很大时,采用强力方式得到具体的因子式很困难的,这就是RSA体系理论的核心。