仿射加密法
乘數加密法 + 凱撒加密法 = 仿射加密法
在乘數加密之後執行凱撒加密,便是仿射加密。
這樣確保字母A不總是加密到A,且密鑰是乘數加密的26倍。
凱撒加密法:https://blog.csdn.net/youngdianfeng/article/details/104333494
乘數加密法:https://blog.csdn.net/youngdianfeng/article/details/104401000
算法大意
加密過程:明文 --> 乘以密鑰A --> 加上密鑰B --> 根據符號集的大小取模 --> 密文
解密過程:明文 <-- 根據符號集的大小取模 <-- 乘以密鑰A的模逆 <-- 減去密鑰B <-- 密文
弊端
密鑰A不能使用任何數字,如果是8,字母C和P都加密成Q。
必須要滿足的條件:密鑰A數字和符號集的大小必須互質 及 gcd(密鑰,符號集大小) ==1
仿射加密法的加密密鑰和解密密鑰是兩個不同的數字。
什麼是模逆
兩個數字a和m的模逆i滿足 (a*i) % m == 1
可以通過暴力運算出模逆,但是如果像8953851這種密鑰就比較耗時,提供了歐幾里得的擴展算法可以快速算出模逆。
代碼實例
# 找出兩個數字的最大公因數 如果是1則代表是互質
def gcd(a,b):
while a!=0:
a,b = b % a,a
return b
# 歐幾里得的擴展算法 找出模逆算法
def findModInverse(a,m):
if gcd(a,m)!=1:
# 如果a和m不互質,則不存在模逆
return None
u1,u2,u3 = 1,0,a
v1,v2,v3 = 0,1,m
while v3!=0:
q = u3 // v3
v1,v2,v3,u1,u2,u3 = (u1 - q*v1),(u2 - q*v3),(u3 - q*v3),v1,v2,v3
return u1 % m
import sys, random
# 字典(符號集)
SYMBOLS = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""" # 第一個是空格字符
# 將一個密鑰分成兩個密鑰及密鑰A和密鑰B,key = (keyA * 字典大小) + keyB
def getKeyParts(key):
keyA = key // len(SYMBOLS)
keyB = key % len(SYMBOLS)
return (keyA, keyB)
# 驗證密鑰是否可用
def checkKeys(keyA, keyB, mode):
# 如果keyA是1的話 或者 KeyB是0的話 那密鑰是個弱密鑰(如果KeyA是1,keyB是0,那麼就沒有加密)
if keyA == 1 and mode == 'encrypt':
sys.exit('The affine cipher becomes incredibly weak when key A is set to 1. Choose a different key.')
if keyB == 0 and mode == 'encrypt':
sys.exit('The affine cipher becomes incredibly weak when key B is set to 0. Choose a different key.')
# keyA和keyB不能是負數
if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' % (len(SYMBOLS) - 1))
# keyA和字典總數互質
if gcd(keyA, len(SYMBOLS)) != 1:
sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
# 加密算法
def encryptMessage(key, message):
keyA, keyB = getKeyParts(key)
checkKeys(keyA, keyB, 'encrypt')
ciphertext = ''
for symbol in message:
if symbol in SYMBOLS:
# 加密單個字符
symIndex = SYMBOLS.find(symbol)
ciphertext += SYMBOLS[(symIndex * keyA + keyB) % len(SYMBOLS)]
else:
ciphertext += symbol # 如果不在字符集裏就在密文裏直接加入這個字符
return ciphertext
# 解密算法
def decryptMessage(key, message):
keyA, keyB = getKeyParts(key)
checkKeys(keyA, keyB, 'decrypt')
plaintext = ''
modInverseOfKeyA = findModInverse(keyA, len(SYMBOLS))
for symbol in message:
if symbol in SYMBOLS:
# 解密單個字符
symIndex = SYMBOLS.find(symbol)
plaintext += SYMBOLS[(symIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
else:
plaintext += symbol # 如果不在字符集裏就在密文裏直接加入這個字符
return plaintext
# 隨機出一個可用密鑰
def getRandomKey():
while True:
keyA = random.randint(2, len(SYMBOLS))
keyB = random.randint(2, len(SYMBOLS))
if gcd(keyA, len(SYMBOLS)) == 1:
return keyA * len(SYMBOLS) + keyB
def main():
myMessage = """"A computer would deserve to be called intelligent if it could deceive a human into believing that it was human." -Alan Turing"""
myKey = getRandomKey() # 例如 2023
# 加密
translated = encryptMessage(myKey, myMessage)
print('Key: %s' % (myKey))
print('ciphertext : %s' %(translated))
print()
# 解密
print('decrypt hte ciphertext....')
translated = decryptMessage(myKey, translated)
print('plaintext : %s' %(translated))
if __name__ == '__main__':
main()
運行結果
Key: 3764
ciphertext : J0[,%6LQz;[@%QnS[Szbz;xz[%[dz[,=nnzS[X]znnXiz][XB[X*[,%QnS[Sz,zXxz[=[1Q6=][X]%[dznXzxX]i[1=[X[@=b[1Q6=]CJ[{0n=][|Q;X]i
decrypt hte ciphertext…
plaintext : “A computer would deserve to be called intelligent if it could deceive a human into believing that it was human.” -Alan Turing