1. 程式人生 > >常見加密方式和Python實現

常見加密方式和Python實現

前言

我們所說的加密方式,都是對二進位制編碼的格式進行加密的,對應到Python中,則是我們的Bytes

所以當我們在Python中進行加密操作的時候,要確保我們操作的是Bytes,否則就會報錯。

將字串和Bytes互相轉換可以使用encode()decode()方法。如下所示:

# 方法中不傳引數則是以預設的utf-8編碼進行轉換
In [1]: '南北'
Out[1]: b'\xe5\x8d\x97\xe5\x8c\x97'
In [2]: b'\xe5\x8d\x97\xe5\x8c\x97'
Out[2]:'南北'

注:兩位十六進位制常常用來顯示一個二進位制位元組。

利用binascii

模組可以將十六進位制顯示的位元組轉換成我們在加解密中更常用的顯示方式:

In [1]: import binascii

In [2]: '南北'.encode()
Out[2]: b'\xe5\x8d\x97\xe5\x8c\x97'

In [3]: binascii.b2a_hex('南北'.encode())
Out[3]: b'e58d97e58c97'

In [4]: binascii.a2b_hex(b'e58d97e58c97')
Out[4]: b'\xe5\x8d\x97\xe5\x8c\x97'

In [5]: binascii.a2b_hex(b'e58d97e58c97'
).decode() Out[5]: '南北'

URL編碼

簡介

正常的URL中是隻能包含ASCII字元的,也就是字元、數字和一些符號。而URL編碼就是一種瀏覽器用來避免url中出現特殊字元(如漢字)的編碼方式。

其實就是將超出ASCII範圍的字元轉換成帶%的十六進位制格式。

Python實現

In [1]: from urllib import parse

# quote()方法會自動將str轉換成bytes,所以這裡傳入str和bytes都可以
In [2]: parse.quote('南北')
Out[2]: '%E5%8D%97%E5%8C%97'

In [3]: parse.
unquote('%E5%8D%97%E5%8C%97') Out[3]: '南北'

Base64編碼

簡述

Base64是一種用64個字元來表示任意二進位制資料的方法。

Base64編碼可以成為密碼學的基石。可以將任意的二進位制資料進行Base64編碼。所有的資料都能被編碼為並只用65個字元就能表示的文字檔案。( 65字元:A~Z a~z 0~9 + / = )編碼後的資料~=編碼前資料的4/3,會大1/3左右。

Base64編碼的原理

image.png
  1. 將所有字元轉化為ASCII碼。
  2. 將ASCII碼轉化為8位二進位制 。
  3. 將二進位制3個歸成一組(不足3個在後邊補0)共24位,再拆分成4組,每組6位。
  4. 統一在6位二進位制前補兩個0湊足8位。
  5. 將補0後的二進位制轉為十進位制。
  6. 從Base64編碼表獲取十進位制對應的Base64編碼。

Base64編碼的說明

  1. 轉換的時候,將三個byte的資料,先後放入一個24bit的緩衝區中,先來的byte佔高位。
  2. 資料不足3byte的話,於緩衝區中剩下的bit用0補足。然後,每次取出6個bit,按照其值選擇查表選擇對應的字元作為編碼後的輸出。
  3. 不斷進行,直到全部輸入資料轉換完成。
  4. 如果最後剩下兩個輸入資料,在編碼結果後加1個“=”。
  5. 如果最後剩下一個輸入資料,編碼結果後加2個“=”。
  6. 如果沒有剩下任何資料,就什麼都不要加,這樣才可以保證資料還原的正確性。

Python的Base64使用

Python內建的base64模組可以直接進行base64的編解碼

注意:用於base64編碼的,要麼是ASCII包含的字元,要麼是二進位制資料

In [1]: import base64

In [2]: base64.b64encode(b'hello world')
Out[2]: b'aGVsbG8gd29ybGQ='

In [3]: base64.b64decode(b'aGVsbG8gd29ybGQ=')
Out[3]: b'hello world'

MD5(資訊-摘要演算法)

簡述

message-digest algorithm 5(資訊-摘要演算法)。經常說的“MD5加密”,就是它→資訊-摘要演算法。

md5,其實就是一種演算法。可以將一個字串,或檔案,或壓縮包,執行md5後,就可以生成一個固定長度為128bit的串。這個串,基本上是唯一的。

不可逆性

每個人都有不同的指紋,看到這個人,可以得出他的指紋等資訊,並且唯一對應,但你只看一個指紋,是不可能看到或讀到這個人的長相或身份等資訊。

特點

  1. 壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
  2. 容易計算:從原資料計算出MD5值很容易。
  3. 抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
  4. 強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。

