实验一:熟悉Python开发环境,凯撒密码与仿射密码

实验目的

  • 熟悉Pycharm/ Python Idle开发

  • 完成Casear密码的编程。

实验内容

1、安装Python、Pycharm;了解Idle的应用;学会pip安装命令,将Cryptography gmpy2库安装到python中。

2、编写Casear密码程序,扩展到仿射密码、以及Casear密码的破译程序。

caesar密码python实现

Python 密码学教程 - w3schools

第一版代码:

两个函数分别实现对Caesar密码的加密和解密,关键的操作公式是相同的,只是对密钥进行加减操作。

1
2
for i in message:
ciphertext += (chr(ord(i) + k)) # 将明文汉字转换为对应ASCLL数值或Unicode数值(ord函数),然后在此数值上+3,再将该值返回汉字(chr函数)

第二版代码:

同样是两个函数分别实现对Caesar密码的加密和解密,将公式进行修改,第一版并不能把密码限制在26个字母。

1
2
3
4
5
6
for i in range(len(text)):
char = text[i]
if (char.isupper()):
ciphertext += chr((ord(char) + key - ord('a')) % 26 + ord('a'))
else:
ciphertext += chr((ord(char) + key - ord('A')) % 26 + ord('A'))

第三版:

能够自定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'
def caesar_encrypt(mode, message, key):
if mode[0] == 'd':
key = -key
ciphertext = ''
for symbol in message:
if symbol.isalpha():
num = ord(symbol)+key
if symbol.isupper(): # 所有密文字母是大写
if num > ord('Z'):
num -= 26
elif num < ord('A'):
num += 26
elif symbol.islower():
if num > ord('z'):
num -= 26
elif num < ord('a'):
num += 26
ciphertext += chr(num)
else:
ciphertext += symbol
return ciphertext

暴力破解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def caesar_decrypt(message):
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
letters = "abcdefghijklmnopqrstuvwxyz"
for key in range(26):
translated = ''
for symbol in message:
if symbol in LETTERS:
num = LETTERS.find(symbol)
num = num - key
if num < 0:
num = num + len(LETTERS)
translated = translated + LETTERS[num]
elif symbol in letters:
num = letters.find(symbol)
num = num - key
if num < 0:
num = num + len(letters)
translated = translated + letters[num]
else:
translated = translated + symbol
print('Hacking key #%s: %s' % (key, translated))

仿射密码

仿射密码Python实现
仿射密码是一种替换密码。它是利用加密函数一个字母对一个字母的加密。

加密函数是E(x)= (ax + b) (mod m),其中,a和m互质,m是字符集的大小。
(例如,26即是以26个字母作为编码,当m是26时,a必须是1,3,5,7,9,11,15,17,19,21,23,25其中之一)

解密函数为D(x) = a-1(x - b) (mod m),其中a-1是a在Zm群的乘法逆元。

乘法逆元
群G中任意一个元素a,都在G中有唯一的逆元a’,具有性质aa’ = a’a = e,其中e为群的单位元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
'''
仿射密码
(a,b)
m = 26,字符集为小写字母
加密函数是E(x)= (ax + b) (mod m)
解密函数为D(x) = (a^-1)(x - b) (mod m),其中a^-1是a的乘法逆元
'''

#通过一个简单的遍历得到a的乘法逆元,也可以通过gmpy2库中的invert函数实现
def get_inverse(a):
for i in range(1,27):
if a*i%26==1:
return i

#加密
def encipher(a, b, p):
c=[]
for i in p:
temp=((ord(i)-97)*a+b)%26+97
c.append(chr(temp))
print(''.join(c))

#解密
def decipher(a, b, c):
a_inv = get_inverse(a)
p=[]
for i in c:
temp=(((ord(i)-97)-b)*a_inv)%26+97
p.append(chr(temp))
print(''.join(p))

if __name__ == "__main__":
a = 11
b = 6
message = 'sorcery'
encipher(a,b,message)
#decipher(a,b,message)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
'''
仿射密码
m = 52
字符集为小写和大写字母
'''
import string
def encrypt(k1,k2,message):
dic = string.ascii_letters
c = []
for i in message:
if i.islower():
num = ord(i)-ord('a')
c.append(dic[(num*k1+k2)%52])
elif i.isupper():
num = ord(i)-ord('A')+26
c.append(dic[(num*k1+k2)%52])
else:
c.append(i)
print(''.join(c))

