本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。
今回は、Pythonで公開鍵暗号を用いた暗号化について学んでいきます。
はじめに
本章について、書籍ではIEのCOMオートメーションを使用して、Tumblr(タンブラー)という外部のサイトにターゲットのドキュメントを暗号化しアップロードする方法について書かれていますが、本記事ではPythonで公開鍵暗号を用いた暗号化について学んでいきたいと思います。
公開鍵暗号とは
公開鍵暗号(Public-Key Cryptography)とは、1976年にWhitfield DiffieとMartin Hellmanが発表した暗号化方式です。
共通鍵暗号では、暗号化と復号に同じ鍵を用いられますが、一番の問題点は鍵の配送(受け渡し)をどのように行うかでした。
この問題を解決したのが、公開鍵暗号方式と呼ばれるもので、暗号化と復号にそれぞれ公開鍵と秘密鍵という別の鍵を用います。
公開鍵暗号では様々な種類の方式(アルゴリズム)がありますが、今回はよく知られているRSA暗号を用いて、ファイルを暗号化します。
RSA暗号は、大きな素数同士を掛け合わせた際に、それを素因数分解することが困難であるという事を利用したものです。
この掛け合わせた素数を効率よく見つける方法は数学的にも見つかっておらず、地道に計算をしていかなければならないため、現実的な時間内で解読することは不可能です。
しかし、近年の量子コンピュータの進歩などを見れば、現実的な時間で解読できてしまうのも時間の問題のようです。
公開鍵と秘密鍵を作成する
それでは、Pythonで公開鍵暗号方式によるファイルを暗号化および復号化するスクリプトを作成します。
まずは公開鍵と秘密鍵を生成して、ファイルに保存します。
# keygen.py from Crypto.PublicKey import RSA new_key = RSA.generate(2048, e=65537) public_key = new_key.publickey().exportKey("PEM") private_key = new_key.exportKey("PEM") with open("rsa.pub", "wb") as f: f.write(public_key) with open("rsa", "wb") as f: f.write(private_key) print(public_key) print(private_key)
上記のスクリプトを実行すると以下の公開鍵と秘密鍵が生成されました。
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSBSDwrpLmogLwUOgxds Jeh32SJdlAETKJ9LmHvdogZNFLwggVuOvtQxkzNs0mgDxCgstC9OWqK2Otbhbf21 wDrD+k4piicdZtmL+KIYUBkrS/To2GkJJR2nb7JFS/TTAlvWM+S3P1oCRCVtFhjx MrB230FC62eU+13tWk3/4EWzmC9Gfc/ahj7MiuVgTAbYD3CElUYwuo13P6aNDiAn lHGoPAlXjqYV0vvN4IcvzxhPWju9RQ0WCqXcT2H9otreq/pTggUm3fxVmSdPA6W/ mp6NgnIyPF/wtKX++51o7tIfkc4vcnzFQ0ORNhXeny7D+w2eo4guZO9r97wytye2 IwIDAQAB -----END PUBLIC KEY----- -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAvSBSDwrpLmogLwUOgxdsJeh32SJdlAETKJ9LmHvdogZNFLwg gVuOvtQxkzNs0mgDxCgstC9OWqK2Otbhbf21wDrD+k4piicdZtmL+KIYUBkrS/To 2GkJJR2nb7JFS/TTAlvWM+S3P1oCRCVtFhjxMrB230FC62eU+13tWk3/4EWzmC9G fc/ahj7MiuVgTAbYD3CElUYwuo13P6aNDiAnlHGoPAlXjqYV0vvN4IcvzxhPWju9 RQ0WCqXcT2H9otreq/pTggUm3fxVmSdPA6W/mp6NgnIyPF/wtKX++51o7tIfkc4v cnzFQ0ORNhXeny7D+w2eo4guZO9r97wytye2IwIDAQABAoIBAQCLqS5vxVLk36Hc lxNcYxON+tlmEyafgRuwx9F/TRkh6R1KzpICdKY47WGenj8iMYV2eWRpuvVVoQqI 8N2P5+criTnnwYiTaMchiE0A0lK585wxfy3jgc0rB3b52N+rc6PO/hp0P9XChqld wXWI9+pF0GI4A21zgQ5XgckhArCN4CWopI27/MwxCaE++lKOqCUp0N78F6i30OFO jsr7XwYsDO8JEFSiGCrCy9qWKXbMAOvATWdTZTNiuGzT6N8mW3tJcoCS2iZFhTPU sBi13e/jZPTLeo+aWx+Q0JeTIikbMbF9B5RZABwyvSW+WencFZHMq5tVnIc7biV3 d7tyi6ihAoGBANIg0VxUtpejg0yf5RzpPMVHH9uhkJLW2yG0hM0VdDkLAa1j3WKV Ti0vB8lIcNsVJgVT6dI1nqCVi0AR3cONXXcQYNOQjSVBQh9e7b+gk3lCoTrYCFJU Ot7OiguCMj6VsjNk+RQsR4qTeVbcwDpo3T3CkErER3fNCokVFwRuipoVAoGBAOZp ytlAdzn31tiMghtDgJxuW74wt6TKO/HjfYdL9oz1HoCUFgxyiV+ZvadbnXz2a3IS X9e3iP+VE2sxRxqq0IdiJOISCfQdWrcTCge04NwZNP2a6FhxiRPIRkQ9IMqDTOx0 +mxfbUBXl9M+soxrTZQscVoLQ4Xdah5KZsoQSzVXAoGAMgVP8WvVH5n0GpyBnPVE 2y2IPu9iDCKLfaWMa4ms5hthpdX9EIe8WXM1pkvRWcXaDDCim8DZ5x5STMk0AmY3 lq1Wb2acwv6a7/7YF+Lw2E/tM6XTrU2RyhJeuEihD1bxWdyykoPL8AgVPYFFXWIz MI2Nbc71uGYeGO3lUc/CvNUCgYBGOyjhX4z31lteVjWyKbkh9WFxb1gKmrn62OSC QN09ydkqR/kQgjjtMudkA8DVPicx9ffQwNi5Tq9ElRifQEGfgoLLhcQS7H+iHeOI xdFkRSqpwnXfv3uQ1BLW6er8z1i7Gkzai5u5aPmWIh46Ptq+t201jQlf3EVkRjle jejesQKBgQCZhdnuA9yH1kNxS2048ucfl4uao31URoAlufsfVkF3B4RZI1p0BHn/ oNc6hX8NNMNFtdK4ebsWz7Z+AtaXkcgzhSD5vHnSyjLKOjC7Ad3bPwwlUhLBlG4i o6jW4mec7w2hV8rUPoTcxHs+IDeuUng7+b3Q0Xz6NsDdvTfrFN/H6w== -----END RSA PRIVATE KEY-----
ファイルを暗号化する
上記で生成した公開鍵を用いて、ファイルを暗号化するスクリプトを作成します。
# encrypt_file.py import os import sys import zlib import base64 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP with open("rsa.pub", "rb") as f: public_key = f.read() def encrypt_string(plaintext): chunk_size = 128 print("[*] Compressing: {} bytes".format(len(plaintext))) plaintext = zlib.compress(plaintext) print("[*] Encrypting {} bytes".format(len(plaintext))) rsakey = RSA.importKey(public_key) rsakey = PKCS1_OAEP.new(rsakey) encrypted = b'' offset = 0 while offset < len(plaintext): chunk = plaintext[offset:offset + chunk_size] if len(chunk) % chunk_size != 0: chunk += b' ' * (chunk_size - len(chunk)) encrypted += rsakey.encrypt(chunk) offset += chunk_size encrypted = base64.b64encode(encrypted) print("[*] Base64 encoded crypto: {} bytes".format(len(encrypted))) return encrypted def save_encrypt(filename): with open(filename, "rb") as f: contents = f.read() print('--- file contents ---') print(contents) print('---------------------') encrypted_body = encrypt_string(contents) filename = 'encrypted_' + filename with open(filename, "wb") as f: f.write(encrypted_body) print("[*] Successfully save file as '{}'.".format(filename)) def main(): if len(sys.argv) == 1: print("Usage: {}".format(sys.argv[0])) sys.exit(1) filename = sys.argv[1] save_encrypt(filename) if __name__ == '__main__': main()
スクリプトを実行し、ファイルを暗号化してみます。
> python encrypt_file.py test --- file contents --- b'this is a test file.\r\n' --------------------- [*] Compressing: 22 bytes [*] Encrypting 28 bytes [*] Base64 encoded crypto: 344 bytes [*] Successfully save file as 'encrypted_test'.
今回は"this is a test file."とだけ記載されたテキストファイルで、以下は暗号化されたものになります。
GAAKS+yRFVoBRFt9Rj4v+D8Lsqt6ema9EMqz4xFHHudXbgMdEHbGCHPrLKBOjQjt\ JKjS/kc9m7HnoGiUw9pyOYPHK0FWcmrug6nbm1hBff6p8F/qkHjogP3t7O9Ey2lR\ mjBswF7M7yppAqODpQh+NNgTH0KQFHh0uc1u1lkDpBGBds7aZ+SgIG4wDVEb5jqk\ JfzUbyaVdjcqQGEkuRhk5IeP99ciGaNTLm+FakMb61BHIOgewYV/TRGPBaKVV7iN\ v9TbZyKbdSWI3seHWM6jGmvF3/rhSMQMkSDLamVHm5jBuy5Jg/lyHtm5E3wJmQGq\ 4nfpQBcUJC/P0IkUXT2+5Q==
ファイルを復号する
上記で暗号化されたファイルを復号するスクリプトを作成します。
# decrypt_file.py import os import sys import zlib import base64 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP with open("rsa", "rb") as f: private_key = f.read() rsakey = RSA.importKey(private_key) rsakey = PKCS1_OAEP.new(rsakey) def decrypt_file(filename): with open(filename, "rb") as f: encrypted = f.read() chunk_size = 256 offset = 0 decrypted = b'' encrypted = base64.b64decode(encrypted) while offset < len(encrypted): decrypted += rsakey.decrypt(encrypted[offset:offset + chunk_size]) offset += chunk_size plaintext = zlib.decompress(decrypted) print('--- file contents ---') print(plaintext) print('--------------------') def main(): if len(sys.argv) == 1: print("Usage: {}".format(sys.argv[0])) sys.exit(1) filename = sys.argv[1] decrypt_file(filename) if __name__ == '__main__': main()
上記のスクリプトを実行すると問題なくコンテンツが復号されたことが確認できました。
> python decrypt_file.py encrypted_test --- file contents --- b'this is a test file.\r\n' --------------------
最後に
今回はPythonで公開鍵暗号を用いたファイルの暗号化および復号について学びました。
公開鍵暗号は現在のセキュリティの基盤となっている技術ですので、その仕組みについてはしっかりと理解したいと思います。