舉個栗子:世界上只有一個我,但是但是妞卻是非常非常多的,以一個有限的我對幾乎是無限的妞,所以可能能搞定非常多(100+)的妞,這個理論上的確是通的,可是實際情況下....

Python的MD5使用

由於MD5模組在python3中被移除,在python3中使用hashlib模組進行md5操作

import hashlib

# 待加密資訊
str = '這是一個測試'

# 建立md5物件
hl = hashlib.md5()

# 此處必須宣告encode
# 若寫法為hl.update(str)  報錯為: Unicode-objects must be encoded before hashing
hl.update(str.encode(encoding='utf-8'))

print('MD5加密前為 :' + str)
print('MD5加密後為 :' + hl.hexdigest())

執行結果

MD5加密前為 :這是一個測試
MD5加密後為 :cfca700b9e09cf664f3ae80733274d9f

MD5長度

md5的長度,預設為128bit,也就是128個0和1的二進位制串。這樣表達是很不友好的。所以將二進位制轉成了16進位制,每4個bit表示一個16進位制,所以128/4 = 32 換成16進製表示後,為32位了。

為什麼網上還有md5是16位的呢?

其實16位的長度,是從32位md5值來的。是將32位md5去掉前八位,去掉後八位得到的。

Python加密庫PyCryptodome

PyCrypto是 Python 中密碼學方面最有名的第三方軟體包。可惜的是,它的開發工作於2012年就已停止。

幸運的是,有一個該專案的分支PyCrytodome 取代了 PyCrypto 。

安裝與匯入

安裝之前需要先安裝Microsoft Visual c++ 2015

在Linux上安裝,可以使用以下 pip 命令:

pip install pycryptodome

匯入:

import Crypto

在Windows 系統上安裝則稍有不同:

pip install pycryptodomex

匯入:

import Cryptodome

DES

簡介

DES演算法為密碼體制中的對稱密碼體制,又被稱為美國資料加密標準。

DES是一個分組加密演算法,典型的DES以64位為分組對資料加密,加密和解密用的是同一個演算法。

DES演算法的入口引數有三個:Key、Data、Mode。其中Key為7個位元組共56位,是DES演算法的工作金鑰;Data為8個位元組64位,是要被加密或被解密的資料;Mode為DES的工作方式,有兩種:加密或解密。

金鑰長64位,金鑰事實上是56位參與DES運算(第8、16、24、32、40、48、56、64位是校驗位,使得每個金鑰都有奇數個1),分組後的明文組和56位的金鑰按位替代或交換的方法形成密文組。

例子

# 匯入DES模組
from Cryptodome.Cipher import DES
import binascii

# 這是金鑰 key = b’abcdefgh’ # 需要去生成一個DES物件 des = DES.new(key, DES.MODE_ECB) # 需要加密的資料 text = ‘python spider!’ text = text + (8 - (len(text) % 8)) * ‘=’

# 加密的過程 encrypto_text = des.encrypt(text.encode()) encrypto_text = binascii.b2a_hex(encrypto_text) print(encrypto_text)

3DES

簡介

3DES(或稱為Triple DES)是三重資料加密演算法(TDEA,Triple Data Encryption Algorithm)塊密碼的通稱。它相當於是對每個資料塊應用三次DES加密演算法。

由於計算機運算能力的增強,原版DES密碼的金鑰長度變得容易被暴力破解。3DES即是設計用來提供一種相對簡單的方法,即通過增加DES的金鑰長度來避免類似的攻擊,而不是設計一種全新的塊密碼演算法。

3DES(即Triple DES)是DES向AES過渡的加密演算法(1999年,NIST將3-DES指定為過渡的加密標準),加密演算法,其具體實現如下:設Ek()和Dk()代表DES演算法的加密和解密過程,K代表DES演算法使用的金鑰,M代表明文,C代表密文,這樣:

3DES加密過程為:C=Ek3(Dk2(Ek1(M)))

3DES解密過程為:M=Dk1(EK2(Dk3(C)))

AES

簡介

高階加密標準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣為全世界所使用。經過五年的甄選流程,高階加密標準由美國國家標準與技術研究院(NIST)於2001年11月26日釋出於FIPS PUB 197,並在2002年5月26日成為有效的標準。2006年,高階加密標準已然成為對稱金鑰加密中最流行的演算法之一。

AES在軟體及硬體上都能快速地加解密,相對來說較易於實作,且只需要很少的儲存器。作為一個新的加密標準,目前正被部署應用到更廣大的範圍。

特點與思想

  1. 抵抗所有已知的攻擊。
  2. 在多個平臺上速度快,編碼緊湊。
  3. 設計簡單。

詳解

image.png

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

一般常用的是128位

Python實現

from Cryptodome.Cipher import AES
from Cryptodome import Random
from binascii import b2a_hex  

