pythonで暗号演算 ~DSA、ECDSAによるデジタル署名~

python,暗号演算,暗号演算

今回はDSA(ECDSA)によるデジタル署名を取り上げます。

デジタル署名自体については、以前RSAにて取り上げているため良ければこちらも参考にしてください。

DSAとは

DSA(Digital Signature Algorithm)は、デジタル署名の標準規格であり、NISTによってFIPS 186として標準化されました。

同様にデジタル署名に用いられるRSAが暗復号処理可能なのに対し、DSAはデジタル署名に用途が限られています。

また暗号強度はRSAのほうが高いですが、DSAの派生であるECDSA、EDDSAを使用することにより暗号強度を上げることが可能であり、主にこれらの派生形が使用されています。

署名方式鍵長*概要
RSA1024
3072
15360
大きい合成数の素因数分解の困難性に基づく公開鍵暗号方式。
詳細についてはRFC8017参照。
DSA1024
3072
15360
離散対数問題の困難性に基づく電子署名方式。
詳細についてはRFC6979参照。
ECDSA160
256
512
DSAにおいて、楕円曲線(Elliptic Curve)暗号を用いる。
詳細についてはRFC6979参照。
EDDSA160
256
512
DSAにおいて、エドワーズ曲線(Edwards Curve)を用いる。
DSA、ECDSAにおける脆弱性に対応可能な比較的新しい方式。
詳細についてはRFC8032参照。

*鍵長については各方式で上から順に暗号強度をそろえた場合のサイズです。

ECDSA

TLS以外にも、ビットコインやイーサリアムなどのブロックチェーンネットワークにて、主要な暗号通信方式の仕様の一部として取り入れられており、広く普及が進んでいます。

RSAやDSAと比較し、より小さな鍵長で署名値の生成が可能なため、暗号演算速度に優れています。

DSAとECDSAには、使用する乱数の生成方法に不備があった場合セキュリティが保証されないという脆弱性があります。

EDDSA

EDDSAは比較的新しい署名方式で、TLS1.3より利用可能となりました。

前述のDSA、ECDSAと異なりEDDSAは乱数を使わないため、より安全な署名検証方式と言えます。

EDDSAはRSAやECDSAほど普及していませんが、速度とセキュリティにおいて優れていることから今後の普及が見込まれます。

実装

以下参考にPyCryptodomeライブラリを使用して、DSAECDSAによる暗号演算を実施します。

ソース

以下はRSADSA、ECDSAによる署名検証を実施し、処理時間を算出するコードです。

from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.PublicKey import DSA
from Crypto.PublicKey import ECC
from Crypto.Signature import pkcs1_15
from Crypto.Signature import DSS
import time


def main():
    message = b'test message'
    # RSAによる署名検証
    start = time.time()*1000000
    key = RSA.generate(3072)
    public_pem = key.publickey().exportKey()
    signature = rsa_signature(key,message)
    rsa_verify(public_pem,message,signature)
    print("RSAによる署名検証", int(time.time()*1000000-start), 'μSec')

    # DSAによる署名検証
    start = time.time()*1000000
    key = DSA.generate(3072)
    public_pem = key.publickey().exportKey()
    signature = dsa_signature(key,message)
    dsa_verify(public_pem,message,signature)
    print("DSAによる署名検証", int(time.time()*1000000-start), 'μSec')

    # ECDSAによる署名検証
    start = time.time()*1000000
    key = ECC.generate(curve='P-256')
    public_pem = key.export_key(format='PEM')
    signature = dsa_signature(key,message)
    ecdsa_verify(public_pem,message,signature)
    print("ECDSAによる署名検証", int(time.time()*1000000-start), 'μSec')


# RSAによる署名値生成
def rsa_signature(key,message):
    h = SHA256.new(message)
    signature = pkcs1_15.new(key).sign(h)
    return signature


# RSAによる署名検証
def rsa_verify(public_key,message,signature):
    key = RSA.import_key(public_key)
    h = SHA256.new(message)
    try:
        pkcs1_15.new(key).verify(h, signature)
        print("The message is authentic.")
    except ValueError:
        print("The message is not authentic.")


# DSAによる署名値生成
def dsa_signature(key,message):
    h = SHA256.new(message)
    signer = DSS.new(key, 'fips-186-3')
    signature = signer.sign(h)
    return signature


# DSAによる署名検証
def dsa_verify(public_key,message,signature):
    key = DSA.import_key(public_key)
    h = SHA256.new(message)
    verifier = DSS.new(key, 'fips-186-3')
    try:
        verifier.verify(h, signature)
        print("The message is authentic.")
    except ValueError:
        print("The message is not authentic.")


# ECDSAによる署名値生成
def ecdsa_signature(key,message):
    h = SHA256.new(message)
    signer = DSS.new(key, 'fips-186-3')
    signature = signer.sign(h)
    return signature


# ECDSAによる署名検証
def ecdsa_verify(public_key,message,signature):
    key = ECC.import_key(public_key)
    h = SHA256.new(message)
    verifier = DSS.new(key, 'fips-186-3')
    try:
        verifier.verify(h, signature)
        print("The message is authentic.")
    except ValueError:
        print("The message is not authentic.")


if __name__ == '__main__':
	main()

出力結果

出力結果は以下となっており、速度においてRSAとDSAがECDSAよりはるかに劣ることが分かります。

The message is authentic.
RSAによる署名検証 6233342 μSec
The message is authentic.
DSAによる署名検証 1515681 μSec
The message is authentic.
ECDSAによる署名検証 47845 μSec