仿射加密法

仿射加密法

乘數加密法 + 凱撒加密法 = 仿射加密法
在乘數加密之後執行凱撒加密,便是仿射加密。
這樣確保字母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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章