def decrypt(k1,k2,message):
for i in range(52):
if k1*i%52==1:
inv = i
break
dic = string.ascii_letters
m = []
for i in message:
if i.islower():
num = ord(i)-ord('a')
m.append(dic[inv*(num-k2)%52])
elif i.isupper():
num = ord(i)-ord('A')+26
m.append(dic[inv*(num-k2)%52])
else:
m.append(i)
print(''.join(m))

message = 'gVEXGT iDIT' #待加密或解密的消息
a = 5 # key的范围0~51之间
b = 29 # key的范围0~51之间
# encrypt(a,b,message)
decrypt(a,b,message)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Affine(object):
DIE = 128
KEY = (7, 3, 55)

def __init__(self):
pass

def encryptChar(self, char):
K1, K2, kI = self.KEY
return chr((K1 * ord(char) + K2) % self.DIE)

def encrypt(self, string):
return "".join(map(self.encryptChar, string))

def decryptChar(self, char):
K1, K2, KI = self.KEY
return chr(KI * (ord(char) - K2) % self.DIE)

def decrypt(self, string):
return "".join(map(self.decryptChar, string))

affine = Affine()
print(affine.encrypt('Affine Cipher'))
print(affine.decrypt('*18?FMT'))

实验二:哈希函数

1、 编程实现生成空字符串、‘Alice’、‘Bob’的md5、sha256的哈希值;

1
2
3
4
5
6
7
8
9
10
11
12
import hashlib
def hash_md5():
alice = hashlib.md5(b"Alice")
bob = hashlib.md5(b"Bob")
print('"Alice" md5:' + alice.hexdigest())
print('"Bob" md5:' + bob.hexdigest())

def hash_sha256():
alice = hashlib.sha256(b"Alice")
bob = hashlib.sha256(b"Bob")
print('"Alice" sha256:' + alice.hexdigest())
print('"Bob" sha256:' + bob.hexdigest())

2、 编程实现生成自己名字的哈希值,注意编码的转换;

1
2
3
def myhash(str = "Yangjiaqing"):
res = hashlib.md5(str.encode(encoding="utf-8"))
print(str + " md5: " + res.hexdigest())

3、 编写体现哈希雪崩的代码,哈希值用二进制表示; 参考Listing2-5 代码;
Python 哈希函数与消息认证实验
展示散列函数的雪崩效应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def cmpcount(str1, str2):
count = 0
for i in range(0, len(str1)):
if str1[i] != str2[i]:
count += 1
return count

def avalanche(str1 = 'bob', str2 = 'aob'):
bin1 = str1.encode('utf-8')
bin2 = str2.encode('utf-8')
hexstring1 = hashlib.md5(bin1).hexdigest()
binstring1 = '{:08b}'.format(int(hexstring1, 16))
# binstring1 = bin(int(hexstring1, 16))
hexstring2 = hashlib.md5(bin2).hexdigest()
binstring2 = '{:08b}'.format(int(hexstring2, 16))
# binstring2 = bin(int(hexstring2, 16))
print(int(hexstring1, 16))
print(int(hexstring2, 16))
print(str1 + " md5:" + binstring1)
print(str2 + " md5:" + binstring2)
print("两个哈希值不同的位数:" + str(cmpcount(binstring1, binstring2)))

4、 利用scrypt密钥派生函数,实现口令加盐,生成更加安全的密钥(口令);
创建一个 Python 盐
Scrypt介绍
Scrypt 是一个强大的密钥派生函数,其通过内存密集的计算方式来抵抗 GPU、ASIC、FPGA 这类密码破解硬件的攻击。

Scrypt 接收多个输入参数,进行计算后输出密钥:

key = Scrypt(password, salt, N, r, p, derived-key-len)
其中的参数被称为” Scrypt 配置参数”,说明如下:

N - 迭代次数,将影响 CPU 和内存用量,例:16384 、2048 ;
r - 块大小,将影响 CPU 和内存用量,例:8 ;
p - 并行因数 (并行运行的线程数,将影响 CPU 和内存用量),通常为 1 ;
password - 输入的密码(推荐至少为 8 - 10 个字符);
salt - 安全产生的随机字节序列(最小为 64 位,推荐 128 位);
derived-key-len - 输出的密钥要有多少字节长,例如 32 (256 位)
Scrypt 的输出密钥长度可以是 128 位到 512 位,但是通常为 256 位。

Salt 用使用 secrets 模块生成,也可以用os.urandom()随机生成

1
2
3
4
5
def hash_password(password = b'p@$Sw0rD~7'):
salt_length = 16
salt = secrets.token_bytes(salt_length)
key = pyscrypt.hash(password, salt, 2048, 8, 1, 32)
return key.hex()