# 要加密的明文 data = ‘南來北往’ # 金鑰key 長度必須為16(AES-128)、24(AES-192)、或32(AES-256)Bytes 長度. # 目前AES-128足夠用 key = b’this is a 16 key’ # 生成長度等於AES塊大小的不可重複的金鑰向量 iv = Random.new().read(AES.block_size)

# 使用key和iv初始化AES物件, 使用MODE_CFB模式 mycipher = AES.new(key, AES.MODE_CFB, iv) # 加密的明文長度必須為16的倍數,如果長度不為16的倍數,則需要補足為16的倍數 # 將iv(金鑰向量)加到加密的密文開頭,一起傳輸 ciphertext = iv + mycipher.encrypt(data.encode())

# 解密的話要用key和iv生成新的AES物件 mydecrypt = AES.new(key, AES.MODE_CFB, ciphertext[:16]) # 使用新生成的AES物件,將加密的密文解密 decrypttext = mydecrypt.decrypt(ciphertext[16:])

print(‘金鑰k為:’, key) print(‘iv為:’, b2a_hex(ciphertext)[:16]) print(‘加密後資料為:’, b2a_hex(ciphertext)[16:]) print(‘解密後資料為:’, decrypttext.decode())

執行結果:

金鑰k為: b'this is a 16 key'
iv為: b'a78a177cffd50878'
加密後資料為: b'33f61e7678c25d795d565d40f2f68371da051202'
解密後資料為: 南來北往

RSA

非對稱加密

典型的如RSA等,常見方法,使用openssl ,keytools等工具生成一對公私鑰對,使用被公鑰加密的資料可以使用私鑰來解密,反之亦然(被私鑰加密的資料也可以被公鑰解密) 。

在實際使用中私鑰一般儲存在釋出者手中,是私有的不對外公開的,只將公鑰對外公佈,就能實現只有私鑰的持有者才能將資料解密的方法。 這種加密方式安全係數很高,因為它不用將解密的金鑰進行傳遞,從而沒有金鑰在傳遞過程中被截獲的風險,而破解密文幾乎又是不可能的。

但是演算法的效率低,所以常用於很重要資料的加密,常和對稱配合使用,使用非對稱加密的金鑰去加密對稱加密的金鑰。

簡介

RSA加密演算法是一種非對稱加密演算法。在公開金鑰加密和電子商業中RSA被廣泛使用。

該演算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可釋出的供任何人使用,私鑰則為自己所有,供解密之用。

Python實現

首先我們需要安裝一個rsa模組:

pip install rsa

而且,因為RSA加密演算法的特性,RSA的公鑰私鑰都是10進位制的,但公鑰的值常常儲存為16進位制的格式,所以需要將其用int()方法轉換為10進位制格式。

用網頁中的公鑰把資料加密

import rsa
import binascii

# 使用網頁中獲得的n和e值,將明文加密 def rsa_encrypt(rsa_n, rsa_e, message): # 用n值和e值生成公鑰 key = rsa.PublicKey(rsa_n, rsa_e) # 用公鑰把明文加密 message = rsa.encrypt(message.encode(), key) # 轉化成常用的可讀性高的十六進位制 message = binascii.b2a_hex(message) # 將加密結果轉化回字符串並返回 return message.decode()

# RSA的公鑰有兩個值n和e,我們在網站中獲得的公鑰一般就是這樣的兩個值。 # n常常為長度為256的十六進位制字串 # e常常為十六進位制‘10001’ pubkey_n = ‘8d7e6949d411ce14d7d233d7160f5b2cc753930caba4d5ad24f923a505253b9c39b09a059732250e56c594d735077cfcb0c3508e9f544f101bdf7e97fe1b0d97f273468264b8b24caaa2a90cd9708a417c51cf8ba35444d37c514a0490441a773ccb121034f29748763c6c4f76eb0303559c57071fd89234d140c8bb965f9725’ pubkey_e = ‘10001’ # 需要將十六進位制轉換成十進位制 rsa_n = int(pubkey_n, 16) rsa_e = int(pubkey_e, 16) # 要加密的明文 message = ‘南北今天很忙’

print(“公鑰n值長度:”, len(pubkey_n)) print(rsa_encrypt(rsa_n, rsa_e, message))

執行結果:

公鑰n值長度: 256
480f302eed822c8250256511ddeb017fcb28949cc05739ae66440eecc4ab76e7a7b2f1df398aefdfef2b9bfce6d6152bf6cc1552a0ed8bebee9e094a7ce9a52622487a6412632144787aa81f6ec9b96be95890c4c28a31b3e8d9ea430080d79297c5d75cd11df04df6e71b237511164399d72ccb2f4c34022b1ea7b76189a56e
      <iframe id="tmp_downloadhelper_iframe" style="display: none;"></iframe></div>