Skip to main content

Pythonでのファイル暗号化:共通鍵暗号と公開鍵暗号を詳しく解説

著者

Keshav Malik

feature python encryption

2023年11月22日

0 分で読めます

現代では、デジタルチャネルを通じて、プライベートな情報や機密情報、センシティブな情報を常に共有しています。こうしたやり取りを支える基本的な要素がファイル暗号化です。暗号化アルゴリズムを使って、データを読み取れない形式に変換します。

暗号化には、共通鍵暗号と公開鍵暗号の2つの主な方式があります。共通鍵暗号は、1つの鍵を使う鍵付きボックスのようなものです。2人がその鍵を持っていれば、鍵を持つ人だけが開けられると分かっているので、ボックスを安全に受け渡せます。一方、公開鍵暗号では2つの鍵を使います。片方の鍵で施錠できても、もう片方の鍵でしか開けられないボックスを想像してください。施錠する鍵(公開鍵)は安全に共有できます。開錠する鍵(秘密鍵)がなければ、誰もボックスを開けてデータを復号できないからです。

この記事では、Pythonでの暗号化について詳しく解説します。共通鍵暗号では、Amazon Key Management Service(KMS)とPyNaCl SecretBoxを取り上げます。続いて、公開鍵暗号とPyNaClの公開鍵・秘密鍵ボックスを見ていきます。

前提条件

このチュートリアルを進めるには、次のものが必要です。

  • Python(v3.7以降)がインストールされていること

  • pipenvがインストールされていること。このPython用依存関係管理ツールを使うと、パッケージのバージョン間の競合を回避できます。pip install pipenvでインストールしてください。

  • pipenv install aws-encryption-sdk pynacl cryptographyを実行し、必要なパッケージ(aws-encryption-sdk、PyNaCl、cryptography)をインストールすること。

  • 利用可能なAWSアカウントとKMSキー(後ほどPythonプログラムを実行するときに使うため、キーIDとAWSリージョンの情報を手元に用意してください)。

  • AWSコマンドラインツール(awscli)がインストールされていること。aws configureを実行し、マシン上でAWSの設定を行います。

  • 次のコマンドを使って、分離された環境に新しいプロジェクトをセットアップすること。

mkdir python_encryption && cd python_encryption
pipenv shell

完成形はプロジェクトの完全なコードをご覧ください。

Pythonでの共通鍵暗号

共通鍵暗号では、データの暗号化と復号に同じ鍵を使います。シンプルで高速かつ効率的なため、大量のデータの保護に適しています。

このチュートリアルでは、Amazon KMSとPyNaCl SecretBoxを使って、Pythonで共通鍵暗号を実装します。Amazon KMSを使うと、Amazon Web Services(AWS)クラウド上で暗号化キーを作成、制御、管理できます。セキュリティがさらに強化された、クラウド上のキーリングのようなものです。SecretBoxは、暗号化を手軽に利用できるPythonライブラリです。個人用の金庫のようなものと考えてください。

Amazon KMSと暗号化SDKを使った共通鍵暗号

Amazon KMSは、暗号鍵を管理するクラウドベースのサービスです。他のAWSサービスや暗号化ソフトウェア開発キット(SDK)とシームレスに連携し、AWSが提供する堅牢なセキュリティを活用します。AWSサービスや暗号化SDKとの緊密な統合により、AWSベースのアプリケーションに最適です。

共通鍵暗号にKMSを使う大きな理由は、共有する秘密鍵を管理できることです。こうした鍵を安全に取り扱うことは、暗号化で最も難しい課題になり得ます。Amazon KMSは、エンベロープ暗号化方式を採用することで、この課題を巧みに解決します。この方式では、データとともに鍵自体も暗号化されます。そのため、主にハードウェアセキュリティモジュール内のマスターキーに依存できます。このキーには、権限のないユーザーはアクセスできません。このキーラッピングの仕組みによって暗号化のプロセスが簡素化され、鍵の管理やローテーションなどに伴う複雑さを効果的に軽減できます。

ただし、シンプルで小規模なソリューションだけが必要な場合、Amazon KMSは不要かもしれません。また、有料サービスのため、利用量が多いとコストが膨らむ可能性があります。

