Engineering Note

プログラミングなどの技術的なメモ

9.2 IEのCOMオートメーションを使用した情報の盗み出し (サイバーセキュリティプログラミング Pythonで学ぶハッカーの思考)

本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―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公開鍵暗号を用いたファイルの暗号化および復号について学びました。

公開鍵暗号は現在のセキュリティの基盤となっている技術ですので、その仕組みについてはしっかりと理解したいと思います。

 

参考書籍

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考