密码学实验报告

这篇博客记录了三次密码学实验的过程,涉及维吉尼亚密码的重合指数法解密、ECB加密的字节破解、padding check算法实现以及CBC比特翻转攻击。博主详细解释了实验原理并展示了代码运行结果,最后提到了RSA算法的漏洞利用实践。

第一次实验

任务1:给出一串用维吉尼亚密码加密后的密文,要求使用重合指数法获得明文。

原理:先使用互重合指数求出密钥长度,将多表替换密码转化为多个单表密码,再对每个单表密码使用重合指数求出替换表。

代码:


s = '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'

from base64 import b64decode

a = b64decode(s.encode())

for i in range(2, 100):
	sum = 0
	for j in range(i):
		cnt = [0 for k in range(256)]
		num = 0
		for k in range(j, len(a), i):
			cnt[a[k]] = cnt[a[k]] + 1
			num = num + 1
		
		for k in range(256):
			sum = sum + cnt[k] * (cnt[k] - 1) / num / (num - 1) / i
	
	if abs(sum - 0.065) < 0.005:
		print(i, sum)

keyl = 29

freq = {  # From https://en.wikipedia.org/wiki/Letter_frequency.
    'e': 0.12702,'t': 0.09056,'a': 0.08167,'o': 0.07507,'i': 0.06966,'n': 0.06749,
    's': 0.06327,'h': 0.06094,'r': 0.05987,'d': 0.04253,'l': 0.04025,'c': 0.02782,
    'u': 0.02758,'m': 0.02406,'w': 0.02360,'f': 0.02228,'g': 0.02015,'y': 0.01974,
    'p': 0.01929,'b': 0.01492,'v': 0.00978,'k': 0.00772,'j': 0.00153,'x': 0.00150,
    'q': 0.00095,'z': 0.00074,' ': 0.25000}

key = []
for i in range(keyl):
	keyc = 0
	min = 999
	for j in range(256):
		cnt = [0 for k in range(256)]
		num = 0
		for k in range(i, len(a), keyl):
			cnt[a[k] ^ j] = cnt[a[k] ^ j] + 1
			num = num + 1
		
		sum = 0
		for k in range(26):
			sum = sum + cnt[k + ord('a')] / num * freq[chr(k + ord('a'))]
		
		if abs(sum - 0.065) < min:
			min = abs(sum - 0.065)
			keyc = j
	
	key.append(keyc)

t = ''
for i in range(len(a)):
	t = t + chr(a[i] ^ key[i % keyl])

print(t)



运行结果截图:

任务2:用户输入密码所使用的按键,告诉你密码的长度和密码SHA1之后的值,让你求密码的值。

任务:直接暴力枚举即可。这道题出得很烂,没有指明每个按键只按了一次,如果没有这个条件本题几乎跑不出来。但大胆猜测这个条件就可以做出本题。

代码:

from hashlib import sha1
import time

start = time.time()

def dfs(x, y, z):
	if x == z:
		if sha1(y.encode()).hexdigest() == '67ae1a64661ac8b4494666f58c4822408dd0a3e4':
			print(y)
			return 1
		return 0
	c = 'qQwW5%8(0=iI*+nN'
	turn = {
		'q': 'Q', 'Q': 'q',
		'w': 'W', 'W': 'w',
		'5': '%', '%': '5',
		'8': '(', '(': '8',
		'0': '=', '=': '0',
		'i': 'I', 'I': 'i',
		'*': '+', '+': '*',
		'n': 'N', 'N': 'n'
	}
	for i in c:
		if i not in y and turn[i] not in y:
			if dfs(x + 1, y + i, z) == 1:
				return 1
	
	return 0

for i in range(10):
	print(i)
	if dfs(0, '', i) == 1:
		break

end = time.time()