それでは、Amazon KMSを使ってPythonアプリケーションでファイルを暗号化してみましょう。

必要なモジュールをインポートする

Amazon KMSを使うには、まずaws-encryption-sdkをインストールしてインポートします。これは、暗号化と復号のプロセスを簡素化するPython用AWS Encryption SDKです。

main.pyというファイルを作成し、次のコードを追加します。

import aws_encryption_sdk

# Configuration
KEY_ARNs = [
    'arn:aws:kms:<REGION>:xxx:key/<KMS-KEY-ID>'
]

client = aws_encryption_sdk.EncryptionSDKClient()
kms_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(
    key_ids=KEY_ARNs)

注: KMSキーのARNは、AWS KMSダッシュボードからコピーできます。

ファイルを暗号化する

暗号化にはAWS Encryption SDKを使います。このSDKはAWS KMSと緊密に連携し、シームレスな暗号化を実現します。

次のコードをmain.pyに追加します。

def encrypt_file(file_path, encrypted_path):
    with open(file_path, 'rb') as pt_file, open(encrypted_path, 'wb') as ct_file:
        with client.stream(
            mode='e',
            source=pt_file,
            key_provider=kms_key_provider
        ) as encryptor:
            for chunk in encryptor:
                ct_file.write(chunk)

ファイルを復号する

ファイルの復号にも、AWS Encryption SDKを使います。復号プロセスでは、暗号化されたファイルを読み込み、KMSキーを使ってデータをシームレスに復号します。

次のコマンドをmain.pyに追加します。

def decrypt_file(encrypted_path, decrypted_path):
    with open(encrypted_path, 'rb') as ct_file, open(decrypted_path, 'wb') as pt_file:
        with client.stream(
            mode='d',
            source=ct_file,
            key_provider=kms_key_provider
        ) as decryptor:
            for chunk in decryptor:
                pt_file.write(chunk)

関数を呼び出す

関数を作成したので、次はそれらを呼び出してPythonプログラムを実行します。まずmyfile.txtというファイルを作成し、適当な内容を追加してください。

次に、main.py内で次の関数を呼び出します。

encrypt_file('myfile.txt', 'encrypted_file.txt')
decrypt_file('encrypted_file.txt', 'decrypted_file.txt')

python main.pyを実行してスクリプトを動かすと、myfile.txtを暗号化してから復号するため、内容は変更されません。

注: 説明を簡潔にするため、コード内(キーARNの一部)にキーIDをハードコードしています。機密情報の保存には.envファイルを使用してください。

PyNaCl SecretBoxを使った共通鍵暗号

PyNaClは、ネットワークおよび暗号化ライブラリlibsodiumのPythonバインディングです。共通鍵暗号機能を提供するSecretBoxをはじめ、便利な暗号化ツールが豊富にそろっています。SecretBoxの魅力は、そのシンプルさにあります。データの暗号化と復号に使う鍵は1つだけです。

SecretBoxを使ってPythonアプリケーション内の機密ファイルを暗号化する方法を見ていきましょう。

まずnacl.txtファイルを作成し、仮のテキストを追加します。次に、sym.pyという名前で新しいPythonプログラムファイルを作成し、必要なモジュールをインポートします。

from nacl import utils
from nacl.secret import SecretBox
import os

次に、秘密鍵を生成して読み込みます。generate_key関数はランダムな鍵を生成し、load_key関数はファイルパスから生成済みの鍵を読み込んで返します。

def generate_key(secret_key_file):
    secret_key = utils.random(SecretBox.KEY_SIZE)
    with open(secret_key_file, 'wb') as f:
        f.write(secret_key)
    return secret_key

def load_key(secret_key_file):
    if not os.path.exists(secret_key_file):
        print(f"Key file {secret_key_file} not found.")
    with open(secret_key_file, 'rb') as f:
        key = f.read()
    if len(key) != SecretBox.KEY_SIZE:
        print("Incorrect key length.")
    return key

