私はPyCryptoで長い間苦しんでいましたが、最終的にこの記事が判明し、次のプロトコルの完全な実装になりました。
派遣段階:
1.アリスはデジタル署名でメッセージに署名し、ボブの公開鍵で暗号化します ( 非対称アルゴリズム )。
2.アリスはランダムセッションキーを生成し、このキーでメッセージを暗号化します( 対称アルゴリズムを使用 )。
3.セッションキーは、ボブの公開キー(非対称アルゴリズム)で暗号化されます。
アリスはボブに暗号化されたメッセージ、署名、暗号化されたセッションキーを送信します。
入学の段階:
ボブは、アリスの暗号化されたメッセージ、署名、および暗号化されたセッションキーを受け取ります。
4.ボブは、プライベートキーでセッションキーを復号化します。
5.このようにして取得したセッションキーを使用して、ボブはアリスの暗号化されたメッセージを解読します。
6.ボブはアリスの署名を解読して検証します。
上記のプロトコルは、次のように動作するハイブリッド暗号化システムです。 対称AESアルゴリズム(またはその他)の場合、ランダムセッションキーが生成されます。
このようなキーのサイズは通常、128〜512ビットです(アルゴリズムによって異なります)。 次に、対称アルゴリズムを使用してメッセージを暗号化します。 ブロック暗号化の場合、 暗号化モード (たとえば、 CBC )を使用する必要があります。これにより、ブロックの長さを超える長さのメッセージを暗号化できます。 ランダムキー自体については、メッセージ受信者の公開キーを使用して暗号化する必要があり、この段階でRSA公開キー暗号システムが使用されます。
セッションキーは短いため、暗号化には少し時間がかかります。 非対称アルゴリズムを使用したメッセージセットの暗号化は、計算がより複雑なタスクであるため、ここでは対称暗号化をお勧めします。 次に、対称アルゴリズムで暗号化されたメッセージと、暗号化された形式の対応するキーを送信するだけで十分です。 受信者は最初に自分の秘密キーを使用してキーを解読し、次に受信したキーを使用してメッセージ全体を受信します。
アリスとボブのキーペアを生成することから始めましょう。
from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA # key generation Alisa privatekey = RSA.generate(2048) f = open('c:\cipher\\alisaprivatekey.txt','wb') f.write(bytes(privatekey.exportKey('PEM'))); f.close() publickey = privatekey.publickey() f = open('c:\cipher\\alisapublickey.txt','wb') f.write(bytes(publickey.exportKey('PEM'))); f.close() # key generation Bob privatekey = RSA.generate(2048) f = open('c:\cipher\\bobprivatekey.txt','wb') f.write(bytes(privatekey.exportKey('PEM'))); f.close() publickey = privatekey.publickey() f = open('c:\cipher\\bobpublickey.txt','wb') f.write(bytes(publickey.exportKey('PEM'))); f.close()
現在、RSAベースの暗号化システムは、2048ビットのキーサイズから始めて、信頼性が高いと考えられています。
RSAシステムでは、暗号化され、デジタル署名を含むメッセージを作成できます。 これを行うには、作成者は最初にデジタル署名をメッセージに追加してから、結果のペア(メッセージ自体と署名で構成される)を受信者に属する公開キーで暗号化する必要があります。 受信者は自分の秘密鍵を使用して受信したメッセージを解読し、公開鍵を使用して作成者の署名を検証します。
一方向暗号ハッシュ関数を使用すると、上記のデジタル署名アルゴリズムを最適化できます。 メッセージ自体ではなく、メッセージから取得したハッシュ関数の値を暗号化します。 この方法には次の利点があります。
1.計算の複雑さを軽減します。 原則として、ドキュメントはハッシュよりもはるかに大きくなります。
2.暗号強度を高めます。 公開鍵を使用すると、暗号解読者はメッセージの署名を取得できず、ハッシュのみを取得できます。
3.互換性の確保。 ほとんどのアルゴリズムはデータビットの文字列で動作しますが、一部のアルゴリズムは異なる表現を使用します。 ハッシュ関数を使用して、任意の入力テキストを適切な形式に変換できます。
ハッシュ関数は、数学またはその他の関数で、可変長の入力文字列(プリイメージと呼ばれる)を受け取り、それを固定長の文字列(通常はより短い)(ハッシュ関数の値と呼ばれる)に変換します。 ハッシュ関数の意味は、逆画像の特性属性を取得することです。これは、逆問題を解くときにさまざまな逆画像が分析される値です。 単方向ハッシュ関数は、一方向でのみ機能するハッシュ関数です。逆画像からハッシュ関数の値を計算するのは簡単ですが、ハッシュ値が所定の値に等しい逆画像を作成することは困難です。 ハッシュ関数は開いており、計算の秘密は存在しません。 一方向ハッシュ関数のセキュリティは、正確には一方向関数にあります。
プロトコルの最初の段落の記述に進みます。
1.アリスはデジタル署名でメッセージに署名し、ボブの公開鍵(非対称RSAアルゴリズム)で暗号化します。
from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP # creation of signature f = open('c:\cipher\plaintext.txt','rb') plaintext = f.read(); f.close() privatekey = RSA.importKey(open('c:\cipher\\alisaprivatekey.txt','rb').read()) myhash = SHA.new(plaintext) signature = PKCS1_v1_5.new(privatekey) signature = signature.sign(myhash) # signature encrypt publickey = RSA.importKey(open('c:\cipher\\bobpublickey.txt','rb').read()) cipherrsa = PKCS1_OAEP.new(publickey) sig = cipherrsa.encrypt(signature[:128]) sig = sig + cipherrsa.encrypt(signature[128:]) f = open('c:\cipher\signature.txt','wb') f.write(bytes(sig)); f.close()
次のリストでは、次の2つのプロトコル句のコード:
2.アリスはランダムセッションキーを生成し、このキーでメッセージを暗号化します(対称AESアルゴリズムを使用)。
3.セッションキーは、ボブの公開キー(RSA非対称アルゴリズム)で暗号化されます。
from Crypto.Cipher import AES from Crypto import Random from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA # creation 256 bit session key sessionkey = Random.new().read(32) # 256 bit # encryption AES of the message f = open('c:\cipher\plaintext.txt','rb') plaintext = f.read(); f.close() iv = Random.new().read(16) # 128 bit obj = AES.new(sessionkey, AES.MODE_CFB, iv) ciphertext = iv + obj.encrypt(plaintext) f = open('c:\cipher\plaintext.txt','wb') f.write(bytes(ciphertext)); f.close() # encryption RSA of the session key publickey = RSA.importKey(open('c:\cipher\\bobpublickey.txt','rb').read()) cipherrsa = PKCS1_OAEP.new(publickey) sessionkey = cipherrsa.encrypt(sessionkey) f = open('c:\cipher\sessionkey.txt','wb') f.write(bytes(sessionkey)); f.close()
CFB暗号化モードには、初期化ベクトル(IV) (変数iv)が必要です。
暗号化では、初期化ベクトル(IV)は一定の数であり、原則としてランダムまたは疑似ランダムでなければなりません。 ランダム性は、同じキーでスキームを再利用するときに、攻撃者が暗号化されたメッセージのセグメント間の関係を推測できないようにするセマンティックセキュリティを実現するために重要です。 初期化ベクトルは暗号化されておらず、暗号化されたメッセージの前に保存されます。
次に、アリスは暗号化されたメッセージ、署名、および暗号化されたセッションキーをボブに送信します。
ボブは、アリスの暗号化されたメッセージ、署名、および暗号化されたセッションキーを受け取ります。
4.ボブは、プライベートキーでセッションキーを復号化します。
5.このようにして取得したセッションキーを使用して、ボブはアリスの暗号化されたメッセージを解読します。
from Crypto.Cipher import AES from Crypto import Random from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA # decryption session key privatekey = RSA.importKey(open('c:\cipher\\bobprivatekey.txt','rb').read()) cipherrsa = PKCS1_OAEP.new(privatekey) f = open('c:\cipher\sessionkey.txt','rb') sessionkey = f.read(); f.close() sessionkey = cipherrsa.decrypt(sessionkey) # decryption message f = open('c:\cipher\plaintext.txt','rb') ciphertext = f.read(); f.close() iv = ciphertext[:16] obj = AES.new(sessionkey, AES.MODE_CFB, iv) plaintext = obj.decrypt(ciphertext) plaintext = plaintext[16:] f = open('c:\cipher\plaintext.txt','wb') f.write(bytes(plaintext)); f.close()
最後の段階:
6.ボブはアリスの署名を解読して検証します。
from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP # decryption signature f = open('c:\cipher\signature.txt','rb') signature = f.read(); f.close() privatekey = RSA.importKey(open('c:\cipher\\bobprivatekey.txt','rb').read()) cipherrsa = PKCS1_OAEP.new(privatekey) sig = cipherrsa.decrypt(signature[:256]) sig = sig + cipherrsa.decrypt(signature[256:]) # signature verification f = open('c:\cipher\plaintext.txt','rb') plaintext = f.read(); f.close() publickey = RSA.importKey(open('c:\cipher\\alisapublickey.txt','rb').read()) myhash = SHA.new(plaintext) signature = PKCS1_v1_5.new(publickey) test = signature.verify(myhash, sig)
RSAセキュリティは、2つの大きな素数の積の因数分解問題の複雑さに基づいています。 大きい数の整数の因数分解は、非常に複雑なタスクです。 この問題を迅速に解決する既知の方法はありません。 因数分解は、比較的簡単に計算できますが、逆変換が非常に困難な一方向関数の候補です。 つまり、xを知ることはf(x)を計算するのは簡単ですが、既知のf(x)からxを計算するのは簡単ではありません。 ここで、「簡単ではない」とは、世界中のすべてのコンピューターがこの問題に勝ったとしても、xをf(x)で計算するのに数百万年かかることを意味します。
参照:
ブルース・シュナイアー-応用暗号