print((end - start) // 60, 'min', (end - start) // 1, 'sec')

运行结果截图:

第二次实验

任务1:给你一个函数,每次你可以在秘密的前面加上几个字节,然后得到这些字节经过固定密钥的ECB加密的结果,现在让你破解秘密。

原理:通过在秘密前面加上特定数量的字节,将秘密的第一个未知字节恰好“顶”到一块的最后一个字节,的到一个加密串。另一个则根据已经求出的秘密前缀将暴力猜测的字节“顶”到同样的位置,保证本字节之前的字节和上一个串是一样的。这样通过比较本块的加密结果是否一样就可以暴出这一字节。

代码:


from base64 import b64decode
from Crypto.Cipher import AES
from random import randint, randbytes


def padding(s: bytes):
	r = (16 - len(s) % 16) % 16
	return s + r.to_bytes(1, 'big') * r


def f1(s: bytes, aes, pre):
	c = b'Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg' + \
	b'aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq' + \
	b'dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg' + \
	b'YnkK'
	c = b64decode(c)
	s = padding(pre + s + c)
	return aes.encrypt(s)

	
def work(aes, pre):
	s = f1(b'', aes, pre)
	last = s[:16]
	pre_len = 0
	for i in range(1, 16):
		s = f1(b'a' * i, aes, pre)
		if s[:16] == last:
			pre_len = 16 - i + 1
			break
		last = s[:16]
	
	print('guess pre_len:', pre_len)
	max_len = 1024
	m = b''
	while True:
		blank = b'\x00' * (max_len - 1 - pre_len - len(m))
		for i in range(1, 256):
			s = blank + m + i.to_bytes(1, 'big')
			t = blank
			if f1(s, aes, pre)[:max_len] == f1(t, aes, pre)[:max_len]:
				m = m + i.to_bytes(1, 'big')
				break
		print(m.decode())


if __name__ == '__main__':
	pre_len = randint(1, 16)
	print('true pre_len:', pre_len)
	pre = randbytes(pre_len)
	key = randbytes(16)
	iv = randbytes(16)
	aes = AES.new(key, AES.MODE_ECB)
	
	work(aes, pre)

运行结果截图:

任务2: 实现一个padding check算法,并给出数据验证你的结果。

原理:没啥好说的。

代码:


def check_padding(s: bytes):
	padding = s[-s[-1]:]
	for i in padding:
		if not i == len(padding):
			return False
	return True

print(check_padding(b'ICE ICE BABY\x04\x04\x04\x04'))
print(check_padding(b'ICE ICE BABY\x05\x05\x05\x05'))
print(check_padding(b'ICE ICE BABY\x01\x02\x03\x04'))
print(check_padding(b'ICE ICE BABY'))

运行结果截图:

任务3:告诉你明文的格式是 "comment1=cooking%20MCs;userdata=" + data + ";comment2=%20like%20a%20pound%20of%20bacon",其中data的数据可以自己定,你能把comment2的值改了么。

原理:CBC的比特翻转攻击,通过添加很长的data构造出一个纯没有意义的块,然后异或这一块的某个密文字节,这样下一块的明文字节在解密时就会被篡改。

代码:


from Crypto.Cipher import AES
from random import randbytes


def padding(s: bytes):
	r = (16 - len(s) % 16) % 16
	return s + r.to_bytes(1, 'big') * r


def f1(s: str, aes):
	if ";" in s or "=" in s:
		exit(0)
	s = "comment1=cooking%20MCs;userdata=" + \
		s + \
		";comment2=%20like%20a%20pound%20of%20bacon"
	return aes.encrypt(padding(s.encode()))


def f2(s: bytes, aes):
	s = aes.decrypt(s)
	print(s)
	return b';admin=true;' in s

	
if __name__ == '__main__':
	key = randbytes(16)
	iv = randbytes(16)
	aes1 = AES.new(key, AES.MODE_CBC, iv)
	aes2 = AES.new(key, AES.MODE_CBC, iv)
	
	payload = '\x00' * 32
	c = f1(payload, aes1)
	m = b';admin=true;' + b'\x00' * 4
	t = b''
	for i in range(32, 48):
		t = t + (m[i - 32] ^ c[i]).to_bytes(1, 'big')
	c = c[:32] + t + c[48:]
	
	print(f2(c, aes2))
	

运行结果截图:

第三次实验

任务:本次大作业要求尝试第一节全国高校密码学挑战赛,目的为练习RSA算法常见漏洞的利用。Alice 正在使用一个使用RSA加密的通信程序。她发了21条信息,但其中有些是相同的,还有些参数不是很合适。现在已经拦截了所有21条信息,告诉你密文的格式是各64bit的n、e、c拼接,明文的格式是64bit的标识符、32bit的序列号、若干个0和64bit的明文,总共521个bit。要求你尽可能多地破解Alice的信息。

原理:

本报告利用了4种常见的RSA破解方法:共模攻击、广播攻击、公因数攻击和p-1分解。

  1. 共模攻击

如果一对消息的模数和明文相同,而指数互质,那么可以找到 使 ,那么有

代码:

def common_mod(n, e1, c1, e2, c2):

    # 共模攻击

    x, y = exgcd(e1, e2)

    return pow(c1, x, n) * pow(c2, y, n) % n

  1. 广播攻击

如果若干个消息明文和指数相同,指数和明文个数相同,而且模数互质,那么可以列出方程组

利用中国剩余定理可以解出 ,从而直接开根解出明文。

代码:

def broadcast(a, m, e):

    c = crt(a, m)

    l, r = 1, c

    while l + 1 < r:

        md = (l + r) // 2

        if md ** e < c:

            l = md

        else:

            r = md

    if l ** e == c:

        return l

    if r ** e == c:

        return r

    return 0

  1. 公因数攻击

如果两个不同模数 的一个因子很不幸地相同了,那么可以直接求出 ,导致分解出模数的因子,进而求得密钥。

代码:

def common_factor(n1, e1, c1, n2, e2, c2):

    # 公因数攻击

    p = gcd(n1, n2)

    q1 = n1 // p

    q2 = n2 // p

    phi1 = (p - 1) * (q1 - 1)

    phi2 = (p - 1) * (q2 - 1)

    d1 = inverse(e1, phi1)

    d2 = inverse(e2, phi2)

    return pow(c1, d1, n1), pow(c2, d2, n2)

  1. P-1分解

选取一个数 并取 ,如果 足够大,就有 。那么由 ,有 。又 ,所以 。将次数与 取gcd即可分解模数。

代码:

def p_1(n, e, c, b):

    # p-1 分解

    k = 1

    for i in range(b):

        k *= i + 1

   

    p = gcd(pow(2, k, n) - 1, n)

    if p == 1 or p == n:

        return 0

    q = n // p

    if p * q != n:

        return 0

    phi = (p - 1) * (q - 1)

    d = inverse(e, phi)

    return pow(c, d, n)

return 0

执行以上算法可以求出第0 1 2 4 18 19帧,也就是明文的第0 1 5 6 10 11块,内容为” My secre t is a f”、” instein. That is”和” m A to B . Imagin”。

代码:


from Crypto.Util.number import inverse
from random import randint

def int2str(a):
    s = ''
    for i in range(8):
        s += chr(a % 256)
        a >>= 8

    s = s[::-1]
    a >>= 352

    return (a & ((1 << 32) - 1)), s

def check(a):
    a >>= 64
    a = a & ((1 << 352) - 1)
    if a == 0:
        return 1
    else:
        return 0

def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

def exgcd(a, b):
    if b == 0:
        return 1, 0
    x, y = exgcd(b, a % b)
    return y, x - a // b * y

def common_mod(n, e1, c1, e2, c2):
    # 共模攻击
    x, y = exgcd(e1, e2)
    return pow(c1, x, n) * pow(c2, y, n) % n

def common_factor(n1, e1, c1, n2, e2, c2):
    # 公因数攻击
    p = gcd(n1, n2)
    q1 = n1 // p
    q2 = n2 // p
    phi1 = (p - 1) * (q1 - 1)
    phi2 = (p - 1) * (q2 - 1)
    d1 = inverse(e1, phi1)
    d2 = inverse(e2, phi2)
    return pow(c1, d1, n1), pow(c2, d2, n2)

def crt(a, m):
    M = 1
    for i in m:
        M *= i

    res = 0
    for i in range(len(m)):
        res = (res + a[i] * M // m[i] * inverse(M // m[i], m[i])) % M

    return res

def broadcast(a, m, e):
    c = crt(a, m)
    l, r = 1, c
    while l + 1 < r:
        md = (l + r) // 2
        if md ** e < c:
            l = md
        else:
            r = md

    if l ** e == c:
        return l
    if r ** e == c:
        return r

    return 0

def p_1(n, e, c, b):
    # p-1 分解
    k = 1
    for i in range(b):
        k *= i + 1
    
    p = gcd(pow(2, k, n) - 1, n)
    if p == 1 or p == n:
        return 0
    q = n // p
    if p * q != n:
        return 0
    phi = (p - 1) * (q - 1)
    d = inverse(e, phi)
    return pow(c, d, n)

    return 0

def main():
    n = []
    e = []
    c = []

    for i in range(21):
        f = open('./data/Frame' + str(i))
        s = f.read()
        n.append(int(s[:256], 16))
        e.append(int(s[256:512], 16))
        c.append(int(s[512:], 16))

    m = [0 for i in range(21)]

    # 寻找共模攻击
    print("Common mod:")
    for i in range(21):
        for j in range(i):
            if n[i] == n[j]:
                # print(i, j, gcd(e[i], e[j]))
                m[i] = common_mod(n[i], e[i], c[i], e[j], c[j])
                m[j] = m[i]
                print(i, j, ":", int2str(m[i]))

    # 寻找公因数攻击
    print("Common factor:")
    for i in range(21):
        for j in range(i):
            if gcd(n[i], n[j]) > 1 and n[i] != n[j]:
                m[i], m[j] = common_factor(n[i], e[i], c[i], n[j], e[j], c[j])
                print(i, ":", int2str(m[i]))
                print(j, ":", int2str(m[j]))

    # 广播攻击
    print("Broadcast:")
    id = [3, 8, 12, 16, 20]
    temp = broadcast([c[i] for i in id], [n[i] for i in id], 5)
    for i in id:
        m[i] = temp
    print(int2str(temp))

    # p-1 分解
    print("P-1:")
    for i in range(1, 21):
        if m[i] > 0:
            continue
        print("Attempt:", i)
        temp = p_1(n[i], e[i], c[i], 10000)
        if temp > 0:
            print(i, ":", int2str(temp))

main()

运行结果截图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值