続いて、ファイルを暗号化します。encrypt_file関数は、機密ファイルを暗号化するためにfile_pathと鍵を必要とします。file_pathでファイルを特定し、内容を読み込んで秘密鍵を使って暗号化した後、暗号化されたテキストを拡張子.encの新しいファイルに書き込みます。

def encrypt_file(file_path, key):
    if not os.path.exists(file_path):
        print(f"Key file {secret_key_file} not found.")
    secret_box = SecretBox(key)
    with open(file_path, 'rb') as f:
        plaintext = f.read()
    ciphertext = secret_box.encrypt(plaintext)
    with open(file_path+'.enc', 'wb') as f:
        f.write(ciphertext)

暗号化関数と同様に、復号関数も必要です。decrypt_file関数は、ファイルを復号するためにfile_pathとkeyを受け取ります。まず、暗号化された.encファイルが存在するか確認します(前の手順で生成済みです)。次に、その内容を読み込み、同じ秘密鍵を使って復号します。最後に、復号した内容を拡張子.decの新しいファイルに書き込みます。

def decrypt_file(file_path, key):
    if not os.path.exists(file_path+'.enc'):
        print(f"Key file {secret_key_file} not found.")
    secret_box = SecretBox(key)
    with open(file_path+'.enc', 'rb') as f:
        ciphertext = f.read()
    plaintext = secret_box.decrypt(ciphertext)
    with open(file_path+'.dec', 'wb') as f:
        f.write(plaintext)

最後に、作成した関数を呼び出します。

secret_key_file = 'secret.key'

# generate key for encryption
key = generate_key(secret_key_file)
encrypt_file('nacl.txt', key)

# load key for decryption
key = load_key(secret_key_file)
decrypt_file('nacl.txt', key)

ターミナルでsym.pyコマンドを実行すると、Pythonプログラムを実行できます。

注: このコードは、実際のアプリケーションや本番環境で使える状態ではありません。暗号鍵を安全に取り扱うには、鍵を定期的にローテーションするなど、追加の手順が必要です。このコードには、こうした重要なセキュリティ対策が含まれていません。

Pythonでの公開鍵暗号

共通鍵暗号では鍵を1つ使うのに対し、公開鍵暗号では暗号化用の公開鍵と復号用の秘密鍵の2つを使います。鍵付きの郵便受けのようなものです。誰でも投入口から手紙を入れられます(暗号化)が、専用の鍵を持つ人だけが開けて手紙を読めます(復号)。

公開鍵暗号は、インターネット上での安全な通信に特に適しています。友人に秘密のメッセージを送りたい場合、相手の公開鍵で暗号化すれば、相手だけが自分の秘密鍵で復号できます。また、公開鍵暗号はデジタル署名や証明書を保護します。これらは、デジタル世界における認証の基盤です。

PyNaClの公開鍵・秘密鍵ボックスを使った公開鍵暗号

では、Pythonで公開鍵暗号を行うにはどうすればよいでしょうか。PyNaClの別のツール、公開鍵・秘密鍵ボックスを見ていきましょう。

PyNaClの公開鍵・秘密鍵ボックスは、個人用の郵便受けのようなものです。公開鍵と秘密鍵の2つを使ってメッセージを暗号化・復号するためのインターフェースです。メッセージの暗号化に使う公開鍵は、誰にでも共有できます。一方、復号に使う秘密鍵は、秘密にしておく必要があります。

まず、asym_nacl.txtファイルを作成し、テスト用のテキストを追加します。暗号鍵を安全に保存し、取り出せるようにする必要があります。save_key関数はfile_pathに鍵を書き込み、load_key関数はfile_pathから鍵を取得して返します。

新しいファイルasym.pyに、次のコードを追加します。

from nacl.public import PrivateKey, Box
import pickle

def save_key(file_path, key):
    with open(file_path, 'wb') as f:
        pickle.dump(key, f)

def load_key(file_path):
    with open(file_path, 'rb') as f:
        key = pickle.load(f)
    return key

次に、機密ファイルを暗号化・復号する関数を実装します。encrypt_file関数は公開鍵を使ってファイルを暗号化し、暗号化されたテキストを.encファイルに保存します。decrypt_file関数は秘密鍵を使ってファイルを開き(復号し)、.decファイルに保存します。

