Pythonでのデジタル署名(署名と検証、暗号化と復号化)

■パッケージcryptographyを使ったサンプル。
Androidの配布についてデジタル署名が出てきたので、少しおさらい。Pythonのパッケージcryptographyでデジタル署名が利用できるようなので、署名と検証、暗号化と復号化を見てみる。

サンプルコードが下のもの。
暗号鍵の作成、署名と検証のコード、暗号化と復号化のコードで、適当にprintで結果をコンソール表示している。

import base64
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# ==============================================================
# 暗号鍵(private key)の作成
private_key_sample033 = rsa.generate_private_key(
    public_exponent=3,
    key_size=1024,
)
print(private_key_sample033.key_size)
# ==============================================================

# ==============================================================
# 暗号鍵による署名と公開鍵(public key)による検証、否認防止
sign_text = b"This is text for signature."
signature = private_key_sample033.sign(
    sign_text,
    # 暗号鍵による署名と公開鍵(public key)による検証
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

sign_text2 = b"This is text for signature2."
print("署名: " + str(signature))
public_key = private_key_sample033.public_key()
public_key.verify(
    signature,
    sign_text,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)
print("\n")
# ==============================================================

# ==============================================================
# メッセージ認証
plain_text = "暗号化テキスト"
print("元の文:\t\t\t\t\t" + plain_text)
plain_text_byte = base64.b64encode(plain_text.encode())
print("元の文のバイナリデータ:\t" + str(plain_text_byte))

public_key = private_key_sample033.public_key()
crypted_text = public_key.encrypt(plain_text_byte,
                                  padding.OAEP(
                                      mgf=padding.MGF1(algorithm=hashes.SHA256()),
                                      algorithm=hashes.SHA256(),
                                      label=None
                                  ))

print("暗号化の文:\t" + str(crypted_text))

decrypted_text_byte = private_key_sample033.decrypt(crypted_text,
                                                    padding.OAEP(
                                                        mgf=padding.MGF1(algorithm=hashes.SHA256()),
                                                        algorithm=hashes.SHA256(),
                                                        label=None
                                                    ))
print("復号したバイナリデータ:\t" + str(decrypted_text_byte))
decrypted_text = base64.b64decode(decrypted_text_byte).decode()
print("戻した文:\t\t\t\t" + decrypted_text)
# ==============================================================


秘密鍵の作成では、公開指数と鍵のサイズを指定。公開指数は65537(2の16乗+1)がデフォルトのよう。適当な数字を入れると、ValueError: public_exponent must be either 3 (for legacy compatibility) or 65537. Almost everyone should choose 65537 here! とのエラーが出る。3でもできた。

秘密鍵は、所有者が厳密に保管するため、例えば、所有者のデータをこれで署名して配布する場合、その署名されたデータが公開鍵で検証できれば、所有者本人のデータと分かる。もしデータが改ざんされていれば、検証でエラーが出るはず。上のコードで、sign_textを署名した上で検証時にsign_text2を指定すると、cyptography.exceptions.InvalidSignature のエラーが出る。

暗号化・復号化では、送信側から公開鍵で暗号化したメッセージを送信、受信側が秘密鍵で復号する。上のコードの実行結果が下。日本語のままでは暗号化できなかったので一端バイナリデータへ変換している。


公開鍵は誰でもアクセスできるので、購買者から送信される情報を暗号化して送信、通販ウェブサイトの管理者とか(秘密鍵の所有者)が復号化といった使い方になると思う。もし、暗号化された情報が漏れても、秘密鍵をもたない第3者では復号化することはできないため内容が漏れることはない。

Pythonを使ったちょっとした試し。