实验一:熟悉 Python 开发环境,凯撒密码与仿射密码
实验目的
-
熟悉 Pycharm/ Python Idle 开发
-
完成 Casear 密码的编程。
实验内容
1、安装 Python、Pycharm;了解 Idle 的应用;学会 pip 安装命令,将 Cryptography gmpy2 库安装到 python 中。
2、编写 Casear 密码程序,扩展到仿射密码、以及 Casear 密码的破译程序。
caesar 密码 python 实现
第一版代码:
两个函数分别实现对 Caesar 密码的加密和解密,关键的操作公式是相同的,只是对密钥进行加减操作。
for i in message: ciphertext += (chr(ord(i) + k)) # 将明文汉字转换为对应ASCLL数值或Unicode数值(ord函数),然后在此数值上+3,再将该值返回汉字(chr函数)第二版代码:
同样是两个函数分别实现对 Caesar 密码的加密和解密,将公式进行修改,第一版并不能把密码限制在 26 个字母。
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'))第三版:
能够自定义
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暴力破解
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 为群的单位元。
'''仿射密码(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)'''仿射密码m = 52字符集为小写和大写字母'''import stringdef 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)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 的哈希值;
import hashlibdef 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、 编程实现生成自己名字的哈希值,注意编码的转换;
def myhash(str = "Yangjiaqing"): res = hashlib.md5(str.encode(encoding="utf-8")) print(str + " md5: " + res.hexdigest())3、 编写体现哈希雪崩的代码,哈希值用二进制表示; 参考 Listing2-5 代码; Python 哈希函数与消息认证实验 展示散列函数的雪崩效应
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()随机生成
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
import sysimport hashlibclass 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_hexif __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 模式
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 m5、练习 3-13 简单 CTR 模式:编程应用 AES 的 ECB 模式实现 CTR 模式
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)