対称暗号と非対称暗号:Pythonによる実践例
Josh Amata
2024年5月15日
0 分で読めます対称暗号と非対称暗号は、暗号技術を使って機密データを保護する最も一般的な2つの方法です。これらの方法では鍵を使い、暗号化されていないメッセージを、正しい鍵がなければ復号が極めて困難な暗号文に変換します。対称暗号では、データの暗号化と復号に同じ鍵を使います。一方、非対称暗号では、公開鍵と秘密鍵のペアを使って機密データを暗号化・復号します。
データの機密性を重視する安全なデータ転送・保存システムでは、これらの暗号化方式がよく使われます。たとえば、エンドツーエンドのメッセージングアプリでは、メッセージを送信前に送信側で暗号化します。これにより、攻撃者が盗聴して平文のメッセージを読み取る中間者攻撃を防げます。暗号化されたメッセージの内容は、攻撃者には読み取れません。
対称暗号と非対称暗号は、パスワードや医療記録などの機密情報を保存するといった用途にも役立ちます。通常、システムはこうした情報をサーバーに保存したり、コンピューター間で共有したりする前に暗号化します。
TLS/SSLなどのアプリケーションでも、これらの方式が使われています。ハンドシェイクでサーバーが公開鍵をクライアントに送ると、クライアントはランダムなプレマスターシークレットを生成し、サーバーの公開鍵で暗号化します。次にサーバーは、秘密鍵を使ってプレマスターシークレットを復号します。その後、サーバーとクライアントはそれぞれ、プレマスターシークレットと他のハンドシェイクパラメーターからマスターシークレットを生成します。
同じパラメーターを使うため、クライアントとサーバーは同一のマスターシークレットを生成します。アプリケーションはこれを使ってセッション鍵を生成します。対称鍵を使ってセッション中のデータを暗号化・復号することで、サーバーとクライアント間の安全な通信を実現します。このプロセスでは、非対称暗号による安全で信頼関係を前提としない鍵共有と、高速で確実なデータ転送を可能にする対称鍵の安全な共有を活用します。
どちらの方式も、不正アクセス、傍受や改ざん、窃取、プライバシー侵害など、データを危険にさらすセキュリティリスクの低減に役立ちます。このガイドでは、対称暗号と非対称暗号について解説し、Pythonで実装しながらベストプラクティスを紹介します。
前提条件
このチュートリアルを進めるには、以下が必要です。
Pythonの基本的な知識
pipを含むPythonツールチェーン(バージョン3.7以上)がインストールされていること
Amazon Web Services(AWS)アカウント。アカウントの作成と一部サービスの利用は無料です。
対称暗号について
対称暗号は暗号化と復号に同じ鍵を使うため、非対称暗号より高速で効率的です。通常、次の手順で処理します。
鍵の生成:送信者または信頼できる第三者が秘密鍵を生成し、受信者に安全な方法で共有します。
暗号化:送信者は、データ暗号化規格(DES)、トリプルDES(3DES)、高度暗号化標準(AES)などの対称暗号アルゴリズムを、平文のメッセージと共有秘密鍵に適用します。これにより、平文が暗号文に変換されます。
データの送信:送信者は、インターネットなどの安全でない通信経路を通じて、暗号文を受信者に送信します。
復号:受信者は、共有鍵を使って同じ対称暗号アルゴリズムを逆に適用します。これにより暗号文が復号され、元の平文メッセージが得られます。
効率と速度に優れているため、対称暗号はファイル、データベース、さらにはディスクパーティション全体の暗号化など、処理負荷の大きいタスクに適しています。この方式なら、保存データが侵害された場合でも、適切な鍵がなければ読み取れない暗号化された状態を保てます。
対称暗号アルゴリズム
DES、3DES、AESについて詳しく見ていき、エンベロープ暗号化が鍵管理にどう役立つかを説明します。
データ暗号化規格(DES)
DESは1970年代から広く使われてきた対称暗号アルゴリズムです。56ビットの鍵を使用するため、鍵の候補は256(2の56乗)通りに限られます。DESは約20年間、十分な安全性を備えていましたが、現在では最新の計算能力を使った総当たり攻撃で簡単に解読されるようになっています。
トリプルデータ暗号化規格(3DES)
3DESはDESを改良したもので、3つのDES鍵を使用します。112ビット相当の高いセキュリティを提供します。しかし、112ビットの安全性を確保するには168ビットの鍵が必要なため、効率面では課題が残ります。
高度暗号化標準(AES)
AESは1990年代に登場し、現在の対称暗号の標準となっています。鍵長は128、196、または256ビットで、各レベルのセキュリティを提供します。128ビットと256ビットの安全性の差はわずかなため、ほとんどのアプリケーションでは128ビット鍵を使用します。AESは3DESより高速で安全です。
エンベロープ暗号化
対称暗号の大きな課題の1つは、鍵管理です。暗号化と復号に同じ鍵を使うため、対称暗号で鍵が漏えいすると深刻な事態になります。この課題を解決するのがエンベロープ暗号化です。データ暗号化鍵(DEK)と鍵暗号化鍵(KEK)を使用します。DEKはアプリケーションがデータの暗号化に使うメインの鍵で、KEKはDEKを暗号化します。
この2層の暗号化では、攻撃を成功させるために両方の鍵が必要となるため、セキュリティが強化されます。クラウドコンピューティング環境では、セキュリティ、柔軟性、拡張性を高めるために、エンベロープ暗号化がよく採用されています。後述するように、クラウドプロバイダーはエンベロープ暗号化を利用するための便利なインターフェースを提供し、マスター鍵(KEK)が外部から見えないように保護しています。
Pythonで対称暗号を実装する
エンベロープ暗号化を使って暗号化と復号を行うPythonアプリケーションを作成しましょう。AWS Key Management Service(KMS)は鍵管理向けのエンベロープ暗号化を提供しており、AWS Encryption SDKを使ってサービスと通信できます。
始める前に
まず、プロジェクト用のディレクトリを作成します。
次に、こちらの手順に従って、AWSダッシュボードで対称KMS鍵を作成します。このKMS鍵は、カスタマーマスターキー(CMK)です。
鍵のアクセス権限を設定し、鍵のダッシュボードにある全般設定セクションからAmazonリソースネーム(ARN)をコピーします。
![AWS Key Management Serviceのコンソール。[General configuration]の下にあるカスタマーマネージドキーのARNが赤い丸で囲まれている](https://res.cloudinary.com/snyk/image/upload/f_auto,w_2560,q_auto/v1715777038/blog-sync-async-kms-arn.jpg)
このARNは、AWS Encryption SDKで操作する際にCMKを識別するためのものです。これは、生成されたDEKを暗号化するKEKです。
次に、AWS CLIをインストールし、以下のコマンドで設定します。
AWS CLIを設定する際は、プロファイルに適切なCMK権限があることを確認してください。この設定により、保存済みの認証情報を使ってアプリケーションがAWS KMSと正しく通信できます。
アプリケーションの残りを作成する
アプリケーションの残りの部分を作成するために、aws-encryption-sdkとargparseのパッケージを使用します。aws-encryption-sdkは、AWS Encryption SDKのPython実装です。以下のpipコマンドでこれらのパッケージをインストールします。
次に、sym_encryptor.pyファイルを作成し、必要なモジュールをインポートするコードを追加します。
続いて、クライアントクラスとキープロバイダークラスを作成するコードを追加します。
EncryptionSDKClientクラスは、データ暗号化用のencrypt、decrypt、streamなどの高水準なクライアントメソッドを提供します。このコードでは、暗号化と復号に使うAWS KMSマスター鍵のリストを指定して、StrictAwsKmsMasterKeyProviderクラスを設定します。
先ほど作成したCMKのARN識別子を渡します。CMKのARNを渡すことで、コードはCMKから鍵を導出し、平文を暗号化できます。
aws-encryption-sdkは、CMKを使ってデータ鍵を自動的に生成、暗号化、復号します。この自動化により、鍵を簡単に管理できます。
暗号化関数を作成する
以下の関数をスクリプトに追加して、暗号化関数を作成します。
この関数はメッセージをバイナリデータとして受け取り、クライアントのencrypt()メソッドを使って暗号化します。encryptメソッドには、暗号化するデータ、キープロバイダークラス、暗号化コンテキストを渡します。
先ほど作成したキープロバイダークラスを渡すと、クライアントはCMKからデータ暗号化鍵を生成し、データを暗号化します。クライアントは暗号文と暗号化ヘッダーを返します。暗号化ヘッダーには、真正性を照合するための情報が含まれています。暗号化コンテキストは、クライアントが返す暗号化ヘッダーの情報を含む辞書です。
復号関数を作成する
復号関数を作成するには、以下のコードをスクリプトに追加します。
この関数は暗号文を受け取り、キープロバイダーと暗号文を引数として、クライアントのdecrypt()メソッドを呼び出します。decryptメソッドはencryptメソッドと同様に動作します。設定済みのCMKからデータ鍵を導出し、同じ鍵とアルゴリズムをデータに適用して暗号化を逆戻しします。このメソッドは、復号されたテキストと、暗号化ヘッダーと同様の復号ヘッダーを返します。実際のアプリケーションでは、両方のヘッダーに暗号スイート、ARN、暗号化コンテキストなどの同一情報が含まれているため、データの照合に役立ちます。
コマンドラインアプリケーションを作成する
テストを簡単にするため、コマンドライン引数を解析して、アプリケーションをコマンドラインアプリケーションに変更しましょう。以下のコードを追加します。
このコードはコマンドライン引数を解析します。鍵のフラグと、暗号化モードまたは復号モードを選択するフラグを設定します。ファイルを保存し、以下のコマンドを実行します。
出力は次のようになります。
アプリケーションをテストする
アプリケーションをテストするには、まず以下のような任意のテキストファイルを作成します。
ファイル名と暗号化モードの-eフラグを指定して、アプリケーションでファイルを処理します。
これにより、新しいファイルencrypted.binが作成されます。
出力は次のようになります。
このファイルは、アプリケーションが作成した暗号文です。この暗号化ファイルを受信者に渡せます。受信者や他の関係者がファイルを復号するには、CMKへの適切なKMSアクセス権限が必要です。
ファイルを復号する
復号の流れを確認するには、暗号化したファイルをアプリケーションで処理します。
decryption completeという通知が表示されます。
次のように、ディレクトリ内にdecrypted.fileという新しいファイルが作成されます。
AWS KMSを使ったエンベロープ対称暗号でメッセージを暗号化・復号し、元のテキストを正常に復元できました。
非対称暗号について
先ほど取り上げた対称暗号では暗号化と復号に同じ鍵を使いますが、非対称暗号では鍵ペアを使用します。1つは公開鍵(ユーザーがインターネットや他の通信経路を通じて広く共有できる鍵)、もう1つは秘密鍵(ユーザーが秘密に保管する鍵)です。
セキュリティと鍵管理
対称暗号は一般に高速で効率的ですが、非対称暗号を使えば、送信者と受信者の間で鍵を安全に共有する際の脆弱性を解消できます。非対称暗号では、対応する秘密鍵だけがファイルを復号できるため、安全でない通信経路であっても公開鍵を誰とでも自由に共有できます。受信者は秘密鍵を厳重に保管します。
AliceがBobから暗号化ファイルを安全に受け取りたい場合、公開鍵と秘密鍵のペアを生成し、公開鍵をBobに送る必要があります。公開鍵を受け取ったBobは、ファイルを暗号化してインターネット経由で送信できます。秘密鍵を持つAliceだけが、そのファイルを復号できます。
次にAliceがBobに暗号化ファイルを送りたい場合、Bobは自分の公開鍵と秘密鍵のペアを作成する必要があります。Bobが公開鍵をAliceに送ると、Aliceはその鍵を使ってファイルを暗号化し、Bobに送信できます。Bobは自分の秘密鍵を使ってファイルを復号します。
非対称暗号を使う場合、各ユーザーがそれぞれ固有の公開鍵と秘密鍵のペアを持つ必要があります。
ユースケース
アプリケーションでは主に、TLS/SSLプロトコルでの安全な鍵交換、Pretty Good Privacy(PGP)を使用した安全なメール通信、メッセージの送信者や受信者の身元を確認するデジタル証明書に、非対称暗号化が使われます。非対称暗号化は、鍵を安全に共有する必要をなくしながら、高いセキュリティを実現します。
非対称暗号化アルゴリズム
鍵ペアの生成に使われる代表的な非対称暗号化アルゴリズムとして、RSA、ElGamal、Curve25519を見ていきましょう。
Rivest-Shamir-Adleman(RSA)
RSAは、最も古く、広く使われている非対称暗号化アルゴリズムの1つです。大きな合成数を素因数に分解することが数学的に困難であることに基づいています。
RSAの鍵生成では、2つの大きな素数を選び、法とオイラーのトーシェント関数を計算して、秘密鍵と公開鍵の構成要素を導き出します。RSAはほぼあらゆるところで使われています。十分に大きな鍵長(2048ビット以上)を使用すれば非常に安全ですが、他のアルゴリズムより計算コストが高くなります。
ElGamal
ElGamalは、離散対数問題を解く困難さに基づいています。このアルゴリズムはRSAより高速ですが、安全性は低く、より高い安全性を確保するには長い鍵が必要です。ElGamalの鍵生成では、巡回群と生成元を選び、剰余べき乗を使って公開鍵と秘密鍵を計算します。暗号化では平文をランダム化し、受信者の公開鍵と乗算します。
Curve25519
Curve25519は、最も高速で安全性の高い楕円曲線暗号(ECC)アルゴリズムの1つです。鍵交換やデジタル署名に使用でき、ECCアルゴリズムはRSAより短い鍵長で強固なセキュリティを実現します。
ECCは、有限体上の楕円曲線が持つ数学的性質を利用します。鍵生成では楕円曲線と基点を選び、曲線上のスカラー倍算に基づいて公開鍵と秘密鍵を計算します。
IoTやブロックチェーンシステムなど、リソースに制約のある環境やデバイスでは、主にECCが使われます。RSAと同等のセキュリティレベルを維持しながら、計算効率に優れています。
Pythonで非対称暗号化を実装する
まず、次のコマンドを使って、プロジェクトディレクトリに新しいasym_encryptor.pyファイルを作成します。
次のライブラリをインポートします。
nacl.publicモジュールには、秘密鍵と公開鍵のペアを生成するクラスと、暗号化・復号を処理する関連メソッドが含まれています。PrivateKeyクラスとPublicKeyクラスは、バイト列から対応する鍵を初期化します。また、SealedBoxは、特定の鍵ペア宛てのメッセージを暗号化・復号します。このコマンドでは、エラー処理に使うTypeError例外クラスとCryptoError例外クラスもインポートします。
ヘルパー関数を作成する
次に、鍵ペアを生成するヘルパー関数を作成します。
この関数はCurve25519 ECCアルゴリズムを使用し、generate()メソッドでランダムなPrivateKeyオブジェクトと、それに対応する公開鍵を生成します。その後、生成した鍵をそれぞれのファイルに書き込みます。
メッセージを暗号化する関数を作成する
次に、公開鍵を使ってメッセージを暗号化する関数を追加します。
この関数は、暗号化するバイナリ形式の平文メッセージと、公開鍵の場所を示す文字列を受け取ります。次に、PublicKeyクラスで公開鍵を読み込み、初期化してから、その公開鍵を使ってSealedBoxを初期化します。
復号関数を作成する
PyNaClは、BoxとSealedBoxという2つのクラスを使った非対称暗号化に対応しています。Boxは、送信者の秘密鍵と受信者の公開鍵を使って共有鍵を作成し、暗号化します。復号には秘密鍵と公開鍵の両方が必要で、送信者が作成者であることを暗号学的に証明できます。
ここではSealedBoxクラスを使って復号関数を作成します。これは、作成者を証明する暗号学的な証明を使わず、鍵ペアだけで暗号化・復号できます。暗号化には公開鍵でSealedBoxを初期化し、暗号化するメッセージを指定してencrypt()メソッドを呼び出します。次に、対応する暗号文を作業ディレクトリ内のファイルに書き込みます。
次のコードをアプリケーションに追加します。
この関数は、復号する暗号文と秘密鍵を受け取ります。PrivateKeyクラスで秘密鍵を読み込んで初期化し、復号用の秘密鍵を使ってSealedBoxを初期化します。decrypt()メソッドでメッセージを復号し、復号したテキストを作業ディレクトリ内のファイルに保存します。
コマンドライン引数の解析を追加する
アプリケーションをテストしやすくするために、次のコードでコマンドライン引数の解析を追加します。
このコードはコマンドライン引数を解析し、暗号化モードと復号モードの切り替え、鍵ペアの生成、ファイルの受け渡しに使うフラグを設定します。
ファイルを保存し、次のコマンドを実行します。
次のように出力されます。
アプリケーションをテストする
アプリケーションをテストするために、次のような任意のテキストファイルを作成します。
ファイルを暗号化する前に、テスト用の鍵ペアを生成するため、次のコマンドを実行します。
次のように出力されます。
このコマンドは、プロジェクトディレクトリ内に秘密鍵と公開鍵のペアを作成します。
生成した公開鍵を使ってテキストファイルを暗号化するには、次のコマンドを実行します。
次のように出力されます。
アプリケーションは暗号文を作成し、プロジェクトディレクトリ内のencrypted.binとして保存します。この暗号化されたファイルは完全に読み取り不能です。次のように、公開鍵に対応する秘密鍵がなければ復号できません。
次のように出力されます。
private key decryption completed
アプリケーションは、復号したファイルをプロジェクトディレクトリ内のdecrypted.fileとして保存しました。次のコマンドで復号したファイルを読み取ります。
完了すると、“Hello World!!!”と出力されます。
これで、非対称暗号化を使って平文ファイルを正常に暗号化・復号できました。
対称暗号化と非対称暗号化のセキュリティ上の考慮点とベストプラクティス
対称暗号化の大きな課題は、鍵を安全に共有することです。システムの参加者が増えるほど、鍵の配布も難しくなります。非対称暗号化は公開鍵と秘密鍵のペアを使ってこの問題を回避できますが、対称暗号化ではエンベロープ暗号化などの方法でセキュリティを高めます。
対称暗号化と非対称暗号化では、次の点を実践しましょう。
AWS KMSなど、対称鍵を使うアプリケーションの鍵管理には、Google Cloud Platform(GCP)やAWSといったプロバイダーのツールを活用しましょう。
可能であれば、将来にわたってアプリケーションを保護するため、RSA暗号化は避けましょう。2048ビット未満の短い鍵は攻撃者に解読される可能性があり、初期化パラメーターが不適切な場合も危険です。RSAを正しく実装するのは困難です。
ファイルサイズが非常に大きい場合は、対称暗号化を選びましょう。非対称暗号化よりも高いパフォーマンスを発揮します。
鍵を選ぶ際は、暗号化アルゴリズムに適した長さの鍵を選びましょう。短すぎる鍵は脆弱性につながります。DESのような安全でないアルゴリズムは使わないでください。
暗号化の仕組みを独自に開発しないでください。簡単なミスが重大な問題につながる可能性があります。
Snykなどのツールを使って、コード内のセキュリティ上の問題を検出し、修正の推奨事項を確認しましょう。Snykのプラットフォームでは、コード、オープンソースの依存関係、コンテナイメージ、Infrastructure as Code(IaC)構成をスキャンし、脆弱性の修正と優先順位付けができます。
暗号化方式にはそれぞれ長所と短所があります。どの方式を使うか判断する際は、これらの要素を考慮しましょう。実際には、効率と安全性のバランスを取るため、対称暗号化と非対称暗号化を組み合わせて使う場合もあります。
Snykを使う
開発中にSnykで脆弱性を検出する方法を知るために、実際にコードをテストしてみましょう。
まず、Snykアカウントを作成します。無料で始められます。

登録すると次のページが表示され、スキャン対象のコードの場所として、GitHub、Bitbucket Cloud、CLIのいずれかを選ぶよう求められます。

CLIを選択し、Next stepをクリックします。
次の画面に表示される手順に従ってSnyk CLIをインストールし、Next stepをクリックします。

次に、以下のコマンドを実行してマシンを認証します。
このコマンドを実行すると、ログインを求めるブラウザーウィンドウが開きます。手順を完了すると、Snyk CLIに認証が成功したことを示すプロンプトが表示されます。

次に、Next stepをクリックします。

Source codeをクリックし、ダッシュボードのSettings > Snyk Codeに移動します。ダッシュボードでEnable Snyk Codeをオンにして、コードに対してSnyk CLIを実行できるようにします。次に、Save changesをクリックします。

コードをテストするには、プロジェクトディレクトリで次のコマンドを実行します。
このコマンドはコードファイルを自動的にアップロードし、脆弱性をスキャンします。

上のスクリーンショットは、アプリケーションにパストラバーサルの脆弱性があり、攻撃者が任意のファイルを読み取れることを示しています。デモ用アプリではこの脆弱性をコードに残しておくこともできますが、より実用的なコードベースでは攻撃経路となります。
Snykを使って、プロジェクトの脆弱性をスキャンできました。
まとめ
対称暗号化と非対称暗号化は、ファイルの暗号化と復号に使う鍵が異なります。前者は共有鍵を1つ使い、後者は公開鍵と秘密鍵のペアを使います。
実装のしやすさと計算効率に優れる対称暗号化は、保存データの一括暗号化に適しています。非対称暗号化は、TLS/SSLやデジタル証明書など、小規模なデータ転送に適しています。
このガイドでは、AWS Encryption SDKを使ってエンベロープ暗号化(対称暗号化と非対称暗号化の組み合わせ)を実装しました。また、Pythonのpynaclライブラリを使って、非対称暗号化と復号も実装しました。さらに、鍵長が短い場合における2種類の暗号化方式の長所と短所を確認し、Snykのようなプラットフォームを活用してアプリケーションの脆弱性をスキャンする方法も紹介しました。
暗号化について理解を深めたところで、これらの方法を使って、自分とユーザーにとってより安全なアプリケーションを構築しましょう。そして、Snykを使って、そのアプローチが安全であることを確認してください。



