提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
把ctfshow密码部分向下翻了下,顺手一点,点开了这道题,进去一看好家伙,有点意思…读了代码后发现好像是一个签名和验证的过程,于是想到了DSA,这个之前没怎么看过,所以还去CTF Wiki上去看了下基本的知识。
一、DSA基本原理及常见攻击
具体的内容可以在CTF Wiki上看到,这里简而言之就是DSA是一种数字签名算法,它的密钥生需要满足以下条件:
1.选择一种合适的哈希函数,用于计算签名内容的哈希
2.选择合适的密钥长度L和N(bit),分别对应公钥参数中的p和q,且p-1 是 q 的倍数
3.计算公钥参数g
4.选择私钥 x,0<x<q ,计算y≡gxmodp
于是我们就得到了公钥为 (p,q,g,y),私钥为 (x)
签名和验证过程直接贴CTF Wiki上的截图了:
推导过程就不纠结了,所以基本的攻击思路就是,如果我们能直接拿到私钥x,就可以直接攻破算法,不过很难实现。其次就是两次签名过程使用了同样的k值,这样就会使得两次签名得到的r值是一定相同的,然后在modq的意义下得到:
s1k - xr ≡ H(m1)
s2k - xr ≡ H(m2)
解这个方程组就可以得到k,x,就可以伪造签名了。
二、关于utf-8编码
utf-8编码中字符对应的索引并不一定在256以内,也就是说会出现多个字节表示一个字符的内容,而且在python3中还有str和bytes类型的区别,使得使用utf-8编码的时候长度可能会不一样。
三、题目解析
1.源码
代码如下(示例):
#!/usr/bin/env python
from hashlib import sha256
from Crypto.Util.number import *
from secret import flag, x
never_gonna_give_you_up = getPrime(128)
never_gonna_let_you_down = getPrime(128)
never_gonna_run_around_and_desert_you = getPrime(101)
def sign(message, public_key, private_key, n):
p, q, g, y = public_key
x = private_key
k = pow(n * never_gonna_give_you_up + never_gonna_let_you_down, never_gonna_run_around_and_desert_you, q)
r = pow(g, k, p) % q
h = int(sha256(message).digest().hex(),16)
s = (h + x * r) * inverse(k, q) % q
return (r, s)
def verify(message, signature, public_key):
p, q, g, y = public_key
r, s = signature
r %= q
s %= q
assert (r != 0 and s != 0), "NAIVE!"
w = inverse(s,q)
h = int(sha256(message).digest().hex(),16)
u1 = (h * w) % q
u2 = (r * w) % q
v = ((pow(g,u1,p