[Network] 12. 常見的加密演算法,含 python 實現

 · 8 mins read

什麼叫數據加密

數據加密是指利用加密演算法和祕鑰將明文轉變爲密文的過程。常見的加密方式有 base64、RSA、MD5、SHA-1、HMAC、DES/AES、ECC


數據加密的方式

  1. 單向加密:

    • 指只能加密數據而不能解密數據,這種加密方式主要是爲了保證數據的完整性

    • 常見的加密演算法有 MD5、sha 系列等(位於 python 內建的 hashlib 模組中)。

  2. 對稱加密:

    • 指數據加密和解密使用相同的祕鑰,這種加密方式主要是爲了保證數據的機密性

    • 常見的加密演算法有 DES、AES(位於 python 第三方庫 pycrytodomex 中)。

  3. 非對稱加密

    • 也叫公鑰加密,指數據加密和解密使用不同的金鑰,這種加密方式基本不可能被破解,主要用於身份驗證等方面

    • 常見的加密演算法有 DSA、RSA(位於 python 第三方模組 rsa 中)。


加密演算法

▍單向加密演算法(MD5、sha系列)不可逆

  • MD5 加密:MD5 訊息摘要演算法(英語:MD5 Message-Digest Algorithm)

    • 一種被廣泛使用的密碼雜湊函數,可以產生出一個 128 位元(16 位元組)的雜湊值(hash value),用於確保資訊傳輸完整一致。

    • md5 加密演算法是不可逆的,所以解密一般都是通過暴力窮舉方法,通過網站的介面實現解密。

        import hashlib
        m = hashlib.md5()
        m.update(str.encode("utf8"))
        print(m.hexdigest())
      
  • sha 加密:安全雜湊演算法(Secure Hash Algorithm)

    • 主要適用於數位簽名標準(Digital Signature Standard DSS)裏面定義的數位簽名演算法(Digital Signature Algorithm DSA),SHA1 比 MD5 的安全性更強。

    • 對於長度小於 2^ 64 位元的訊息,SHA1 會產生一個 160 位元的訊息摘要。

        import hashlib
        sha1 = hashlib.sha1()
        data = 'helloword'
        sha1.update(data.encode('utf-8'))
        sha1_data = sha1.hexdigest()
        print(sha1_data)
      