5、实现区块链中的工作量证明编程,通过设置不同的难度,体会生成符合要求哈希值需要时间长短的不同;
从零开始构建一个区块链(二): 工作量证明
使用python实现简版区块链-工作量证明
区块链的简单实现 - github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import sys
import hashlib
class NonceNotFoundError(Exception):
pass

def encode(str, code='utf-8'):
return str.encode(code)

def decode(bytes, code='utf-8'):
return bytes.decode(code)

def sum256_hex(*args):
m = hashlib.sha256()
for arg in args:
if isinstance(arg, str):
m.update(arg.encode())
else:
m.update(arg)
return m.hexdigest()

def sum256_byte(*args):
m = hashlib.sha256()
for arg in args:
if isinstance(arg, str):
m.update(arg.encode())
else:
m.update(arg)
return m.digest()

class ProofOfWork(object):
_N_BITS = 20
MAX_BITS = 256
MAX_SIZE = sys.maxsize
def __init__(self, n_bits=_N_BITS):
self._n_bits = n_bits
self._target_bits = 1 << (self.MAX_BITS - n_bits)
def _prepare_data(self, nonce):
data_lst = [str(nonce)]
return encode(''.join(data_lst))
def run(self):
nonce = 0
found = False
hash_hex = None
print('Mining a new block')
while nonce < self.MAX_SIZE:
data = self._prepare_data(nonce)
hash_hex = sum256_hex(data)
hash_val = int(hash_hex, 16)
sys.stdout.write("try nonce == %d hash_hex == %s \r" % (nonce, hash_hex))
if (hash_val < self._target_bits):
found = True
break

nonce += 1
if found:
print('Found nonce == %d' % nonce)
else:
print('Not Found nonce')
raise NonceNotFoundError('nonce not found')
return nonce, hash_hex
if __name__ == "__main__":
bc = ProofOfWork()
bc.run()

实验三:实现对称加密编程

分组密码的五大工作模式

使用cryptography进行AES的cbc模式加密 - 知乎 (zhihu.com)

istommao/cryptokit: cryptokit is a cryptography kit (github.com)

4、练习3-12 手工CBC:编程应用AES的ECB模式实现CBC模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def ecbTocbc(self, data):
if not isinstance(data, bytes):
data = data.encode()
cipher = Cipher(algorithms.AES(self.aes_key),
modes.ECB(),
backend=default_backend())
dataList = []
cipherText = []
padded_data = self.pkcs7_padding(data)
binary_data = bin((int(padded_data.hex(), 16)))[2:]
while len(binary_data) / 128 >= 0:
if len(binary_data) / 128 == 0 & len(binary_data) % 128 == 0:
break
elif len(binary_data) / 128 == 0 & len(binary_data) % 128 != 0:
dataList.append(binary_data[0:])
dataList.remove()
else:
dataList.append(binary_data[0:128])
binary_data = binary_data[128:]

c = "{:08b}".format(int(self.aes_iv.hex(), 16))
for i in dataList:
ci = cipher.encryptor().update(bytes(i^c))
cipherText.append(ci)
c = ci.decode()
print(c)
m = ''.join([c.hex() for c in cipherText])
return m

5、练习3-13 简单CTR模式:编程应用AES的ECB模式实现CTR模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def ecbToctr(self, data):
if not isinstance(data, bytes):
data = data.encode()
iv = self.aes_iv
cipher = Cipher(algorithms.AES(self.aes_key),
modes.ECB(),
backend=default_backend())

dataList = []
cipherText = []
padded_data = self.pkcs7_padding(data)
binary_data = "{:08b}".format(int(padded_data.hex(), 16)) + '0'
while len(binary_data) / 128 >= 0:
if len(binary_data) / 128 == 0 & len(binary_data) % 128 == 0:
break
elif len(binary_data) / 128 == 0 & len(binary_data) % 128 != 0:
dataList.append(binary_data[0:])
dataList.remove()
else:
dataList.append(binary_data[0:128])
binary_data = binary_data[128:]


for i in dataList:
ci = cipher.encryptor().update(iv) ^ i.encode()
iv += 1
cipherText.append(ci)
print(ci)
m = ''.join(cipherText)
return m

实验四:实现非对称加密编程

非对称密钥沉思系列(1):RSA专题之PKCSv1.5填充模式下的选择性密文攻击概述-腾讯云开发者社区-腾讯云 (tencent.com)