常見加密方式的Python實現

常見加密方式的Python實現

1. 前言

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

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

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

# 方法中不傳參數則是以默認的utf-8編碼進行轉換
enc = 'PyJun'.encode()
print(enc)
print(enc.decode())

# b'PyJun'
# PyJun

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

利用binascii模塊可以將十六進制顯示的字節轉換成我們在加解密中更常用的顯示方式:

import binascii

info = binascii.b2a_hex(b'PyJun')
print(info)
print(binascii.a2b_hex(info))

# b'50794a756e'
# b'PyJun'

2. URL編碼

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

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

例子

from urllib import parse

info = parse.quote("PyJun到此一遊")
print(info)
print(parse.unquote(info))

# PyJun%E5%88%B0%E6%AD%A4%E4%B8%80%E6%B8%B8
# PyJun到此一遊

3. Base64編碼

Base64是一種用64個字符來表示任意二進制數據的方法。

Base64編碼可以稱爲密碼學的基石。可以將任意的二進制數據進行Base64編碼。所有的數據都能被編碼爲並只用65個字符就能表示的文本文件。( 65字符:A~Z a~z 0~9 + / = )編碼後的數據~=編碼前數據的4/3,會大1/3左右。

3.1. 原理

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

3.2. 說明

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

3.3. python使用

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'

4. MD5(信息-摘要算法)

message-digest algorithm 5(信息-摘要算法)。經常說的“MD5加密”,就是信息摘要算法。

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

4.1. 特點

  1. 壓縮性:任意長度的數據,算出的MD5值長度都是固定的。
  2. 容易計算:從原數據計算出MD5值很容易。
  3. 抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。
  4. 強抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即僞造數據)是非常困難的。
  5. 不可逆性:每個人都有不同的指紋,看到這個人,可以得出他的指紋等信息,並且唯一對應,但你只看一個指紋,是不可能看到或讀到這個人的長相或身份等信息。

4.2. python使用

由於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的長度,默認爲128bit,也就是128個0和1的二進制串。這樣表達是很不友好的。所以將二進制轉成了16進制,每4個bit表示一個16進制,所以128/4 = 32 換成16進製表示後,爲32位了。

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

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

5. AES

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

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

5.1. 特點

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

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

一般常用的是128位

5.2. Python實現

PyCrypto是 Python 中密碼學方面最有名的第三方軟件包,提供了許多加密算法的使用。可惜的是,它的開發工作於2012年就已停止。

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

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

pip install pycryptodome

導入:

import Crypto

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

pip install pycryptodomex

導入:

import Cryptodome

代碼示例:

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

# 要加密的明文
data = "I'm PyJun"
# 密鑰key 長度必須爲16(AES-128)、24(AES-192)、或32(AES-256)Bytes 長度.
# 目前AES-128足夠用
key = b'PyJun'
key = key[:32] + (32-len(key[:32]))*b' '
# 生成長度等於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'PyJun                           '
iv爲: b'c4b5667e86a7cd00'
加密後數據爲: b'372ea7889776b6c649d11de4c25de9eefe'
解密後數據爲: I'm PyJun

6. RSA

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

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

6.1. 非對稱加密

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

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

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

6.2. Python實現

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

pip install rsa

代碼示例

import rsa 

publickey,privatekey = rsa.newkeys(1024)  # 加密得到公鑰和私鑰
pub = publickey.save_pkcs1()#獲取公鑰
#將公鑰保存到文件*************
filepub = open("public.pem",'wb')
filepub.write(pub)
filepub.close()

pri = privatekey.save_pkcs1()#獲取私鑰
#將私鑰保存到文件***********
filepri = open('private.pem','wb')
filepri.write(pri)
filepri.close()

content = b"PyJun"  #待加密的字符串

#取出公鑰
with open('public.pem','rb') as file_pub:
     f_pub = file_pub.read()
     pubkey = rsa.PublicKey.load_pkcs1(f_pub)

#取出私鑰
with open('private.pem','rb') as file_pri:
     f_pri =file_pri.read()
     prikey = rsa.PrivateKey.load_pkcs1(f_pri)

#加密字符串string
crypt = rsa.encrypt(content, pubkey)#使用公鑰去加密字符串
#解密
de_crypt = rsa.decrypt(crypt,prikey)#用私鑰去解密

print(crypt)
print(de_crypt)
#解出來的de_crypt與content應該是相等的,判斷一下
assert content == de_crypt

運行結果:

b'u\x1fy\\/b\x87O\x9c\x86\x13\x16\xabti\xa4\xf7\xba\xd2\xe0bD\x02\xb9\x7fi\xd5\xeb\xc6\x9a\xc8f\x8c\xd5NoA\x17P\x97\xf8\xfe\xb0%\xd2*\x8fK\x03\x9c\xd0\xb9\x9a\x93@\xbec\xd7\xd5;M\x92\x1bq\xb7`l\x8b\xf9\xb9\xa9\x88\x01\x96\x98\xc1\x17\xcb\x12\xce\xd2\xe7^M\xa5\xbfAq\x94\x0e)\xaf\xc0\xdb\xc9\x7f\ngT\x16\xd5YnND\xd85\xf0}\xc3\xd3\x12ovYp\xfdQ\\`<\xb4Q\xa0|a\xcd\xb9'
b'PyJun'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章