▍對稱加密演算法(AES、DES)

  • DES 加密

    • DES 是一個分組加密演算法,典型的 DES 以 64 位元爲分組對數據加密

    • 加密和解密用的是同一個演算法。

    • 它的金鑰長度是 56 位(因爲每個第 8 位都用作奇偶校驗),金鑰可以是任意的 56 位的數,而且可以任意時候改變。

        from Cryptodome.Cipher import DES
      
        key = b'88888888'
        data = "Good Good Study! Day Day Up!"
        count = 8 - (len(data) % 8)
        plaintext = data + count * "="
      
        # 金鑰
        des = DES.new(key, DES.MODE_ECB)
      
        # 加密
        ciphertext = des.encrypt(plaintext.encode())
        print(ciphertext)
        >>> b'D\xa4Z\x1dt\xba\xf3\xe8\xdbv\x1aP\x81\xe4\xe6Jx?\xfe\xf2\x0b\x82\nG\x08d\xea\xd0\t\x07vs'
      
        # 解密
        plaintext = des.decrypt(ciphertext)
        plaintext = plaintext[:(len(plaintext)-count)]
        print(plaintext)
        >>>
        b'Good Good Study! Day Day Up!'
      
  • AES 加密

    • 在密碼學中又稱 Rijndael 加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的 DES

    • 目前使用的比較廣泛,一串看不懂的字串如果有 / 我們首先想到的是 aes 加密

    • AES 只是個基本演算法,實現 AES 有幾種模式,主要有 ECB、CBC、CFB 和 OFB 這幾種(其實還有個 CTR)。

    • AES 爲分組密碼,分組密碼也就是把明文分成一組一組的,每組長度相等,每次加密一組數據,直到加密完整個明文。在 AES 標準規範中,分組長度只能是 128 位元,也就是說,每個分組爲 16 個位元組(每個位元組 8 位元)。金鑰的長度可以使用 128 位元、192 位或 256 位。金鑰的長度不同,推薦加密輪數也不同。

    • AES ECB 模式加密方法 Python 實現:
        from Crypto.Cipher import AES
        import base64
      
        BLOCK_SIZE = 16  # Bytes
        pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
                        chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
        unpad = lambda s: s[:-ord(s[len(s) - 1:])]
                      
      
        # AES的ECB模式加密方法
        def aesEncrypt(key, data):
            '''
            :param key: 金鑰
            :param data:被加密字串(明文)
            :return:密文
            '''
            key = key.encode('utf8')
            # 字串補位
            data = pad(data)
            cipher = AES.new(key, AES.MODE_ECB)
            # 加密後得到的是bytes型別的數據,使用Base64進行編碼,返回byte字串
            result = cipher.encrypt(data.encode())
            encodestrs = base64.b64encode(result)
            enctext = encodestrs.decode('utf8')
            print(enctext)
            return enctext
      
        # AES的ECB模式解密方法
        def aesDecrypt(key, data):
            '''
            :param key: 金鑰
            :param data: 加密後的數據(密文)
            :return:明文
            '''
            key = key.encode('utf8')
            data = base64.b64decode(data)
            cipher = AES.new(key, AES.MODE_ECB)
      
            # 去補位
            text_decrypted = unpad(cipher.decrypt(data))
            text_decrypted = text_decrypted.decode('utf8')
            print(text_decrypted)
            return text_decrypted
      
        if __name__ == '__main__':
            key = '5c44c819appsapi0'
            data = 'herish acorn'
            ecdata = aesEncrypt(key, data)
            aesDecrypt(key, ecdata)
      
    • AES CBC 模式加密方法 Python 實現

        from Crypto.Cipher  import AES
        import base64
      
        # 金鑰(key), 密斯偏移量(iv) CBC模式加密
        BLOCK_SIZE = 16  # Bytes
        pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
                        chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
        unpad = lambda s: s[:-ord(s[len(s) - 1:])]
        vi = '0102030405060708'  # 偏移量
      
      
        # 加密
        def AES_Encrypt(key, data):
            data = pad(data)
            # 字串補位
            cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
            encryptedbytes = cipher.encrypt(data.encode('utf8'))
            # 加密後得到的是bytes型別的數據,使用Base64進行編碼,返回byte字串
            encodestrs = base64.b64encode(encryptedbytes)
            # 對byte字串按utf-8進行解碼
            enctext = encodestrs.decode('utf8')
            return enctext
      
      
        # 解密
        def AES_Decrypt(key, data):
            data = data.encode('utf8')
            encodebytes = base64.decodebytes(data)
            # 將加密數據轉換位bytes型別數據
            cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
            text_decrypted = cipher.decrypt(encodebytes)
            # 去補位
            text_decrypted = unpad(text_decrypted)
            text_decrypted = text_decrypted.decode('utf8')
            print(text_decrypted)
            return text_decrypted
      
        if __name__ == '__main__':
            key = '5c44c819appsapi0'
            data = 'herish acorn'
            enctext = AES_Encrypt(key, data)
            print(enctext)
            AES_Decrypt(key, enctext)
      


