pythonで暗号演算 ~RSAのパディング~

python,暗号演算,python,RSA,暗号演算

今回はRSAの各種パディング方法について調べてみました。

また、pythonのpycryptodomeライブラリを使用して、実際に各パディング処理を動かしてみます。

概要

RSAはAESと同様にブロック暗号方式です。

そのため、暗号演算時のブロック長は鍵長に依存しパディングが必要となります。

PKCS#1(RFC8017)にて規定されている各パディング方式は以下になります。

*PKCS = Public Key Cryptographic Standards (公開鍵暗号標準)

パディング方式用途概要
PKCS1-v1_5非対称暗号化/
デジタル署名
非対称暗号、デジタル署名両方に用いられるパディング方式。
特定の平文とキーに対して毎回異なる暗号文を生成する。(乱数を使用)
特定のメッセージと鍵値から、同一の署名値を生成する。
OAEP非対称暗号化システム間でデータをやり取りする際に用いられるパディング方式。
特定の平文とキーに対して毎回異なる暗号文を生成する。(乱数を使用)
PKCS1-v1_5よりもセキュリティ強度が高い。
PSSデジタル署名デジタル署名のハッシュ値生成時に用いられるパディング方式。
特定のハッシュ値とキーに対して毎回異なる署名値を生成する。(ソルト値を使用)
PKCS1-v1_5よりもセキュリティ強度が高い。

特にデジタル署名で扱うパディング方式はRSASSA(Signature Scheme with Appendix)という表現を用いて、RSASSA-PKCS1-v1_5RSASSA-PSSという表現をされたりします。

TLS1.2まではRSASSA-PKCS1-v1_5がRSAの電子署名において使用されておりましたが、TLS1.3からはRSASSA-PSSの導入が必須とされました。

RSAの各パディング処理実行

pycryptodomeを使用して、各種RSAのパディング処理を出力してみました。

ライブラリの使用方法については以下から確認お願いします。
(左の検索ボックスに、OAEPなどのキーワードを入力すると見つかります)

ソース

#!/user/env python3
# -*- coding: utf-8 -*-

from Crypto.PublicKey import RSA
from Crypto import Random
from Crypto.Cipher import PKCS1_v1_5
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Random import get_random_bytes
from Crypto.Signature import pss
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
import time


# RSAの鍵生成
random_func = Random.new().read
rsa = RSA.generate(2048, random_func)
private_pem = rsa.exportKey(format='PEM')
private_key = RSA.importKey(private_pem)
public_pem = rsa.publickey().exportKey()
public_key = RSA.importKey(public_pem)


