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')) # => 中國