▍非對稱加密演算法(RSA、DSA)

  • 安裝第三方庫:

    • RSA 加密演算法是一種非對稱加密演算法。加密和解密使用不同的祕鑰。一把作爲公開的公鑰,另一把作爲私鑰。這對金鑰中的公鑰進行加密,私鑰用於解密。

    • 事實上,公鑰加密演算法很少用於數據加密,它通常只是用來做身份認證,因爲它的金鑰太長,加密速度太慢–公鑰加密演算法的速度甚至比對稱加密演算法的速度慢上 3 個數量級(1000 倍)。

    • 在公開金鑰加密和電子商業中 RSA 被廣泛使用。它被普遍認爲是目前最優秀的公鑰方案之一。RSA 是第一個能同時用於加密和數位簽名的演算法,它能夠抵抗到目前爲止已知的所有密碼攻擊。

        import rsa
      
        # 返回 公鑰加密、私鑰解密
        public_key, private_key = rsa.newkeys(1024)
        print(public_key)
        # PublicKey(115358647593237027749555219330290547595292720354379729059572469455025379115527291514465303947468690370446593609121177089794716265226101498361786298396410892325533861129676356325971818358112602498513419680609056457389715318966834362898086552554130435425753061655286667511557573410756120684188042377774434444807, 65537)
        print(private_key)
        # PrivateKey(115358647593237027749555219330290547595292720354379729059572469455025379115527291514465303947468690370446593609121177089794716265226101498361786298396410892325533861129676356325971818358112602498513419680609056457389715318966834362898086552554130435425753061655286667511557573410756120684188042377774434444807, 65537, 111285529956928522901721617280604228002764723117703733926382810265271061290888840676549733913221737511493431004615227720952917381576663793443813612330045581626681883557204892676718975967265771623672679503776812616315334112382868415864141371197836942710738341439472967902074841400226409659228441303650822610777, 
        # 38470842708546405208704625508367712891471208299333441783981501073550023095274000403563916879641908231011170773153102126825952183163444780110955065002626432268349573, 2998599444966402616240989100234166167805477168671480027322013770416359562366030923233399853472241710845190991402502764232214027989568380839340059)
        plaintext = b"15863274538"
        ciphertext = rsa.encrypt(plaintext, public_key)
        print(ciphertext)
        # b'\x04\xb3ri\x1e\nA\xfb\x94\xff\xde{HtNw\xd4Q{\xdeRJ\xe0Fwl\x97kL\xde\xe6m\xc1\x8f\xd4\t\x96=\xb62\xad\x02\xfe\xeb4\xb4i\x8f\x9e\x0fp\x10\xbe\x8fiNrrUB\xbc\xe3\x87Q-\xe2\xa5\x86\xd9\x0b6,.\x90\xa1\xa6\x80\xf3\xaa\xcc\xdf7!\xdcp\xea\x0eE_?$\x8b\xcd\xb2\xca\x18\xf9e\xb5\x9b^\x84CcU\xe5.\xaeeFlz\xdeh\xb8\xa3D\xcb\xb6\xd5\x02\xe38\x98\xc80#Q'
        plaintext = rsa.decrypt(ciphertext, private_key)
        print(plaintext)
        # b'15863274538'
      
        # 私鑰加簽、公鑰驗签
        plaintext = b"15863274538"
        sign_message = rsa.sign(plaintext, private_key, "MD5")
        plaintext = b"15863274538"
        method = rsa.verify(b"15863274532", sign_message, public_key)
        print(method)
        # rsa.pkcs1.VerificationError: Verification failed
      


▍補充演算法

  • Base64

    • 目前 Base64 已經成爲網路上常見的傳輸 8Bit 位元組程式碼的編碼方式之一。

    • 在做支付系統時,系統之間的報文互動都需要使用 Base64 對明文進行轉碼,然後再進行簽名或加密,之後再進行(或再次 Base64)傳輸。

    • Base64 在 URL、Cookie、網頁傳輸少量二進制檔案中也有相應的使用。

        from base64 import b64encode, b64decode
      
        plaintext = "我的銀行卡密碼是020012"
        ciphertext = b64encode(plaintext.encode())
        print(ciphertext)
        # b'5oiR55qE6ZO26KGM5Y2h5a+G56CB5pivMDIwMDEy'
      
        plaintext = b64decode(ciphertext)
        print(plaintext)
        # b'\xe6\x88\x91\xe7\x9a\x84\xe9\x93\xb6\xe8\xa1\x8c\xe5\x8d\xa1\xe5\xaf\x86\xe7\xa0\x81\xe6\x98\xaf020012'
      
      
        print(plaintext.decode())
        # 我的銀行卡密碼是020012
      

ref

  • https://tw511.com/a/01/8289.html#2_6