def main():
    # 初期設定
    start = time.time()*1000000
    message = b"test data 16byte"

    # PKCS1-v1_5による暗復号
    for i in range(1,3):
        cipher_text = cipher_pkcs1_v1_5(public_key,message)
        print("PKCS1-v1_5による暗号化 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , len(cipher_text) ,cipher_text.hex())
        decipher_text = decipher_pkcs1_v1_5(private_key,cipher_text)
        print("PKCS1-v1_5による復号化 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , decipher_text)

    # OAEPによる暗復号
    for i in range(1,3):
        cipher_text = cipher_oaep(public_key,message)
        print("OAEPによる暗号化 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , len(cipher_text) ,cipher_text.hex())
        decipher_text = decipher_oaep(private_key,cipher_text)
        print("OAEPによる復号化 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , decipher_text)

    # PKCS1-v1_5による署名作成、検証
    for i in range(1,3):
        signature = signature_pkcs1_15(private_key,message)
        print("PKCS1-v1_5による署名作成 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , len(signature) ,signature.hex())
        rtn = chk_signature_pkcs1_15(public_key,message,signature)
        print("PKCS1-v1_5による署名検証 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , rtn)

    # PSSによる署名作成、検証
    for i in range(1,3):
        signature = signature_pss(private_key,message)
        print("PSSによる署名作成 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , len(signature) ,signature.hex())
        rtn = chk_signature_pss(public_key,message,signature)
        print("PSSによる署名検証 ({}回目)".format(i), int(time.time()*1000000-start), 'μSec' , rtn)


# PKCS1-v1_5による暗号化
def cipher_pkcs1_v1_5(key,message):
    cipher = PKCS1_v1_5.new(key)
    cipher_text = cipher.encrypt(message)
    return cipher_text


# PKCS1-v1_5による復号化
def decipher_pkcs1_v1_5(key,cipher_text):
    sentinel = get_random_bytes(16)
    cipher = PKCS1_v1_5.new(key)
    message = cipher.decrypt(cipher_text,sentinel)
    return message


# OAEPによる暗号化
def cipher_oaep(key,message):
    cipher = PKCS1_OAEP.new(key)
    cipher_text = cipher.encrypt(message)
    return cipher_text


# OAEPによる復号化
def decipher_oaep(key,cipher_text):
    decipher = PKCS1_OAEP.new(key)
    message = decipher.decrypt(cipher_text)
    return message


# PKCS1-v1_5による署名作成
def signature_pkcs1_15(key,message):
    hash = SHA256.new(message)
    signature = pkcs1_15.new(key).sign(hash)
    return signature


# PKCS1-v1_5による署名検証
def chk_signature_pkcs1_15(key,message,signature):
    hash = SHA256.new(message)
    verifier = pkcs1_15.new(key)
    try:
        verifier.verify(hash, signature)
        rtn = "検証結果OK"
    except (ValueError, TypeError):
        rtn = "検証結果NG"
    return rtn


# PSSによる署名作成
def signature_pss(key,message):
    hash = SHA256.new(message)
    signature = pss.new(key).sign(hash)
    return signature


# PSSによる署名検証
def chk_signature_pss(key,message,signature):
    hash = SHA256.new(message)
    verifier = pss.new(key)
    try:
        verifier.verify(hash, signature)
        rtn = "検証結果OK"
    except (ValueError, TypeError):
        rtn = "検証結果NG"
    return rtn


if __name__ == '__main__':
	main()

出力結果

以下がソースの出力結果で、いずれも出力値が鍵長の2042bit(256bytes)となっています。

また、RSASSA-PKCS1-v1_5によって生成された署名値だけが、1回目2回目ともに同じ値です。

PKCS1-v1_5による暗号化 (1回目) 969 μSec 256 a022e0450390f9ab656200edf285aba9d3e25ea08ec0596f165612a78b1a17cf81b44823f5bd0bb61abbec26a7472af4352d1a1ac9667544376253d24e75d3a3b7ee45ef625cb822806f5d5deb9f15973e71dfc97ac7d7b71cad1da8f3cca6e4636a9a604e84690cb48db1ddb6ffcbc46e2410d43c7d0732c978ab4ebf0dd00bd977136fdc990b132e73a14ffdae4be74e5e7e3163a7e704a557df1e0c63db30f99a474c2bc99e1928d246cfca4e083e081a21791c32d0fcd86356e6743961fe53d0a8b174eddf8cf5d6875c88023db33de525c0463849aebf2f39dcf7eadf47add0ede0c6a50c7a727bf7c33d9656ebeaf794f574e11bc547e523e4ef90aaf7
PKCS1-v1_5による復号化 (1回目) 4987 μSec b'test data 16byte'
PKCS1-v1_5による暗号化 (2回目) 5970 μSec 256 efaa2b70f8b346ad21b95d1b7cde720892b9dc8f9530a7fe0d73806c4c7156f911e09caec4246fb0966f8676f0919cf860298fa2d66f9bc1cfd0f16391d4f3fda2af63af97a7df6a10671c03a6666dd4038735d51f72027b405d89dd96f8558f8013eb118c8c0157bd86b077c767bea0b780b048ba401708558098c681fd42461e9487fce1f2997827ced386472b4481d0e208b82075a50f98754f4c0bd6277de70cce32bf2a9da92ad32494807b67e6afcd4eb1a1eca32694389040a9fea2e18899ac470bc5f98d20253a1286c2cc0c7692c64ea30ccd18d14208c3d3f4347053682ead2d4e177e8bfb1293c8809832a9649683b638188afe7b4cb84787d74f
PKCS1-v1_5による復号化 (2回目) 9972 μSec b'test data 16byte'
OAEPによる暗号化 (1回目) 11958 μSec 256 1555cca6d36ec8bcdea9a4a3b1e4e5215042be696fe1f7437a04c34eb8a34cb78161fd52f8d5c0d31345f9a31afba07ff7a7e340b9259e53eba1ace260918a7c985c2427cfe05a2bce239cb7c93b958fb31e51ba006b095fc6233aa0122c0f95d5f65c50deb3b38152189a2a84220fc4dbb14b5dc5893c8f025c0e72acb5d7d7e2ea74452b70b37de806b6183a733caef45fe6eed8a837d5dc93d47b3d547e513cf40db28039f3752ea13f1f0b17b4efa2c981517cb1dff57d40532be81f811cd0b3be508a7db3948512130d84a00bc85eeb4600ef9600a4ad1327cce6fadce47fc7d63a5bca7da120b99438d16fd59486ef842b5baff12cd3bacefca1d676d3
OAEPによる復号化 (1回目) 19918 μSec b'test data 16byte'
OAEPによる暗号化 (2回目) 20915 μSec 256 67eb73a5f13a869fe61f83b498fa3e9dc1c7e3b55277677ce9cb69d33d993b4ee9b7da1a45c3779559311890018a6de9784a6108e1c24f16a271caeeac9fbbbfae7f90d4931d7d263ee51b71f4636e407f8c18839dba199819a17e0e2ac41690993cd19b47036836ae21a5cd9b10842e6da774cc1ce23d84bdd0a29cf63b086b0acf2089ccb84cbf07d9e538614e79f7780cc54bdaff35fe2f88b0159d99537761aefd5ec3dcf32fd231a919f7202d89606c8f06dcaa3c380fe9d3501235f0b522dcd4b81bdf09cebd5a49a08cd9416ba35277a2e2fd1808f8a467f6246cd9a5835072044e1260e46a317a7c843dac4178efe161ca0276c60ba13d0c29077f2b
OAEPによる復号化 (2回目) 24904 μSec b'test data 16byte'
PKCS1-v1_5による署名作成 (1回目) 28894 μSec 256 105f12779de942652f4d47c661b309b428f2b540cbc008bec2cf9153d421a7c397fb9ed82af7c583a2e3e821700c71ff2dad628709b264c9c0e3d328f088a088b04c17feb2344d5f29bb8d984632af63cff3637152e7f9b2eaa5680f9e0e46b4dc8c804dfa397ef4335bf87ea569e62302c2c134bc8607bccba740add2bef07956a4e16c9f2b30dad194132dbf97a8a7cf510efef59166f51bbf80d2188345da977bd5f265c23fee6b9379e9d88118c1f8ae140172a4f12d543d3ded8639acecf1fbc3b61c7551ef2691a288f7fa8b9e60003e9001b4a6e6282c554bf8bda09bdf51ff519fab64c7a7f906c9adf540b0189fe71e68f53d927994aefbd32db019
PKCS1-v1_5による署名検証 (1回目) 30889 μSec 検証結果OK
PKCS1-v1_5による署名作成 (2回目) 36873 μSec 256 105f12779de942652f4d47c661b309b428f2b540cbc008bec2cf9153d421a7c397fb9ed82af7c583a2e3e821700c71ff2dad628709b264c9c0e3d328f088a088b04c17feb2344d5f29bb8d984632af63cff3637152e7f9b2eaa5680f9e0e46b4dc8c804dfa397ef4335bf87ea569e62302c2c134bc8607bccba740add2bef07956a4e16c9f2b30dad194132dbf97a8a7cf510efef59166f51bbf80d2188345da977bd5f265c23fee6b9379e9d88118c1f8ae140172a4f12d543d3ded8639acecf1fbc3b61c7551ef2691a288f7fa8b9e60003e9001b4a6e6282c554bf8bda09bdf51ff519fab64c7a7f906c9adf540b0189fe71e68f53d927994aefbd32db019
PKCS1-v1_5による署名検証 (2回目) 38867 μSec 検証結果OK
PSSによる署名作成 (1回目) 42857 μSec 256 7c873cb9b6bb19827f15dc971307a7b184fed89a8b9162d6a2e032491975d45b9a501f940d20357c2e4d4b390f62edc320d52033b24902a1cf9b22c8c368b66391817755489051c8077c2bfb8f41768f7985d1cdac82db6c28e26106e3363c0e5a55d409df43a66eb363377414110ddd4e0b0c5fd2d8215f15cce0244710a1e5507a6e3f8f6d625cd854fdd9806f14bcc1f091eec078beaaf394c82b9d351f6cd8f65cd2afe95caf9b98dd6f0937db7ed100be829475d81ad3a6ae0dd32a6469714d238b64ddd8cbb0a6b0559809b5ee5d5eef4ad58cb497e970bf94545c8ff61bf9f57808f36e7fa2278a6052ac97d06fd83083f0e7c2a9be10c54cae9226f4
PSSによる署名検証 (1回目) 43854 μSec 検証結果OK
PSSによる署名作成 (2回目) 48841 μSec 256 363954acfa3be11119e26433c25b1014981a9714788fa638932953c91cda16c1ea12bb8a4aea8a0e4d297d308ed80a90a60438ae80e905dacc7e38cce5943101293b0338463ce76805638712a946e76588a5fcf168e60fced0dce92dbab443907f10ef4f52b84ed45f61b401fba64ae0612c03e68496955579f0607b8ce4f58d2d6d5a77af3e38b47cce83fedbcef9a6249ccbdb1d51a8c674804281d5c5a6e8e0a04d23444479b7c6419cd7ba4112c84cef70e6ad7a66eef5115b0959a6cec9e402f882d30af84f3db359a4b26267ae39a25181093cac93d7b2cab3466e818ad0f81424057b664f6e25d9f90ee0739127c00d1c303b6b23324109605c81f85c
PSSによる署名検証 (2回目) 49838 μSec 検証結果OK