python3下常用編解碼與加解密
- 2019 年 10 月 5 日
- 筆記
字符編解碼
Python3相對於Python2的一大改變就是,對默認字符類型進行了修改。Python2中定義字符串默認為二進制字符串,強制加前綴u的才是unicode字符串;而Python3中字符串默認為unicode,強制加前綴b的才是二進制字符串。(也就是剛好反過來了)
這裡的二進制字符串:指的是進行過編碼後的字符串。即utf8、gbk、ascii等編碼後的串都是二進制的。存放到文件的時候也必須是二進制的內容。
unicode字符串:是一種在內存中存在的編碼規範,它可以和任意其它類型的編碼進行轉換。比如:utf-8、gbk、ascii等。並且其它編碼在進行互相轉換時,都必須通過unicode來中轉。該字符串只在內存中存在,沒有具體的編碼實現。
以"中國"為例,來看下各種編碼方式下的表示形式:
"中國" unicode:u4e2du56fd utf8:xe4xb8xadxe5x9bxbd gbk:xd6xd0xb9xfa
可以看到同樣的字符串,在不同的編碼中其對應的值是不一樣的。但是不管哪種編碼都可以轉成unicode,unicode也可以轉成任意其它的編碼。(unicode被稱為萬國碼,是世界上最大的字符集,可以支持編碼全球的語言,但目前在編的並不是全部)
1、編碼
python3中字符編碼很簡單。直接通過encode方法即可。(該方法只有unicode字符對象才有,Python3中unicode是str對象)
s = '中國' # => unicode print(type(s)) # => str s2 = s.encode('utf8') # => utf8 print(type(s2)) # => byte s3 = s.encode('gbk') # => gbk print(type(s3)) # => byte
2、解碼
Python3中解碼用的是decode方法。(該方法只有byte對象才有)
b = b'中國' # => 具體編碼編程環境的默認編碼而定,通常為utf8 print(type(b)) # => byte b1 = b.decode('utf8') # => unicode print(type(b1)) # => str
URL編解碼
1、解析URL字符串
from urllib import parse url = 'https://www.baidu.com/s?wd=python3%20url編碼' parsed = parse.urlparse(url) print(parsed)
urlparse會把完整的URL串解析成各個部分,方便我們直接提取特定內容。其執行結果如下:
ParseResult(scheme='https', netloc='www.baidu.com', path='/s', params='', query='wd=python3%20url編碼', fragment='')
即解析後分解為:協議,域名、路徑、參數、查詢字符串等。我們可以直接使用對應的屬性名來獲取。比如:獲取query內容。
print(parsed.query) # => wd=python3%20url編碼
2、query參數編碼
由於query參數對應的是GET請求時,附件在URL路徑後的查詢參數。為了避免瀏覽器解析到特殊字符而導致不可預知的問題,所以通常需要對其內容進行編碼,稱為URLEncode。Python3中編碼的方式如下:
d = { 'name': '陳霸天', 'sex': 'male', 'age': 18 } query = parse.urlencode(d) print(query) # => name=%E9%99%88%E9%9C%B8%E5%A4%A9&sex=male&age=18
使用urlencode方法,可以很方便的對一個給定的字典對象的鍵值內容進行編碼,並串聯成一個有效格式的query字符串。可以直接附件到URL路徑後來使用。(中間需要?鏈接)
3、query參數解碼
有編碼就有對應的解碼方法,python3中其對應的解碼方法不是urldecode,而是parse_qs方法。具體如下:
query = 'name=%E9%99%88%E9%9C%B8%E5%A4%A9&sex=male&age=18' d = parse.parse_qs(query) print(d) # => {'name': ['陳霸天'], 'sex': ['male'], 'age': ['18']}
注意返回的字典中,其value值都是list對象。這是因為有些鍵可能有多個值的情況。(正常可能會發生的情況)
4、純字符串URL編碼
上面方法是對字典對象進行的編碼,如果只需對單個字符串內容編碼, 則可以使用quote方法。
s = '中國' print(parse.quote(s)) # => %E4%B8%AD%E5%9B%BD
5、純字符串URL解碼
字符串的解碼方法是和編碼方法對應的叫unquote。使用方式如下:
s = '%E4%B8%AD%E5%9B%BD' print(parse.unquote(s)) # => 中國
BASE64編解碼
1、BASE64編碼
BASE64是一種對二進制進行編碼的一種方式;之所以叫BASE64編碼是因為經過這個算法進行編碼之後,其內容只在規定的64個可打印字符之內。(0-9、a-z、A-Z、+、/)。python3中對字符進行BASE64編碼的方式如下:
import base64 src = '中國'.encode('utf8') print("明文:", src) enc = base64.b64encode(src) print("密文:", enc)
這段代碼運行後的結果:
明文: b'xe4xb8xadxe5x9bxbd' 密文: b'5Lit5Zu9'
可以看到b64encode方法接收的是一個二進制類型的字符串,返回的也是一個二進制類型的字符串。
2、BASE64解碼
同編碼相反的就是解碼了,BASE64的解碼也很簡單。
import base64 enc = b'5Lit5Zu9' print("密文:", enc) src = base64.b64decode(enc) print("明文:", src) print("unicode:", src.decode('utf8'))
執行後的代碼如下:
密文: b'5Lit5Zu9' 明文: b'xe4xb8xadxe5x9bxbd' unicode: 中國
3、BASE16編解碼
base64庫除了可以進行BASE64編碼外,還可以支持BASE16編解碼碼。其使用方式如下:
import base64 src = '中國'.encode('utf8') print("明文:", src) enc = base64.b16encode(src) print("密文:", enc) src = base64.b16decode(enc) print("明文:", src) print("unicode:", src.decode('utf8')) 執行結果如下: 明文: b'xe4xb8xadxe5x9bxbd' 密文: b'E4B8ADE59BBD' 明文: b'xe4xb8xadxe5x9bxbd' unicode: 中國
與BASE64編碼後的內容不同,BASE16的編碼後內容其實是有規律的。仔細看下明文和密文,可以發現其實是一種對應關係。比如:明文中的xe4,對應的是密文中的E4;以此類推。即BASE16隻是把二進制在內存中的數值使用16進制字符串來表示而已。
AES加解密
1、AES加密
AES(Advanced Encryption Standard)是一種加密技術和標準。其實DES(Data Encryption Standard)加密的升級版。該加密算法是一種對稱加密,即使用相同的key來進行加密和解密。Python3中進行AES加密的方式如下:
import base64 from Crypto.Cipher import AES def ensure_to_16(value): pad = 16 – len(value) % 16 return value + b'x00' * pad if pad != 16 else value key = b'123456' iv = b'ABCD' src = '中國'.encode('utf8') # AES ECB encrypt aes = AES.new(ensure_to_16(key), AES.MODE_ECB) print('src:', src) enc = aes.encrypt(ensure_to_16(src)) print('enc:', enc) base_str = base64.encodebytes(enc) print('base_str:', base_str) # AES CBC encrypt, need iv arg aes = AES.new(ensure_to_16(key), AES.MODE_CBC, iv=ensure_to_16(iv)) print('src:', src) enc = aes.encrypt(ensure_to_16(src)) print('enc:', enc) base_str = base64.encodebytes(enc) print('base_str:', base_str)
上面的代碼中需要說明的是,ensure_to_16主要作用是把key、iv、src等內容的長度補齊到16的整數倍。這個AES加密的標準要求,其中key、iv還可以是32、48等的整數倍,而src只能是16的整數倍。
另外,上面演示了2種加密類型,第一種是ECB,第二種CBC。其中第一種為不需要iv,第二種需要iv(初始化向量,俗稱鹽)參數。
代碼執行效果如下:
src: b'xe4xb8xadxe5x9bxbd' enc: b'3+z@P4x98x0cx1dx9axce#Fxdfxecx1d' base_str: b'Myt6QFA0mAwdms4jRt/sHQ==n' src: b'xe4xb8xadxe5x9bxbd' enc: b'4xd7xa42U:Yzxc7>xba\xb2x96x81n' base_str: b'NNekMlU6WXrHPrpcspaBCg==n'
2、AES解密
AES解碼時需要使用相同的key、iv和模式,有一個不一致都會導致解密失敗。具體代碼如下:
import base64 from Crypto.Cipher import AES def ensure_to_16(value): pad = 16 – len(value) % 16 return value + b'x00' * pad if pad != 16 else value key = b'123456' iv = b'ABCD' b64_str = b'NNekMlU6WXrHPrpcspaBCg==n' # AES CBC decrypt, need iv arg aes = AES.new(ensure_to_16(key), AES.MODE_CBC, iv=ensure_to_16(iv)) print('b64_str:', b64_str) enc = base64.decodebytes(b64_str) print('enc:', enc) src = aes.decrypt(enc) print('src:', src)
上面代碼以CBC模式為例,對密文進行了解密。其執行的結果如下:
b64_str: b'NNekMlU6WXrHPrpcspaBCg==n' enc: b'4xd7xa42U:Yzxc7>xba\xb2x96x81n' src: b'xe4xb8xadxe5x9bxbdx00x00x00x00x00x00x00x00x00x00'
可以看到明文src中的內容是以x00補齊的,具體操作時還需要對其進行處理。比如:
src = b'xe4xb8xadxe5x9bxbdx00x00x00x00x00x00x00x00x00x00' print(src.strip(b'x00').decode('utf8')) # => 中國