我眼中的最有代表性的公匙密码算法 ECC,RSA,DSA。
[第一部分: 理论篇]
0. 前言
我眼中的最有代表性的公匙密码算法 ECC,RSA,DSA。 其中前两种已经被国内众多
Cracker界前辈高人详细的解释并实现,使我等初学者获益匪浅,其中尤为具代表性
的两篇如:
ECC加密算法入门介绍 [作者:zmworm/CCG]
RSA与大数运算 [作者:afanty]
更是使我受益良多。
唯独DSA算法 由于各种原因鲜有相关文章 国内的共享软件也很少有使用这种加密算
法进行注册验证的, 其实DSA算法有许多它自己独特的地方 它比ECC算法更加便于理
解和实现, 比RSA产生密匙的速度快很多,且安全性与RSA不相上下 但是DSA的一个
重要特点是两个素数(P,Q)公开,这样,当使用别人的p和q时,即使不知道私钥,你
也能确认它们是否是随机产生的,还是作了手脚。RSA算法却作不到。
我用了一个礼拜时间四处搜集资料 其中包括应用密码学一书中DSA算法章节 DSATool
中的说明文件 看学学院中DSA算法的简单介绍 等诸多权威信息, 经过自己的整理,
翻译,理解写出本文,文中少有复杂的数论知识,重在应用和实践,旨在起到一个抛
砖引玉的作用 希望这套算法被更多人接受,理解,并得到广泛的应用。不足之处在所
难免 希望各位高人能不吝赐教加以指点 :)
// 为免译文不准确 部分段落引用英文原文 英文好的朋友可以自行参考
1. General stuff / 常规资料
In 1991 the Digital Signature Algorithm(DSA) has becometheDigital
Signature Standard(DSS). DSA is a public-key signature schemethatuses a
pair of transformations to generate and verify a digitalvaluecalled
signature.
DSA has been developed by the US National Security Agency(NSA)andcan
-not- be used for encryption or key distribution. DSA issomevariant of
the ElGamal signature algorithm and, as defined in thestandard,uses the
Secure Hash Algorithm(SHA/SHA-1) as one-way hash n.
// Digital SignatureAlgorithm(DSA)是Schnorr和ElGamal签名算法的变种,被美国
NIST(美国国家标准局)作为数字签名标准(DigitalSignature Standard)。同样属于
公匙密码体系,并使用Secure Hash Algorithm(SHA/SHA-1)作为中间单向计算算法
2. meters / 参数
P = A prime number in range 512 to 1024 bits which must beamultiple of 64
P = 一个范围在512至1024之间的素数 且必须为64的倍数
Q = A 160 bit prime factor of P-1
Q = P - 1的160bits的素因子
G = H^((P-1)/Q) mod P. H is any number < P-1 suchthatH^((P-1)/Q) mod P > 1
G = h^((p-1)/q) mod P,H 必须 < p - 1, h^((p-1)/q) mod p>1
X = A number < Q
X = 小于Q的一个数
Y = G^X mod P
meters P, Q, G and Y are public where Y is the public key. Xisthe
private key and must be kept secret! To obtain X from Y oneneedsto solve
the Discrete Logarithm Problem which is virtuallyimpossiblefor
-properly- generated meters of reasonable size.
// 以上参数其中P, Q, G 以及 Y 为公匙, X为私匙必须保密!任何第三方用户想要从
Y解密成X 都必须解决整数有限域离散对数难题
3. Signing a message (M) / 签名部分
To sign M, carry through the following steps:
// 若需要对M进行数字签名 则需要进行下列运算:
- Generate a -random- number K < Q. NEVER use same K twiceormore to sign
other messages!
- 产生一个随机数 K (K < Q),,永远不要将同样的K用于进行其他的签名运算!
- Compute R = (G^K mod P) mod Q
- 计算 R = (G^K mod P) mod Q
- Compute S = (K^-1*(SHA(M) + X*R)) mod Q
- 计算 S = (K^-1*(SHA(M) + X*R)) mod Q
The number pair (R,S) is the signature of M.
// R以及S 为这次对M的数字签名结果
4. Verifying a signature (R,S) of M /验证部分
- Compute W = S^-1 mod Q
- 计算 W = S^-1 mod Q
- Compute U1 = (SHA(M) * W) mod Q
- 计算 U1 = (SHA(M) * W) mod Q
- Compute U2 = (R*W) mod Q
- 计算 U2 = (R*W) mod Q
- Compute V = ((G^U1 * Y^U2) mod P) mod Q
- 计算 V = ((G^U1 * Y^U2) mod P) mod Q
If V R the signature is verified.
// 若v = r,则认为签名有效。
5. DSA的安全性
DSA主要依赖于整数有限域离散对数难题。素数 P 必须足够大,且p-1至少包含一个大
素数因子以抵抗Pohlig & Hellman算法的攻击。M 一般都应采用信息的HASH值(官方推
荐为SHA算法)。DSA的安全性主要依赖于p和g,若选取不当则签名容易伪造,应保证g对
于p-1的大素数因子不可约。 个人观点DSA的安全性要次于ECC 与RSA不相上下。但是有
一点, 就是DSA算法的验证过程 R,S 是以明文形式出现的, 这点很容易被利用,在
第二篇中各位会体会到这一点。
6. Cracker眼中的DSA
DSA算法鲜有被用于国产共享软件的注册验证部分 即使在国外的共享软件中也远不如
RSA,Blowfish等算法应用广泛。
DSA算法在破解时 关键的参数就是X 根据 Y = G^X mod P 只要知道 P,G,Y,Q 且能
分解出 X 就可以伪造R,S写出KeyGen了。
[第二部分: 签名篇]
介绍完理论之后 我们来实践了解一下:
目标: pDriLl"s Crypto KeygenMe #4
主要工具: C32Asm(感谢pll621/CCG 送给我Keyfile); Ollydbg1.09d;
BigInt Calc Pro 1.2(感谢Stkman/CCG 送给我Keyfile)
等等....
先脱壳:用CoolDumper找到OEP是407952 然后使用 ImportREC1.6f 修复IAT表。
用Ollydbg载入程序 下断点 BPX GetDigItem 断下后走到不远处:
004012A6 . LEA EBX,DWORD PTR SS:[EBP-104]
004012AC . MOV DWORD PTR DS:[EBX],EAX
004012AE . POP EBX
004012AF . CMP DWORD PTR SS:[EBP-1D0],0 // 该不会连名字都不输入吧
004012B6 . JNZ SHORT Dumped4W.004012DE
004012B8 . PUSH 0
004012BA . LEA EAX,DWORD PTR SS:[EBP-3C]
004012C0 . PUSH EAX
004012C1 . LEA EAX,DWORD PTR SS:[EBP-20]
004012C7 . PUSH EAX
004012C8 . LEA EAX,DWORD PTR SS:[EBP+8]
004012CE . MOV EAX,DWORD PTR DS:[EAX]
004012D0 . PUSH EAX
004012D1 . LEA EAX,DWORD PTR DS:[4017BD]
004012D7 . PUSH EAX
004012D8 .-JMP DWORD PTR DS:[40E9A4] user32.MessageBoxA
004012DE LEA EAX,DWORD PTR SS:[EBP-22C]
004012E4 . PUSH EAX
004012E5 . CALL Dumped4W.00401900 // 过了这个CALL, DB EAX 就看到:
0012F878 01 23 45 67 89 AB CD EF FE DC BA 98 76 54 3210#Eg壂惋簶vT2
See... MD5的常量, 不知道为什么 我一见到MD5就兴奋 对这个算法很有感情 :)
MD5特点 - 单向不可逆 所以在这里不多说了 主要介绍DSA。
004012EA . ADD ESP,4
004012ED . MOV CL,BYTE PTR DS:[40E9C6] // 取Key的第6位
004012F3 . MOV BYTE PTR DS:[40E940],CL
004012F9 . MOV DL,BYTE PTR DS:[40E9CD] // 取Key的第14位
004012FF . MOV BYTE PTR DS:[40E941],DL
00401305 . MOV AL,BYTE PTR DS:[40E9D4] // 取Key的第21位
//所以 Key 最少为21位, 重新输入注册信息, Name:LingDiKey:90901
0040130A . MOV BYTE PTR DS:[40E942],AL
0040130F . MOV DWORD PTR SS:[EBP-48],Dumped4W.0040E>;ASCII"12FHCF-YEAH!!"
00401316 . MOV ECX,DWORD PTR SS:[EBP-104]
0040131C . PUSH ECX
0040131D . PUSH Dumped4W.0040D1C0 ASCII "%d"
00401322 . MOV EDX,DWORD PTR SS:[EBP-48]
00401325 . PUSH EDX
00401326 . CALL Dumped4W.00407900 // 将Key的位数连接到刚才取出的3个Key值后.
0040132B . ADD ESP,0C
0040132E . MOV DWORD PTR SS:[EBP-48],Dumped4W.0040E>;ASCII"FHCF-YEAH!!"
00401335 . PUSH Dumped4W.0040D1B4 ASCII "EGBE-YEAH!!"
0040133A . PUSH Dumped4W.0040D1B0 ASCII "%s"
0040133F . MOV EAX,DWORD PTR SS:[EBP-48]
00401342 . PUSH EAX
00401343 . CALL Dumped4W.00407900 // 连接 EGBE-YEAH!!
// DB 40e940看到如下结果
0040E940 37 34 31 32 37 45 47 42 45 2D 59 45 41 48 212174127EGBE-YEAH!!
0040E950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000................
00401348 . ADD ESP,0C
0040134B . MOV CL,BYTE PTR DS:[40E945]
00401351 . ADD CL,1
00401354 . MOV BYTE PTR DS:[40E945],CL
0040135A . MOV DL,BYTE PTR DS:[40E946]
00401360 . ADD DL,1
00401363 . MOV BYTE PTR DS:[40E946],DL
00401369 . MOV AL,BYTE PTR DS:[40E947]
0040136E . ADD AL,1
00401370 . MOV BYTE PTR DS:[40E947],AL
00401375 . MOV CL,BYTE PTR DS:[40E948]
0040137B . ADD CL,1
0040137E . MOV BYTE PTR DS:[40E948],CL
// 00401354 至 0040137E 只不过是分别取 EGBE的ASCII值 并加1
// 结果为 FHCF, 好像是个组织名 不过没在0DayFTP上看到过这个组织的Relase
00401384 . MOV BYTE PTR DS:[40E950],0
0040138B . PUSH 10
0040138D . PUSH Dumped4W.0040E940
00401392 . LEA EDX,DWORD PTR SS:[EBP-22C]
00401398 . PUSH EDX // 啦啦啦~ 又看到MD5的四个常量了 你猜接下来要干吗?
00401399 . CALL Dumped4W.00401930
0040139E . ADD ESP,0C
004013A1 . LEA EAX,DWORD PTR SS:[EBP-22C]
004013A7 . PUSH EAX
004013A8 . LEA ECX,DWORD PTR SS:[EBP-F0]
004013AE . PUSH ECX
004013AF . CALL Dumped4W.004019E0
004013B4 . ADD ESP,8
004013B7 . MOV EDX,DWORD PTR SS:[EBP-1CC]
004013BD . PUSH EDX
004013BE . LEA EAX,DWORD PTR SS:[EBP-F0]
004013C4 . PUSH EAX
//EAX中保存"74127FHCF-YEAH!!"的MD5Hash结果:AA6E429C590F8579E5D51EBAAE66643A
0012F9B4 AA 6E 42 9C 59 0F 85 79 E5 D5 1E BA AE 66 643A猲B淵厃逭寒fd:
004013C5 . PUSH 10
004013C7 . CALL Dumped4W.004065C0
004013CC . ADD ESP,0C
004013CF . PUSH Dumped4W.0040D194ASCII"Bn6EN1dDFrupNxw1Wk4WO5"
004013D4 . MOV ECX,DWORD PTR SS:[EBP-FC]
004013DA . PUSH ECX
004013DB . CALL Dumped4W.00405E80
01CE2CA0 04 00 00 00 AC 2C CE 01 00 00 00 00 B9 63 E1A4...??....筩幛
01CE2CB0 55 C3 71 93 BA 6B 31 74 75 43 E8 67 00 00 0000U胵摵k1tuC鑗....
004013E0 . ADD ESP,8
004013E3 . MOV EDX,DWORD PTR SS:[EBP-1CC]
004013E9 . PUSH EDX
004013EA . MOV EAX,DWORD PTR SS:[EBP-FC]
004013F0 . PUSH EAX
004013F1 . CALL Dumped4W.004039C0
// 这个CALL用来比较Hash结果是否等于 67E8437574316BBA9371C355A4E163B9
004013F6 . ADD ESP,8
004013F9 . TEST EAX,EAX
004013FB . JE SHORT Dumped4W.00401423
开始我怎么也弄不明白 Bn6EN1dDFrupNxw1Wk4WO5 这个字符串是干什么用的,后来在学习一段
Delphi源代码的时候才想起来 Base64 这个东西。上面一段代码的作用就是 对比"74127FHCF-YEAH!!"
的MD5 Hash结果:AA6E429C590F8579E5D51EBAAE66643A是否等于"Bn6EN1dDFrupNxw1Wk4WO5"
即 67E8437574316BBA9371C355A4E163B9, 不相等则提示 Hmmm.. you don"tevenpass the first threshold!!
我们来考虑一下如何过这第一关, 过关条件是:
"Key的第6位 + Key的第14位 + Key的第21位 + Key的位数 +FHCF-YEAH!!"这段字符串的MD5结果必须
等于67E8437574316BBA9371C355A4E163B9
这就要靠猜了 稍微有点Cracker头脑的人 都会猜到注册码是 xxxxxx-xxxxxx-xxxxxx-xxxxxx这种格式的不然
就必须编程穷举了。 相当于穷举5位密码的MD5值,穷举的速度也不慢 如果条件设置得当,理论上3分钟左右应该
就可以得到结果 我是属于没有Cracker头脑的人 所以这种格式并不是我想到的 :(
验证一下: MD5("27FHCF-YEAH!!") =67E8437574316BBA9371C355A4E163B9验证通过。
重新输入注册信息,进入下一阶段:
Name: LingDi
Key: 123456-890123-567890-234567
00401448 . MOV EDI,Dumped4W.0040E9C0ASCII"123456-890123-567890-234567"
0040144D . OR ECX,FFFFFFFF
00401450 . XOR EAX,EAX
00401452 . REPNE SCAS BYTE PTR ES:[EDI]
00401454 . NOT ECX
00401456 . ADD ECX,-1
00401459 . CMP DWORD PTR SS:[EBP-100],ECX
0040145F . JGE SHORT Dumped4W.004014D5
00401461 . CMP DWORD PTR SS:[EBP-100],6
00401468 . JNZ SHORT Dumped4W.00401479
0040146A . MOV EDX,DWORD PTR SS:[EBP-100]
00401470 . ADD EDX,1
00401473 . MOV DWORD PTR SS:[EBP-100],EDX
00401479 > CMP DWORD PTR SS:[EBP-100],0D
00401480 . JNZ SHORT Dumped4W.00401491
00401482 . MOV EAX,DWORD PTR SS:[EBP-100]
00401488 . ADD EAX,1
0040148B . MOV DWORD PTR SS:[EBP-100],EAX
00401491 > CMP DWORD PTR SS:[EBP-100],14
00401498 . JNZ SHORT Dumped4W.004014A9
0040149A . MOV ECX,DWORD PTR SS:[EBP-100]
004014A0 . ADD ECX,1
004014A3 . MOV DWORD PTR SS:[EBP-100],ECX
004014A9 > MOV EDX,DWORD PTR SS:[EBP-108]
004014AF . MOV EAX,DWORD PTR SS:[EBP-100]
004014B5 . MOV CL,BYTE PTR DS:[EAX+40E9C0]
004014BB . MOV BYTE PTR DS:[EDX+40E940],CL
004014C1 . MOV EDX,DWORD PTR SS:[EBP-108]
004014C7 . ADD EDX,1
004014CA . MOV DWORD PTR SS:[EBP-108],EDX
004014D0 .^JMP Dumped4W.00401439
//看结果就知道 以上是个循环 目地是去除Key中的 "-" 结果:
0040E940 31 32 33 34 35 36 38 39 30 31 32 33 35 36 37381234568901235678
0040E950 39 30 32 33 34 35 36 37 00 00 00 00 00 00 000090234567........
004014F0 > MOV EDI,Dumped4W.0040E940ASCII"123456890123567890234567"
004014F5 . OR ECX,FFFFFFFF
004014F8 . XOR EAX,EAX
004014FA . REPNE SCAS BYTE PTR ES:[EDI]
004014FC . NOT ECX
004014FE . ADD ECX,-1
00401501 . CMP DWORD PTR SS:[EBP-100],ECX
00401507 . JGE SHORT Dumped4W.0040157C
00401509 . MOV ECX,DWORD PTR SS:[EBP-100]
0040150F . MOVSX EDX,BYTE PTR DS:[ECX+40E940]
00401516 . CMP EDX,41
00401519 . JL SHORT Dumped4W.0040152D
0040151B . MOV EAX,DWORD PTR SS:[EBP-100]
00401521 . MOVSX ECX,BYTE PTR DS:[EAX+40E940]
00401528 . CMP ECX,46
0040152B . JLE SHORT Dumped4W.00401577
0040152D > MOV EDX,DWORD PTR SS:[EBP-100]
00401533 . MOVSX EAX,BYTE PTR DS:[EDX+40E940]
0040153A . CMP EAX,30
0040153D . JL SHORT Dumped4W.00401551
0040153F . MOV ECX,DWORD PTR SS:[EBP-100]
00401545 . MOVSX EDX,BYTE PTR DS:[ECX+40E940]
0040154C . CMP EDX,39
0040154F . JLE SHORT Dumped4W.00401577
00401551 > PUSH 0
00401553 . LEA EAX,DWORD PTR SS:[EBP-3C]
00401559 . PUSH EAX
0040155A . LEA EAX,DWORD PTR SS:[EBP-1C4]
00401560 . PUSH EAX
00401561 . LEA EAX,DWORD PTR SS:[EBP+8]
00401567 . MOV EAX,DWORD PTR DS:[EAX]
00401569 . PUSH EAX
0040156A . LEA EAX,DWORD PTR DS:[4017BD]
00401570 . PUSH EAX
00401571 .-JMP DWORD PTR DS:[40E9A4] user32.MessageBoxA
// 得到了 Key 的有效范围: 0~9 A~F
004015C1 . LEA ECX,DWORD PTR SS:[EBP-A4]
004015C7 . PUSH ECX
004015C8 . CALL Dumped4W.00402490 // 跟进这个CALL 会看到SHA算法的5个常量
// 00402490 /$Content$nbsp;MOV EAX,DWORD PTR SS:[ESP+4]
// 00402494 . XOR ECX,ECX
// 00402496 . MOV DWORD PTR DS:[EAX],67452301
// 0040249C . MOV DWORD PTR DS:[EAX+4],EFCDAB89
// 004024A3 . MOV DWORD PTR DS:[EAX+8],98BADCFE
// 004024AA . MOV DWORD PTR DS:[EAX+C],10325476
// 004024B1 . MOV DWORD PTR DS:[EAX+10],C3D2E1F0
// 004024B8 . MOV DWORD PTR DS:[EAX+14],ECX
// 004024BB . MOV DWORD PTR DS:[EAX+18],ECX
// 004024BE \. RETN
004015CD . ADD ESP,4
004015D0 . MOV EDX,DWORD PTR SS:[EBP-1D0]
004015D6 . PUSH EDX
004015D7 . PUSH Dumped4W.0040E8C0 ASCII "LingDi"
004015DC . LEA EAX,DWORD PTR SS:[EBP-A4] // 期待已久的用户名入栈
004015E2 . PUSH EAX
004015E3 . CALL Dumped4W.004024C0
004015E8 . ADD ESP,0C
004015EB . LEA ECX,DWORD PTR SS:[EBP-A4]
004015F1 . PUSH ECX // SHA计算所需要的常量。