python (3.x) 實現RSA 加簽 驗籤 以及key的序列化

EthanSun發表於2019-02-16

首先 安裝cryptography

sudo pip3 install cryptography

確認安裝的是2.1.x版本 (1.x版本的api是不一樣的).

生成公私鑰對:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from base64 import b64encode, b64decode

# 生成私鑰 (同時包含公鑰), 此處為RSA 2048
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

序列化公鑰 私鑰, 並寫入檔案儲存.


# 序列化私鑰
pem = private_key.private_bytes(
   encoding=serialization.Encoding.PEM,
   format=serialization.PrivateFormat.PKCS8,
   # 無密碼
   encryption_algorithm=serialization.NoEncryption()
   # 也可以加入密碼保護私鑰:
   # encryption_algorithm=serialization.BestAvailableEncryption(b`mypassword`)
)

# 將私鑰寫入檔案
with open(`private.pem`, `wb`) as f:
    f.write(pem)

public_key = private_key.public_key()

pub_pem = public_key.public_bytes(
   encoding=serialization.Encoding.PEM,
   format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 將公鑰寫入檔案
with open(`public.pem`, `wb`) as f:
    f.write(pub_pem)

也可以採用其他序列化方法, 比如對公鑰, 生成OpenSSH格式的序列化字串:

public_key.public_bytes(
    encoding=serialization.Encoding.OpenSSH,
    format=serialization.PublicFormat.OpenSSH
)

輸出類似於:

b`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCbySUldhJYrDmf8+yyQXMB5HsIMwownZq3ft7k1nei/kVC/720np/1xyk7U8yNc2jL9Yb/pctD/0WAnI7vrkIVQniYfRwiEohDlZHGIvlUpXS4dtKvbOFRDQod9nn4Q9VwfrMq4yuBbKSaCqwcu8rkApWyj36
XhTV5/mT7Zs4H4m3OAyCVD2kkAoZYYbxA+pBNDLDcU4VlUuiC1FaWLTdU5dV6A0IGRGlOuFw4qaJZaFLu6mANaRdX836LA5DOmKpcwerb0ae3eGHZNSLQO6JkIT7oe/I+417jz5JmUI3V35BjKUrd7VjBgbE0jvpkxrzMFCaoOWP0BzTDn+C64fSR`

此後可以從檔案讀出公鑰(私鑰)

# 從檔案讀出並建立公鑰
with open("public.pem", "rb") as pub_key_file:
    # 讀入私鑰為 load_pem_private_key
    public_key = serialization.load_pem_public_key(
        pub_key_file.read(),
        # 讀入私鑰此處還有: password=b`xxxx`,
        backend=default_backend()
    )

對於openSSH格式, 應採用load_ssh_public_key

給指定訊息加簽, 獲得簽名

# 需要加簽的訊息
message = b"A message I want to sign"
# 對訊息加簽, 獲得簽名, 為bytes
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA1()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA1()
)

# 需要傳輸, 展示, 儲存時, 可以將簽名轉為base64格式的bytes,
# 也可以繼續轉為str: str(enc_sig, `utf-8`)
enc_sig = b64encode(signature)

驗證簽名:

# 通過signature, 公鑰 給 訊息message 驗籤, 驗證是由私鑰持有者發出的這條訊息.
# 如果驗籤通過, 則正常通過, 否則, 丟擲 cryptography.exceptions.InvalidSignature 異常
public_key.verify(
    signature,
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA1()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA1()
)

這樣生成的簽名base64encode之後, 會有344byte 的長度.

相關文章