次の関数をasym.pyに追加します。

def encrypt_file(file_path, public_key):
    private_key = PrivateKey.generate()
    box = Box(private_key, public_key)

    with open(file_path, 'rb') as f:
        plaintext = f.read()

    ciphertext = box.encrypt(plaintext)

    with open(file_path+'.enc', 'wb') as f:
        f.write(ciphertext)

    return private_key

def decrypt_file(file_path, private_key, public_key):
    box = Box(private_key, public_key)

    with open(file_path+'.enc', 'rb') as f:
        ciphertext = f.read()

    plaintext = box.decrypt(ciphertext)

    with open(file_path+'.dec', 'wb') as f:
        f.write(plaintext)

いよいよ、ここまでの作業の成果を確認しましょう。2つの鍵を生成します。ファイルの施錠に使う公開鍵と、保存しておく秘密鍵です。ファイルにアクセスするときは、保存した秘密鍵と、それに対応する公開鍵を読み込み、ファイルを復号します。

最後の関数呼び出しをasym.pyファイルに追加します。

private_key = PrivateKey.generate()
public_key = private_key.public_key

encryption_private_key = encrypt_file('asym_nacl.txt', public_key)
save_key('private.key', encryption_private_key)

encryption_private_key = load_key('private.key')
decrypt_file('asym_nacl.txt', private_key, encryption_private_key.public_key)

ローカルのターミナルでpython asym.pyを実行して、プログラムを動かします。

注: 上記のコードは、暗号化と復号の仕組みを示すものです。ただし、実際のアプリケーションで安全に使えるものではありません。鍵ファイルが保護されていないため、第三者がアクセスできると、ファイルを開けてしまいます。

PyNaClの公開鍵・秘密鍵ボックスのメリットとデメリット

PyNaClの公開鍵・秘密鍵ボックスは、高いセキュリティを提供します。また、安全でないチャネル上でも安全に通信できるため、インターネット通信に最適です。

一方、公開鍵・秘密鍵ボックスでは、鍵の管理が難しい場合があります。秘密鍵を安全に保管し、暗号化に適切な公開鍵を使う必要があります。また、公開鍵暗号はより複雑な処理を伴うため、共通鍵暗号より遅くなることがあります。

今回の解説のまとめ

この記事では、Pythonで暗号化方式を実装するための強力なツールをいくつか紹介しました。Amazon KMSとPyNaClのSecretBoxは共通鍵暗号に適しており、PyNaClの公開鍵・秘密鍵ボックスは公開鍵暗号に優れています。

これらのツールには、安全なデータ処理に役立つさまざまなメリットがあります。Amazon KMSは優れたスケーラビリティを備え、PyNaClのSecretBoxは高いパフォーマンスと幅広い互換性を持ち、PyNaClの公開鍵・秘密鍵ボックスは非常に安全な通信を可能にします。これらを組み合わせることで、包括的な暗号化ツールキットを構成できます。

暗号化方式を選ぶ際には、データの機密性、データを送信する環境、鍵の管理方法を考慮する必要があります。完璧な暗号化方式はありませんが、ニーズに最適なものを見つけるのは難しくありません。

Pythonアプリのセキュリティ対策を始めましょう

Snykを使って、Pythonの脆弱性を無料で検出・修正できます。

クレジットカードの登録は不要です。

またはAzure AD、Docker ID、Bitbucketで登録

Snykを利用すると、利用規約およびプライバシーポリシーを含む各種ポリシーに同意したものとみなされます。

続きを読む

blog feature ai violet
Blog

VulnBump:AIエージェントはビルドを壊さずに脆弱な依存関係を修正できるか?

VulnBumpは、実際の依存関係アップグレード307件を対象にAIエージェントをテストします。脆弱性が解消され、ビルドとテストが成功した場合にのみ、修正としてカウントされます。

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSecのご紹介:AI開発のスピードに対応するセキュリティ

AI駆動型開発のスピードに、従来のセキュリティワークフローは追いついていません。SnykのAgentic AppSecは、セキュリティエージェントが脆弱性を継続的に検出、修正、検証し、人が基準を定められるようにします。

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。