サーチ…


前書き

Pythonは、コンピュータセキュリティとネットワークセキュリティで最も一般的な言語の1つで、セキュリティと暗号化に大きな可能性を秘めています。このトピックでは、コンピュータとネットワークのセキュリティ、ハッシング、暗号化/復号化アルゴリズムに使用されているPythonの暗号化機能と実装について説明します。

構文

  • hashlib.new(name)
  • hashlib.pbkdf2_hmac(名前、パスワード、ソルト、ラウンド、dklen =なし)

備考

hashlib多くのメソッドでは、文字列ではなくバイトのバッファとして解釈可能な値を渡す必要があります。これは、 hashlib.new().update()およびhashlib.pbkdf2_hmacです。文字列がある場合は、文字bを文字列の先頭に付加することで、文字列をバイトバッファに変換することができます。

  "This is a string"
 b"This is a buffer of bytes"

メッセージダイジェストの計算

hashlibモジュールでは、 newメソッドを使用してメッセージダイジェストジェネレータを作成できます。これらのジェネレータは任意の文字列を固定長ダイジェストに変換します:

import hashlib

h = hashlib.new('sha256')
h.update(b'Nobody expects the Spanish Inquisition.')
h.digest()
# ==> b'.\xdf\xda\xdaVR[\x12\x90\xff\x16\xfb\x17D\xcf\xb4\x82\xdd)\x14\xff\xbc\xb6Iy\x0c\x0eX\x9eF-='

大きなファイルチャンクをチャンクでハッシュするのに便利なdigestを呼び出す前に、 updateを任意の回数呼び出すことができます。 hexdigestを使ってダイジェストを16進形式で取得することもできます:

h.hexdigest()
# ==> '2edfdada56525b1290ff16fb1744cfb482dd2914ffbcb649790c0e589e462d3d'

利用可能なハッシングアルゴリズム

hashlib.newは、ジェネレータを生成するためにアルゴリズムを呼び出すときにアルゴリズムの名前が必要です。現在のPythonインタプリタで利用可能なアルゴリズムを調べるには、 hashlib.algorithms_available使用してhashlib.algorithms_available

import hashlib
hashlib.algorithms_available
# ==> {'sha256', 'DSA-SHA', 'SHA512', 'SHA224', 'dsaWithSHA', 'SHA', 'RIPEMD160', 'ecdsa-with-SHA1', 'sha1', 'SHA384', 'md5', 'SHA1', 'MD5', 'MD4', 'SHA256', 'sha384', 'md4', 'ripemd160', 'sha224', 'sha512', 'DSA', 'dsaEncryption', 'sha', 'whirlpool'}

返されるリストは、プラットフォームとインタプリタによって異なります。アルゴリズムが利用可能であることを確認してください。

hashlib.algorithms_guaranteedを使用して利用可能な、すべてのプラットフォームとインタプリタで利用できることが保証されているアルゴリズムもあります。

hashlib.algorithms_guaranteed
# ==> {'sha256', 'sha384', 'sha1', 'sha224', 'md5', 'sha512'}

安全なパスワードのハッシュ

hashlibモジュールによって公開されたPBKDF2アルゴリズムを使用して、安全なパスワードハッシングを実行できます。このアルゴリズムでは、格納されたハッシュから元のパスワードを回復するために無差別攻撃を防ぐことはできませんが、そのような攻撃は非常に高価になります。

import hashlib
import os

salt = os.urandom(16)
hash = hashlib.pbkdf2_hmac('sha256', b'password', salt, 100000)

PBKDF2は任意のダイジェストアルゴリズムで動作できますが、上記の例では通常推奨されるSHA256を使用しています。ランダムな塩は、ハッシュされたパスワードと共に保存されるべきです。入力されたパスワードと保存されたハッシュを比較するために、もう一度それを必要とします。各パスワードは異なる塩でハッシュされることが不可欠です。ラウンド数については、アプリケーションの可能な限り高く設定することお勧めします

結果を16進数で表示するには、 binasciiモジュールを使用します。

import binascii
hexhash = binascii.hexlify(hash)

:PBKDF2は悪くないが、 bcrypt 、特にscryptはbrute-force攻撃に対してより強力であると考えられている。現在のところ、どちらもPython標準ライブラリの一部ではありません。

ファイルハッシング

ハッシュは、可変長シーケンスのバイトを固定長シーケンスに変換する関数です。ファイルをハッシュすることは、多くの理由で利点があります。ハッシュを使用して、2つのファイルが同一であるかどうかをチェックしたり、ファイルの内容が破損または変更されていないことを確認したりすることができます。

hashlibを使ってファイルのハッシュを生成することができます:

import hashlib

hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
    contents = f.read()
    hasher.update(contents)

print hasher.hexdigest() 

大きなファイルの場合、固定長のバッファを使用できます。

import hashlib
SIZE = 65536
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
    buffer = f.read(SIZE)
    while len(buffer) > 0:
        hasher.update(buffer)
        buffer = f.read(SIZE)
print(hasher.hexdigest())

pycryptoを使用した対称暗号化

Pythonに組み込まれている暗号機能は現在、ハッシングに限定されています。暗号化にはpycryptoのような第三者のモジュールが必要です。例えば、これは、対称暗号化のための最先端技術と考えられるAESアルゴリズムを提供する。次のコードは、パスフレーズを使用して特定のメッセージを暗号化します。

import hashlib
import math
import os

from Crypto.Cipher import AES

IV_SIZE = 16    # 128 bit, fixed for the AES algorithm
KEY_SIZE = 32   # 256 bit meaning AES-256, can also be 128 or 192 bits
SALT_SIZE = 16  # This size is arbitrary

cleartext = b'Lorem ipsum'
password = b'highly secure encryption password'
salt = os.urandom(SALT_SIZE)
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
                              dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]

encrypted = salt + AES.new(key, AES.MODE_CFB, iv).encrypt(cleartext)

AESアルゴリズムは、暗号化キー、初期化ベクトル(IV)、および暗号化される実際のメッセージという3つのパラメータをとります。あなたがランダムに生成されたAESキーを持っているならば、それを直接使用して、ランダム初期ベクトルを生成するだけです。しかしパスフレーズは正しいサイズを持っておらず、本当にランダムではなく、したがってエントロピーは比較的小さいと仮定すれば、直接使用することは推奨できません。代わりに、 PBKDF2アルゴリズムの組み込みの実装を使用して、パスワードから128ビットの初期化ベクトルと256ビットの暗号化キーを生成します。

暗号化されたメッセージごとに異なる初期化ベクトルと鍵を持つことが重要なランダムな塩に注意してください。これにより、特に2つの等しいメッセージが同一の暗号化されたテキストにならないことが保証されますが、攻撃者は別のパスフレーズで暗号化されたメッセージに対してあるパスフレーズを推測した作業を再利用できません。解読のために同じ初期化ベクトルと鍵を導出するために、この塩は暗号化されたメッセージと共に格納されなければならない。

次のコードは、私たちのメッセージを再度解読します:

salt = encrypted[0:SALT_SIZE]
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
                              dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]
cleartext = AES.new(key, AES.MODE_CFB, iv).decrypt(encrypted[SALT_SIZE:])

pycryptoを使用したRSA署名の生成

RSAを使用してメッセージ署名を作成できます。有効な署名は、秘密のRSA鍵へのアクセスでのみ生成することができ、他方では、対応する公開鍵だけで検証することが可能である。相手側があなたの公開鍵を知っている限り、あなたは署名されたメッセージを確認でき、変更されません。たとえば、電子メールに使用される方法です。現在、この機能にはpycryptoなどのサードパーティ製のモジュールが必要です。

import errno

from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

message = b'This message is from me, I promise.'

try:
    with open('privkey.pem', 'r') as f:
        key = RSA.importKey(f.read())
except IOError as e:
    if e.errno != errno.ENOENT:
        raise
    # No private key, generate a new one. This can take a few seconds.
    key = RSA.generate(4096)
    with open('privkey.pem', 'wb') as f:
        f.write(key.exportKey('PEM'))
    with open('pubkey.pem', 'wb') as f:
        f.write(key.publickey().exportKey('PEM'))

hasher = SHA256.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(hasher)

署名の検証は同様ですが、秘密鍵ではなく公開鍵を使用します。

with open('pubkey.pem', 'rb') as f:
    key = RSA.importKey(f.read())
hasher = SHA256.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(hasher, signature):
    print('Nice, the signature is valid!')
else:
    print('No, the message was signed with the wrong private key or modified')

:上記の例では、非常に一般的なPKCS#1 v1.5署名アルゴリズムを使用しています。 pycryptoは新しいPKCS#1 PSSアルゴリズムも実装していますPKCS1_v1_5PKCS1_PSSで置き換えると、その例を使用したい場合に動作します。現在、 それを使用する理由はほとんどないようです。

pycryptoを使用した非対称RSA暗号化

非対称暗号化には、メッセージの受信者と秘密鍵を交換することなく、メッセージを暗号化できるという利点があります。送信者は受信者の公開鍵を知るだけでよく、指定された受信者(対応する秘密鍵を持っている)だけがそれを解読できるようにメッセージを暗号化することができます。現在、この機能にはpycryptoなどのサードパーティ製のモジュールが必要です。

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA

message = b'This is a very secret message.'

with open('pubkey.pem', 'rb') as f:
    key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
encrypted = cipher.encrypt(message)

受信者は、正しい秘密鍵を持っている場合は、メッセージを復号化できます。

with open('privkey.pem', 'rb') as f:
    key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
decrypted = cipher.decrypt(encrypted)

:上記の例では、PKCS#1 OAEP暗号化方式を使用しています。 pycryptoはPKCS#1 v1.5暗号化方式も実装していますが、これは既知の警告のため新しいプロトコルには推奨されません。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow