Buscar..


Introducción

Python, siendo uno de los lenguajes más populares en seguridad de computadoras y redes, tiene un gran potencial en seguridad y criptografía. Este tema trata sobre las funciones criptográficas y las implementaciones en Python desde sus usos en seguridad informática y de red hasta algoritmos de hash y cifrado / descifrado.

Sintaxis

  • hashlib.new (nombre)
  • hashlib.pbkdf2_hmac (nombre, contraseña, salt, rounds, dklen = Ninguno)

Observaciones

Muchos de los métodos en hashlib requerirán que usted pase valores interpretables como buffers de bytes, en lugar de cadenas. Este es el caso de hashlib.new().update() así como hashlib.pbkdf2_hmac . Si tiene una cadena, puede convertirla en un búfer de bytes al añadir el carácter b al comienzo de la cadena:

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

Cálculo de un resumen del mensaje

El módulo hashlib permite crear generadores de resumen de mensajes a través del new método. Estos generadores convertirán una cadena arbitraria en un compendio de longitud fija:

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-='

Tenga en cuenta que puede llamar a la update un número arbitrario de veces antes de llamar al digest que es útil para agrupar un fragmento de archivo grande por fragmento. También puede obtener el resumen en formato hexadecimal utilizando hexdigest :

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

Algoritmos de hash disponibles

hashlib.new requiere el nombre de un algoritmo cuando lo llama para producir un generador. Para averiguar qué algoritmos están disponibles en el intérprete de Python actual, use 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'}

La lista devuelta variará según la plataforma y el intérprete; Asegúrate de comprobar que tu algoritmo está disponible.

También hay algunos algoritmos que están garantizados para estar disponibles en todas las plataformas e intérpretes, que están disponibles usando hashlib.algorithms_guaranteed :

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

Contraseña segura Hashing

El algoritmo hashlib expuesto por el módulo hashlib se puede usar para realizar el hashing de contraseña seguro. Si bien este algoritmo no puede evitar los ataques de fuerza bruta para recuperar la contraseña original del hash almacenado, hace que dichos ataques sean muy costosos.

import hashlib
import os

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

PBKDF2 puede funcionar con cualquier algoritmo de resumen, el ejemplo anterior utiliza SHA256, que generalmente se recomienda. La sal aleatoria debe almacenarse junto con la contraseña hash, la necesitará nuevamente para comparar una contraseña ingresada con el hash almacenado. Es esencial que cada contraseña tenga una sal diferente. En cuanto al número de rondas, se recomienda establecerlo lo más alto posible para su aplicación .

Si desea que el resultado sea hexadecimal, puede usar el módulo binascii :

import binascii
hexhash = binascii.hexlify(hash)

Nota : si bien PBKDF2 no es malo, bcrypt y especialmente scrypt se consideran más fuertes contra los ataques de fuerza bruta. Tampoco es parte de la biblioteca estándar de Python en este momento.

Hash de archivo

Un hash es una función que convierte una secuencia de longitud variable de bytes en una secuencia de longitud fija. Los archivos de hash pueden ser ventajosos por muchas razones. Los hash se pueden usar para verificar si dos archivos son idénticos o verificar que el contenido de un archivo no se haya dañado o cambiado.

Puedes usar hashlib para generar un hash para un archivo:

import hashlib

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

print hasher.hexdigest() 

Para archivos más grandes, se puede usar un búfer de longitud fija:

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

Cifrado simétrico utilizando pycrypto

La funcionalidad criptográfica incorporada de Python se limita actualmente al hashing. El cifrado requiere un módulo de terceros como pycrypto . Por ejemplo, proporciona el algoritmo AES que se considera el estado de la técnica para el cifrado simétrico. El siguiente código cifrará un mensaje dado usando una frase de contraseña:

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)

El algoritmo AES toma tres parámetros: clave de cifrado, vector de inicialización (IV) y el mensaje real a cifrar. Si tiene una clave AES generada aleatoriamente, puede usarla directamente y simplemente generar un vector de inicialización aleatorio. Sin embargo, una frase de contraseña no tiene el tamaño correcto, ni sería recomendable utilizarla directamente, ya que no es realmente aleatoria y, por lo tanto, tiene una entropía relativamente pequeña. En su lugar, usamos la implementación incorporada del algoritmo PBKDF2 para generar un vector de inicialización de 128 bits y una clave de cifrado de 256 bits a partir de la contraseña.

Tenga en cuenta la sal aleatoria que es importante tener un vector de inicialización diferente y una clave para cada mensaje cifrado. Esto garantiza en particular que dos mensajes iguales no darán como resultado un texto cifrado idéntico, pero también evita que los atacantes reutilicen el trabajo empleado en adivinar una frase de contraseña en los mensajes cifrados con otra frase de contraseña. Esta sal debe almacenarse junto con el mensaje cifrado para poder derivar el mismo vector de inicialización y la clave para descifrar.

El siguiente código volverá a descifrar nuestro mensaje:

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

Generando firmas RSA usando pycrypto

RSA se puede utilizar para crear una firma de mensaje. Una firma válida solo se puede generar con acceso a la clave RSA privada, por lo que la validación es posible con la clave pública correspondiente. Por lo tanto, mientras la otra parte sepa su clave pública, podrán verificar el mensaje que usted firmará y no se modificará, por ejemplo, un enfoque utilizado para el correo electrónico. Actualmente, se requiere un módulo de terceros como pycrypto para esta funcionalidad.

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)

La verificación de la firma funciona de manera similar pero usa la clave pública en lugar de la clave privada:

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

Nota : los ejemplos anteriores utilizan el algoritmo de firma PKCS # 1 v1.5, que es muy común. pycrypto también implementa el nuevo algoritmo PKCS # 1 PSS, reemplazando PKCS1_v1_5 por PKCS1_PSS en los ejemplos debería funcionar si desea usar ese. Sin embargo, actualmente parece que hay pocas razones para usarlo .

Cifrado RSA asimétrico utilizando pycrypto

El cifrado asimétrico tiene la ventaja de que un mensaje puede cifrarse sin intercambiar una clave secreta con el destinatario del mensaje. El remitente simplemente necesita conocer la clave pública de los destinatarios, esto permite cifrar el mensaje de manera que solo el destinatario designado (que tiene la clave privada correspondiente) pueda descifrarlo. Actualmente, se requiere un módulo de terceros como pycrypto para esta funcionalidad.

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)

El destinatario puede descifrar el mensaje si tiene la clave privada correcta:

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

Nota : Los ejemplos anteriores utilizan el esquema de cifrado OPAP PKCS # 1. pycrypto también implementa el esquema de cifrado PKCS # 1 v1.5, este no se recomienda para nuevos protocolos, sin embargo, debido a advertencias conocidas .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow