Python, являясь одним из самых популярных языков в области компьютерной и сетевой безопасности, обладает большим потенциалом в области безопасности и криптографии. В этом разделе рассматриваются криптографические функции и реализации в Python от его использования в компьютерной и сетевой безопасности до алгоритмов хэширования и шифрования / дешифрования.


Безопасное шифрование паролей

Алгоритм PBKDF2 , hashlib модулем hashlib может использоваться для выполнения безопасного хеширования паролей. Хотя этот алгоритм не может предотвратить атаки грубой силы, чтобы восстановить исходный пароль из хранимого хэша, он делает такие атаки очень дорогими.

import hashlib
import os

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

PBKDF2 может работать с любым алгоритмом дайджеста, в приведенном выше примере используется SHA256, который обычно рекомендуется. Случайную соль следует хранить вместе с хешированным паролем, вам понадобится снова, чтобы сравнить введенный пароль с сохраненным хэшем. Очень важно, чтобы каждый пароль хэшировал с другой солью. Что касается количества раундов, рекомендуется установить его как можно выше для вашего приложения .

Если вы хотите получить результат в шестнадцатеричном формате, вы можете использовать модуль binascii :

import binascii
hexhash = binascii.hexlify(hash)

Примечание : В то время как PBKDF2 не плох, bcrypt и особенно scrypt считаются более сильными против атак с грубой силой. На данный момент ни одна из них не является стандартной библиотекой Python.


Вычисление сообщения

Модуль 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-='

Обратите внимание, что вы можете вызывать update произвольным числом раз, прежде чем вызывать digest который полезен для хеширования большого фрагмента файла куском. Вы также можете получить дайджест в шестнадцатеричном формате с помощью hexdigest :

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

Доступные алгоритмы хеширования

hashlib.new требует имя алгоритма, когда вы вызываете его для создания генератора. Чтобы узнать, какие алгоритмы доступны в текущем интерпретаторе Python, используйте 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 для генерации хэша для файла:

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())

Асимметричное шифрование RSA с использованием pycrypto

Преимущество асимметричного шифрования состоит в том, что сообщение может быть зашифровано без обмена секретным ключом с получателем сообщения. Отправитель просто должен знать открытый ключ получателей, это позволяет шифровать сообщение таким образом, что только назначенный получатель (имеющий соответствующий закрытый ключ) может расшифровать его. В настоящее время для этой функции требуется сторонний модуль, такой как 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)

Примечание . В приведенных выше примерах используется схема шифрования OAEP PKCS # 1. Pycrypto также реализует схему шифрования PKCS # 1 v1.5, однако это не рекомендуется для новых протоколов, однако из-за известных оговорок .


Создание сигнатур RSA с использованием pycrypto

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 также реализует новый PSS-алгоритм PKCS # 1, заменяя PKCS1_v1_5 на PKCS1_PSS в примерах, должен работать, если вы хотите использовать его. В настоящее время, похоже, нет оснований для его использования .


Симметричное шифрование с использованием 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) и фактическое сообщение для шифрования. Если у вас случайно сформированный AES-ключ, вы можете использовать его напрямую и просто генерировать случайный вектор инициализации. Однако кодовая фраза не имеет нужного размера и не рекомендуется использовать ее напрямую, учитывая, что она не является действительно случайной и, следовательно, имеет сравнительно небольшую энтропию. Вместо этого мы используем встроенную реализацию алгоритма PBKDF2 для генерации 128-битного вектора инициализации и 256-битного ключа шифрования из пароля.

Обратите внимание на случайную соль, которая важна для того, чтобы иметь различный вектор инициализации и ключ для каждого зашифрованного сообщения. Это гарантирует, в частности, что два равных сообщения не приведут к идентичному зашифрованному тексту, но также предотвращают повторное использование злоумышленниками работы, угадывающей одну кодовую фразу для сообщений, зашифрованных с помощью другой кодовой фразы. Эта соль должна храниться вместе с зашифрованным сообщением, чтобы получить один и тот же вектор инициализации и ключ для дешифрования.

Следующий код расшифровывает наше сообщение еще раз:

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:])

python Tutorials & Examples