前前后后忙了近半个月,总算是把实践弄完了 记录一下密码学的基础知识吧
单表替代密码 Caesar Cipher 恺撒密码(英语:Caesar cipher),或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。它是一种替换加密的技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def encrypt (plaintext, key ): re_str = "" for i in plaintext: if not i.isalpha(): re_str += i else : a = "A" if i.isupper() else "a" re_str += chr ((ord (i) - ord (a) + key) % 26 + ord (a)) return re_str def decrypt (ciphertext, key ): return encrypt(ciphertext, 26 - key)
Keyword Cipher 关键字(词)密码,给定一个关键字key(假定),得到关键字列表:“keyabcdfghijlmnopqrstuvwxz”,然后对照明文,按照“a”->“k”,“b”->“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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 def getKeywordList (keyword ): """ 主要用于生成密码表 """ normalList = '' keyword = keyword.lower() for i in range (26 ): normalList = normalList + chr (ord ('a' ) + i) toCombine = keyword + normalList combineList = '' for i in toCombine: if i in combineList: pass else : combineList = combineList + i if len (combineList) == 26 : return combineList else : return '' def replaceChar (keywordList, inputChar ): """ 对于大写字母,就先转换为小写然后继续调用改函数 转换至小写字母后,得到该字母在字母表上的位置然后使用keywordList替换即可 """ if inputChar.isupper(): return replaceChar(keywordList, inputChar.lower()).upper() else : return keywordList[ord (inputChar) - 97 ] def dereplaceChar (keywordList, inputChar ): """ 解密函数就是加密函数的逆过程 """ if inputChar.isupper(): return dereplaceChar(keywordList, inputChar.lower()).upper() else : return chr (keywordList.find(inputChar) + 97 ) def encrypt (toReplace, keyword ): """ keyword 字符替换法 替换函数 """ afterReplace = '' keywordList = getKeywordList(keyword) for i in toReplace: if i.isalpha(): afterReplace = afterReplace + replaceChar(keywordList, i) else : afterReplace = afterReplace + i return afterReplace def decrypt (toReplace, keyword ): """ keyword 字符替换法 反替换函数 """ afterReplace = '' keywordList = getKeywordList(keyword) for i in toReplace: if i.isalpha(): afterReplace = afterReplace + dereplaceChar(keywordList, i) else : afterReplace = afterReplace + i return afterReplace
Affine Cipher 仿射密码为单表加密的一种,字母系统中所有字母都藉一简单数学方程加密,对应至数值,或转回字母
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 def gcd (a, b ): """ 求a,b的最大公因数,仿射密码中,a与b互素 """ if a < b: a, b = b, a while b != 0 : temp = a % b a = b b = temp return a def egcd (a, b ): """ 扩展欧几里得算法,用于之后求逆元 """ if a == 0 : return b, 0 , 1 else : g, y, x = egcd(b % a, a) return g, x - (b // a) * y, y def modinv (a, m ): """ 求逆元 """ g, x, y = egcd(a, m) if g != 1 : raise Exception('modular inverse does not exist' ) else : return x % m def encrypt (plaintext, a, b ): """ 加密函数:E(x) = (ax + b)(mod m) m为编码系统中的字母数,一般为26 """ cipher = "" for i in plaintext: if not i.isalpha(): cipher += i else : n = "A" if i.isupper() else "a" cipher += chr ((a * (ord (i) - ord (n)) + b) % 26 + ord (n)) return cipher def decrypt (ciphertext, a, b ): """ 解密函数:D(x) = a^-1 * (x - b)(mod m) a^-1:a在m上的乘法逆元 """ a1 = modinv(a, 26 ) plain = "" for i in ciphertext: if not i.isalpha(): plain += i else : n = "A" if i.isupper() else "a" plain += chr ((a1 * (ord (i) - ord (n) - b)) % 26 + ord (n))
Multiliteral Cipher 与棋盘密码相识,不过棋盘密码中是将字母替换为数字,而在Multiliteral中,是替换为字母 给出一个长度为5的单词,然后将26个字母排成5*5的矩阵,其中i和j合在一起,对照着明文的每一个字母,找到字母在矩阵中的位置,将其替换为key中相应位置的两个字母
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 def removePunctuation (text ): filter = '[^A-Z]' return re.sub(filter , '' , text.upper()) def encrypt (plaintext, key ): """ 主要算法,获取加密字母在二维字母表中的行列值,其密文为与之对应的key中位置的相应字母 """ plain = removePunctuation(plaintext).replace('J' , 'I' ) key = key.upper() cipher = '' for i in range (len (plain)): row = int (keywordList.index(plain[i]) / 5 ) cipher += key[row] col = keywordList.index(plain[i]) % 5 cipher += key[col] return cipher def decrypt (ciphertext, key ): """ 主要算法:对密文两两遍历,找到相应字母在key中的位置,分别对应明文在字母表中的行列 """ cipher = removePunctuation(ciphertext).replace('J' , 'I' ) key = key.upper() plain = '' for i in range (0 , len (cipher), 2 ): row = key.index(cipher[i]) col = key.index(cipher[i + 1 ]) num = row * 5 + col plain += keywordList[num] return plain
多表替代密码 Vigenere Cipher 密码学经典加解密之一 直接百科
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 def encrypt (plaintext, key ): """ vigenere密码就相当于Caesar密码,不同的是vigenere的密钥key是可变的几个值,然后使用这几个值进行加解密 """ cipher = '' count = 0 key = key.lower() for i in range (len (plaintext)): if not plaintext[i].isalpha(): cipher += plaintext[i] count += 1 else : a = "A" if plaintext[i].isupper() else "a" offset = ord (key[(i - count) % len (key)]) - ord ('a' ) cipher += chr ((ord (plaintext[i]) - ord (a) + offset) % 26 + ord (a)) return cipher def decrypt (ciphertext, key ): """ 解密算法原理同Caesar """ plain = '' count = 0 key = key.lower() for i in range (len (ciphertext)): if not ciphertext[i].isalpha(): plain += ciphertext[i] count += 1 else : a = "A" if ciphertext[i].isupper() else "a" offset = ord (key[(i - count) % len (key)]) - ord ('a' ) plain += chr ((ord (ciphertext[i]) - ord (a) - offset) % 26 + ord (a)) return plain
Autokey Cipher Autokey就是Vigenere的一种变种,在Vigenere加密中,密钥是循环使用的,这样就有了被爆破的可能,在Autokey中,有两种形式,一种是Autokey Plaintext,就是将明文直接添加在key之后作为密钥进行加密,另一种是Autokey Ciphertext,是先用key加密得到的Cipher片段作为key循环进行加密 Plaintext与Ciphertext的加解密正好是相反的
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 def removePunctuation (text, filter ='[^A-Z]' ): return re.sub(filter , '' , text.upper()) def encryptPlain (text, keyword ): """ 对于Autokey Plaintext,本质上和vigenere相同,相当于将明文附加在key后面作为密钥来进行vigenere加密 但明文中的空格、非字母字符会影响加密,所以在Autokey的加解密中将除去所有非字母字符,并将全部字母大写 """ text = removePunctuation(text) res = "" key = keyword.upper() for (i, c) in enumerate (text): if i < len (key): offset = ord (key[i]) - 65 else : offset = ord (text[i - len (key)]) - 65 res += chr ((ord (c) - 65 + offset) % 26 + 65 ) return res def decryptPlain (text, keyword ): """ 因为Autokey本质上是vigenere加密,所以在解密上唯一不同的点就是“-offset” """ text = removePunctuation(text) res = "" key = keyword.upper() for (i, c) in enumerate (text): if i < len (key): offset = ord (key[i]) - 65 else : offset = ord (res[i - len (key)]) - 65 res += chr ((ord (c) - 65 - offset) % 26 + 65 ) return res def encryptCipher (text, keyword ): """ Autokey Cipher的加解密与Autokey Plain的解加密相对应 但在求offset上不同,此处使用的与plain相反 """ text = removePunctuation(text) res = "" key = keyword.upper() for (i, c) in enumerate (text): if i < len (key): offset = ord (key[i]) - 65 else : offset = ord (res[i - len (key)]) - 65 res += chr ((ord (c) - 65 + offset) % 26 + 65 ) return res def decryptCipher (text, keyword ): text = removePunctuation(text) res = "" key = keyword.upper() for (i, c) in enumerate (text): if i < len (key): offset = ord (key[i]) - 65 else : offset = ord (text[i - len (key)]) - 65 res += chr ((ord (c) - 65 - offset) % 26 + 65 ) return res
多图替代密码 Playfair Cipher 密码详情见百科
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 def getKeywordList (keyword ): """ 获取密钥矩阵 """ keyword = keyword.upper().replace('J' , 'I' ) normalList = 'ABCDEFGHIKLMNOPQRSTUVWXYZ' toCombine = keyword + normalList combineList = '' for i in toCombine: if i in combineList: pass else : combineList = combineList + i return combineList def removePunctuation (text ): """ 将文本中非英文字母去掉,并将字母转为大写 """ filter = '[^A-Z]' return re.sub(filter , '' , text.upper()) def encryptPart (keywordList, a, b ): """ 加密主要算法: 首先获取得到的两个字母在矩阵中的行列值 然后对两个坐标分别进行判断: 1.位于同一行,返回两个字母在矩阵中右边的字母 2.位于同一列,返回两个字母在矩阵中下边的字母 3.位于对角,返回相应对角字母 """ arow, acol = int (keywordList.index(a) / 5 ), keywordList.index(a) % 5 brow, bcol = int (keywordList.index(b) / 5 ), keywordList.index(b) % 5 if arow == brow: return keywordList[arow * 5 + (acol + 1 ) % 5 ] + keywordList[brow * 5 + (bcol + 1 ) % 5 ] elif acol == bcol: return keywordList[((arow + 1 ) % 5 ) * 5 + acol] + keywordList[((brow + 1 ) % 5 ) * 5 + bcol] else : return keywordList[arow * 5 + bcol] + keywordList[brow * 5 + acol] def decryptPart (keywordList, a, b ): """ 加密算法的逆运算 """ arow, acol = int (keywordList.index(a) / 5 ), keywordList.index(a) % 5 brow, bcol = int (keywordList.index(b) / 5 ), keywordList.index(b) % 5 if arow == brow: return keywordList[arow * 5 + (acol - 1 ) % 5 ] + keywordList[brow * 5 + (bcol - 1 ) % 5 ] elif acol == bcol: return keywordList[((arow - 1 ) % 5 ) * 5 + acol] + keywordList[((brow - 1 ) % 5 ) * 5 + bcol] else : return keywordList[arow * 5 + bcol] + keywordList[brow * 5 + acol] def encrypt (plaintext, key ): """ 对获取的明文进行基本的处理 转为大写 除去非字母 如果遇到两个相同的字母,在中间加'Q' 文本长度不足2的倍数需要加'X'补齐 然后以两两字母进行加密 """ plaintext = removePunctuation(plaintext).replace('J' , 'I' ) plain = "" plain += plaintext[0 ] for i in range (1 , len (plaintext)): if plain[len (plain) - 1 ] == plaintext[i]: plain += 'Q' plain += plaintext[i] keywordList = getKeywordList(key) if len (plain) % 2 == 1 : plain += 'X' ret = '' for c in range (0 , len (plain), 2 ): ret += encryptPart(keywordList, plain[c], plain[c + 1 ]) return ret def decrypt (ciphertext, key ): """ 同加密 """ cipher = removePunctuation(ciphertext).replace('J' , 'I' ) keywordList = getKeywordList(key) if len (cipher) % 2 == 1 : cipher += 'X' ret = '' for c in range (0 , len (cipher), 2 ): ret += decryptPart(keywordList, cipher[c], cipher[c + 1 ]) return ret
置换密码 Permutation cipher 给定一个顺序,例:1,4,5,3,2,然后将明文按照此顺序输出即可,例:abcde fghij -> adecb fijhg
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 def removePunctuation (text, filter ='[^A-Z]' ): return re.sub(filter , '' , text.upper()) def inverseKey (key ): """ 通过key获取解密时所需要的key的逆解 """ inverse = [] for i in range (min (key), max (key) + 1 ): inverse.append(key.index(i) + 1 ) return inverse def encrypt (plaintext, key ): """ 便于在之后对文本的遍历,首先将文本补齐,使其长度为密钥长度的整数倍 然后开始分组遍历,通过对range参数的设置,使每次加密都对密钥长度的明密文进行加解密 然后对组内明密文进行加解密 """ plaintext = removePunctuation(plaintext) cipher = '' for i in range (0 , len (plaintext) % len (key) * -1 % len (key)): plaintext += "X" for i in range (0 , len (plaintext), len (key)): for j in [a - 1 for a in key]: cipher += plaintext[i + j] cipher += " " return cipher[:-1 ] def decrypt (ciphertext, key ): """ 得到逆密钥,然后和加密一样 """ return encrypt(ciphertext, inverseKey(key))
Column permutation cipher 原理与置换密码相似,将明文按照key的长度排成矩阵,然后根据key中字母在字母表中的先后顺序输出每一列,即为密文 Double Translation Cipher即为两次列置换加密
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 def removePunctuation (text, filter ='[^A-Z]' ): return re.sub(filter , '' , text.upper()) def sortind (key ): """ 通过key获取加密顺序 """ t1 = [(key[i], i) for i in range (len (key))] t2 = [(k[1 ], i) for i, k in enumerate (sorted (t1))] return [q[1 ] for q in sorted (t2)] def unsortind (key ): """ 通过key获取解密顺序 """ t1 = [(key[i], i) for i in range (len (key))] return [q[1 ] for q in sorted (t1)] def encrypt (plaintext, key ): """ 加密函数 """ plain = removePunctuation(plaintext) cipher = '' ind = sortind(key) for i in range (len (key)): cipher += plain[ind.index(i)::len (key)] return cipher def decrypt (ciphertext, key ): """ 解密函数 """ cipher = removePunctuation(ciphertext) plain = ['_' ] * len (cipher) l, m = len (cipher), len (key) ind = unsortind(key) upto = 0 for i in range (len (key)): collen = int (l / m) if ind[i] < l % m: collen += 1 plain[ind[i]::m] = cipher[upto:upto + collen] upto += collen return '' .join(plain)