¿Una forma sencilla de codificar una cadena de acuerdo con una contraseña?


123

¿Python tiene una forma simple e incorporada de codificar / decodificar cadenas usando una contraseña?

Algo como esto:

>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'

Entonces, la cadena "John Doe" se cifra como 'sjkl28cn2sx0'. Para obtener la cadena original, "desbloquearía" esa cadena con la clave 'mypass', que es una contraseña en mi código fuente. Me gustaría que esta fuera la forma en que puedo cifrar / descifrar un documento de Word con una contraseña.

Me gustaría usar estas cadenas encriptadas como parámetros de URL. Mi objetivo es la ofuscación, no una seguridad sólida; no se codifica nada crítico para la misión. Me doy cuenta de que podría usar una tabla de base de datos para almacenar claves y valores, pero estoy tratando de ser minimalista.


28
El término "contraseña" aquí es inapropiado. Está usando esto como una CLAVE criptográfica y debe usar esa terminología para evitar confusiones en sus preguntas, así como en cualquier documento, comentario, especificación, planes de prueba, etc.
Jim Dennis

2
"Me gustaría que esta fuera la forma en que puedo cifrar / descifrar un documento de Word con una contraseña". Word ya tiene una opción incorporada para cifrar sus documentos si solo necesita cifrar documentos de Word.
Byron Filer

2
Curiosamente, según este artículo de investigación sobre problemas de almacenamiento de contraseñas como este , los desarrolladores que utilizan Stack Overflow tienden a producir código menos seguro. Vaya, me pregunto por qué.
bosque

Además, uno debe leer esta respuesta de seguridad
SE

Uno no simplemente implementa la codificación / decodificación simplemente
luckyging3r

Respuestas:


70

Suponiendo que solo está buscando una ofuscación simple que oscurezca las cosas para el observador muy casual, y no está buscando usar bibliotecas de terceros. Recomendaría algo como el cifrado de Vigenere. Es uno de los cifrados antiguos simples más fuertes.

Cifrado de Vigenère

Es rápido y fácil de implementar. Algo como:

import base64

def encode(key, string):
    encoded_chars = []
    for i in xrange(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return base64.urlsafe_b64encode(encoded_string)

Decodificar es prácticamente lo mismo, excepto que resta la clave.

Es mucho más difícil de romper si las cadenas que está codificando son cortas y / o si es difícil adivinar la longitud de la frase de contraseña utilizada.

Si está buscando algo criptográfico, PyCrypto es probablemente su mejor opción, aunque las respuestas anteriores pasan por alto algunos detalles: el modo ECB en PyCrypto requiere que su mensaje tenga una longitud múltiple de 16 caracteres. Entonces, debes rellenar. Además, si desea utilizarlos como parámetros de URL, utilice base64.urlsafe_b64_encode(), en lugar del estándar. Esto reemplaza algunos de los caracteres en el alfabeto base64 con caracteres seguros para URL (como sugiere su nombre).

Sin embargo, debe estar ABSOLUTAMENTE seguro de que esta capa muy delgada de ofuscación es suficiente para sus necesidades antes de usar esto. El artículo de Wikipedia al que me vinculé proporciona instrucciones detalladas para descifrar el cifrado, por lo que cualquier persona con una cantidad moderada de determinación podría descifrarlo fácilmente.


6
Arreglé el script de smehmood y agregué la función de decodificación gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester

3
¡Atención! El código de smehmood y la corrección de Adrian Mester solo funcionan para cadenas con caracteres del rango ascii inferior. Consulte la precedencia del operador%, la entrada Unicode, etc. Consulte la respuesta de qneill para conocer el código de trabajo
le_m

2
@smehmood Recibo el siguiente error'str' object cannot be interpreted as an integer
Rohit Khatri

3
"for i in xrange (string)" podría necesitar cambiar a "for i in xrange (len (string))"
user3113626

2
codificador y decodificador para python 2 y 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman

71

Como declaras explícitamente que quieres oscuridad y no seguridad, evitaremos reprenderte por la debilidad de lo que sugieres :)

Entonces, usando PyCrypto:

import base64
from Crypto.Cipher import AES

msg_text = b'test some plain text here'.rjust(32)
secret_key = b'1234567890123456'

cipher = AES.new(secret_key,AES.MODE_ECB) # never use ECB in strong systems obviously
encoded = base64.b64encode(cipher.encrypt(msg_text))
print(encoded)
decoded = cipher.decrypt(base64.b64decode(encoded))
print(decoded)

Si alguien se apodera de su base de datos y su base de código, podrá decodificar los datos cifrados. ¡Mantén tu secret_keycaja fuerte!


3
No creo que esto funcione a menos que msg_text sea un múltiplo de 16 bytes de longitud, ya que el cifrado AES requiere bloques que sean múltiplos de 16 de longitud. Una implementación funcional para msg_text de longitud arbitraria necesitaría agregar relleno a la cadena para obtener un múltiplo de 16 de longitud.
tohster

6
Un ejemplo con relleno: paste.ubuntu.com/11024555 Funciona con contraseña arbitraria y longitud de mensaje.
iman

3
@Ethan no, esta encryptfunción en particular tiene estado dlitz.net/software/pycrypto/api/current/… así que no debes intentar reutilizarla.
Será el

1
@Will +1 Gracias por la información y el enlace. Me salvó de una corrección de errores muy costosa en el futuro.
Ethan

3
re - "nunca use ECB en sistemas fuertes obviamente": solo estoy experimentando con esto para mi propia diversión. pero vi el comentario anterior en su código. para aquellos de nosotros con una experiencia mínima en seguridad / encriptación / teoría de la información, ¿por qué "nunca usar"? tal vez necesite otra pregunta ... o tal vez haya un enlace al respecto.
Trevor Boyd Smith

69

Python no tiene esquemas de cifrado integrados, no. También debe tomarse en serio el almacenamiento de datos cifrados; esquemas de cifrado triviales que un desarrollador entiende que son inseguros y un esquema de juguete bien puede ser confundido con un esquema seguro por un desarrollador menos experimentado. Si encripta, encripte correctamente.

Sin embargo, no necesita hacer mucho trabajo para implementar un esquema de cifrado adecuado. En primer lugar, no reinvente la rueda de criptografía , use una biblioteca de criptografía confiable para manejar esto por usted. Para Python 3, esa biblioteca confiable es cryptography.

También recomiendo que el cifrado y el descifrado se apliquen a los bytes ; codificar mensajes de texto en bytes primero; stringvalue.encode()codifica a UTF8, fácilmente revertido nuevamente usando bytesvalue.decode().

Por último, pero no menos importante, a la hora de cifrar y descifrar, hablamos de claves , no de contraseñas. Una clave no debe ser memorable por humanos, es algo que usted almacena en un lugar secreto pero legible por máquina, mientras que una contraseña a menudo puede ser legible y memorizada por humanos. Usted puede obtener una clave de una contraseña, con un poco de cuidado.

Pero para una aplicación web o un proceso que se ejecuta en un clúster sin atención humana para seguir ejecutándolo, debe usar una clave. Las contraseñas son para cuando solo un usuario final necesita acceder a la información específica. Incluso entonces, normalmente protege la aplicación con una contraseña y luego intercambia información cifrada mediante una clave, tal vez una adjunta a la cuenta de usuario.

Cifrado de clave simétrica

Fernet - AES CBC + HMAC, muy recomendable

La cryptographybiblioteca incluye la receta de Fernet , una receta de mejores prácticas para usar la criptografía. Fernet es un estándar abierto , con implementaciones listas en una amplia gama de lenguajes de programación y empaqueta el cifrado AES CBC para usted con información de versión, una marca de tiempo y una firma HMAC para evitar la manipulación de mensajes.

Fernet hace que sea muy fácil cifrar y descifrar mensajes y mantenerlo seguro. Es el método ideal para cifrar datos con un secreto.

Te recomiendo que lo uses Fernet.generate_key()para generar una clave segura. También puede usar una contraseña (siguiente sección), pero una clave secreta completa de 32 bytes (16 bytes para cifrar, más otros 16 para la firma) será más segura que la mayoría de las contraseñas que pueda imaginar.

La clave que genera Fernet es un bytesobjeto con URL y caracteres base64 seguros para el archivo, por lo que se puede imprimir:

from cryptography.fernet import Fernet

key = Fernet.generate_key()  # store in a secure location
print("Key:", key.decode())

Para cifrar o descifrar mensajes, cree una Fernet()instancia con la clave dada y llame al Fernet.encrypt()o Fernet.decrypt(), tanto el mensaje de texto sin formato para cifrar como el token cifrado son bytesobjetos.

encrypt()y las decrypt()funciones se verían así:

from cryptography.fernet import Fernet

def encrypt(message: bytes, key: bytes) -> bytes:
    return Fernet(key).encrypt(message)

def decrypt(token: bytes, key: bytes) -> bytes:
    return Fernet(key).decrypt(token)

Manifestación:

>>> key = Fernet.generate_key()
>>> print(key.decode())
GZWKEhHGNopxRdOHS4H4IyKhLQ8lwnyU7vRLrM3sebY=
>>> message = 'John Doe'
>>> encrypt(message.encode(), key)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> token = _
>>> decrypt(token, key).decode()
'John Doe'

Fernet con contraseña - clave derivada de contraseña, debilita un poco la seguridad

Puede utilizar una contraseña en lugar de una clave secreta, siempre que utilice un método de derivación de claves seguro . Luego, debe incluir la sal y el recuento de iteraciones HMAC en el mensaje, por lo que el valor cifrado ya no es compatible con Fernet sin separar primero sal, recuento y token de Fernet:

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

backend = default_backend()
iterations = 100_000

def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
    """Derive a secret key from a given password and salt"""
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(), length=32, salt=salt,
        iterations=iterations, backend=backend)
    return b64e(kdf.derive(password))

def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
    salt = secrets.token_bytes(16)
    key = _derive_key(password.encode(), salt, iterations)
    return b64e(
        b'%b%b%b' % (
            salt,
            iterations.to_bytes(4, 'big'),
            b64d(Fernet(key).encrypt(message)),
        )
    )

def password_decrypt(token: bytes, password: str) -> bytes:
    decoded = b64d(token)
    salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
    iterations = int.from_bytes(iter, 'big')
    key = _derive_key(password.encode(), salt, iterations)
    return Fernet(key).decrypt(token)

Manifestación:

>>> message = 'John Doe'
>>> password = 'mypass'
>>> password_encrypt(message.encode(), password)
b'9Ljs-w8IRM3XT1NDBbSBuQABhqCAAAAAAFyJdhiCPXms2vQHO7o81xZJn5r8_PAtro8Qpw48kdKrq4vt-551BCUbcErb_GyYRz8SVsu8hxTXvvKOn9QdewRGDfwx'
>>> token = _
>>> password_decrypt(token, password).decode()
'John Doe'

La inclusión de la sal en la salida permite utilizar un valor de sal aleatorio, lo que a su vez garantiza que la salida cifrada sea totalmente aleatoria independientemente de la reutilización de la contraseña o la repetición de mensajes. La inclusión del recuento de iteraciones garantiza que pueda ajustar los aumentos del rendimiento de la CPU con el tiempo sin perder la capacidad de descifrar mensajes más antiguos.

Una contraseña sola puede ser tan segura como una clave aleatoria Fernet de 32 bytes, siempre que genere una contraseña aleatoria adecuada a partir de un grupo de tamaño similar. 32 bytes le dan 256 ^ 32 números de claves, por lo que si usa un alfabeto de 74 caracteres (26 superiores, 26 inferiores, 10 dígitos y 12 símbolos posibles), entonces su contraseña debe tener al menos math.ceil(math.log(256 ** 32, 74))== 42 caracteres. Sin embargo, una mayor cantidad de iteraciones de HMAC bien seleccionadas puede mitigar un poco la falta de entropía, ya que esto hace que sea mucho más costoso para un atacante ingresar por la fuerza bruta.

Solo sepa que elegir una contraseña más corta pero razonablemente segura no paralizará este esquema, solo reduce el número de valores posibles que un atacante de fuerza bruta tendría que buscar; asegúrese de elegir una contraseña lo suficientemente segura para sus requisitos de seguridad .

Alternativas

Oscureciendo

Una alternativa es no cifrar . No se sienta tentado a usar un cifrado de baja seguridad o una implementación casera de, digamos Vignere. No hay seguridad en estos enfoques, pero pueden darle a un desarrollador sin experiencia que se le encomiende la tarea de mantener su código en el futuro la ilusión de seguridad, que es peor que ninguna seguridad.

Si todo lo que necesita es oscuridad, simplemente base64 los datos; para los requisitos de seguridad URL, la base64.urlsafe_b64encode()función está bien. No use una contraseña aquí, solo codifíquela y listo. Como máximo, agregue algo de compresión (como zlib):

import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

def obscure(data: bytes) -> bytes:
    return b64e(zlib.compress(data, 9))

def unobscure(obscured: bytes) -> bytes:
    return zlib.decompress(b64d(obscured))

Esto se convierte b'Hello world!'en b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Integridad solamente

Si todo lo que necesita es una forma de asegurarse de que se puede confiar en que los datos no se modificarán después de haber sido enviados a un cliente que no son de confianza y recibidos de vuelta, entonces desea firmar los datos, puede usar la hmacbiblioteca para esto con SHA1 (aún considerado seguro para la firma HMAC ) o mejor:

import hmac
import hashlib

def sign(data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    assert len(key) >= algorithm().digest_size, (
        "Key must be at least as long as the digest size of the "
        "hashing algorithm"
    )
    return hmac.new(key, data, algorithm).digest()

def verify(signature: bytes, data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    expected = sign(data, key, algorithm)
    return hmac.compare_digest(expected, signature)

Use esto para firmar datos, luego adjunte la firma con los datos y envíelo al cliente. Cuando reciba los datos, divida los datos y la firma y verifique. Establecí el algoritmo predeterminado en SHA256, por lo que necesitará una clave de 32 bytes:

key = secrets.token_bytes(32)

Es posible que desee ver la itsdangerousbiblioteca , que incluye todo esto con serialización y deserialización en varios formatos.

Usar cifrado AES-GCM para proporcionar cifrado e integridad

Fernet se basa en AEC-CBC con una firma HMAC para garantizar la integridad de los datos cifrados; un atacante malintencionado no puede alimentar su sistema con datos sin sentido para mantener su servicio ocupado funcionando en círculos con una entrada incorrecta, porque el texto cifrado está firmado.

El cifrado de bloque en modo Galois / Counter produce texto cifrado y una etiqueta para el mismo propósito, por lo que puede usarse para los mismos propósitos. La desventaja es que, a diferencia de Fernet, no existe una receta única y fácil de usar para reutilizar en otras plataformas. AES-GCM tampoco usa relleno, por lo que este texto cifrado de cifrado coincide con la longitud del mensaje de entrada (mientras que Fernet / AES-CBC cifra los mensajes en bloques de longitud fija, oscureciendo un poco la longitud del mensaje).

AES256-GCM toma el secreto habitual de 32 bytes como clave:

key = secrets.token_bytes(32)

luego usa

import binascii, time
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidTag

backend = default_backend()

def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes:
    current_time = int(time.time()).to_bytes(8, 'big')
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.GCM(iv), backend=backend)
    encryptor = cipher.encryptor()
    encryptor.authenticate_additional_data(current_time)
    ciphertext = encryptor.update(message) + encryptor.finalize()        
    return b64e(current_time + iv + ciphertext + encryptor.tag)

def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes:
    algorithm = algorithms.AES(key)
    try:
        data = b64d(token)
    except (TypeError, binascii.Error):
        raise InvalidToken
    timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:]
    if ttl is not None:
        current_time = int(time.time())
        time_encrypted, = int.from_bytes(data[:8], 'big')
        if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted:
            # too old or created well before our current time + 1 h to account for clock skew
            raise InvalidToken
    cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend)
    decryptor = cipher.decryptor()
    decryptor.authenticate_additional_data(timestamp)
    ciphertext = data[8 + len(iv):-16]
    return decryptor.update(ciphertext) + decryptor.finalize()

He incluido una marca de tiempo para admitir los mismos casos de uso de tiempo de vida que admite Fernet.

Otros enfoques en esta página, en Python 3

AES CFB - como CBC pero sin la necesidad de rellenar

Este es el enfoque que sigue All Іѕ Vаиітy , aunque incorrectamente. Esta es la cryptographyversión, pero tenga en cuenta que incluyo el IV en el texto cifrado , no debe almacenarse como global (reutilizar un IV debilita la seguridad de la clave y almacenarlo como un módulo global significa que se volverá a generar la próxima invocación de Python, haciendo que todo el texto cifrado no se pueda cifrar):

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_cfb_encrypt(message, key):
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return b64e(iv + ciphertext)

def aes_cfb_decrypt(ciphertext, key):
    iv_ciphertext = b64d(ciphertext)
    algorithm = algorithms.AES(key)
    size = algorithm.block_size // 8
    iv, encrypted = iv_ciphertext[:size], iv_ciphertext[size:]
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted) + decryptor.finalize()

Esto carece del blindaje adicional de una firma HMAC y no hay marca de tiempo; tendrías que agregarlos tú mismo.

Lo anterior también ilustra lo fácil que es combinar incorrectamente los bloques de construcción de criptografía básica; Todo el manejo incorrecto de Vаиітy del valor de IV puede conducir a una violación de datos o que todos los mensajes encriptados sean ilegibles porque el IV se pierde. En cambio, usar Fernet lo protege de tales errores.

AES ECB - no seguro

Si implementó anteriormente el cifrado AES ECB y aún necesita admitirlo en Python 3, también puede hacerlo con cryptography. Se aplican las mismas advertencias, ECB no es lo suficientemente seguro para aplicaciones de la vida real . Reimplementando esa respuesta para Python 3, agregando manejo automático de relleno:

from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_ecb_encrypt(message, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(cipher.algorithm.block_size).padder()
    padded = padder.update(msg_text.encode()) + padder.finalize()
    return b64e(encryptor.update(padded) + encryptor.finalize())

def aes_ecb_decrypt(ciphertext, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder()
    padded = decryptor.update(b64d(ciphertext)) + decryptor.finalize()
    return unpadder.update(padded) + unpadder.finalize()

Nuevamente, esto carece de la firma HMAC, y no debería usar ECB de todos modos. Lo anterior está solo para ilustrar que cryptographypuede manejar los bloques de construcción criptográficos comunes, incluso los que realmente no debería usar.


51

El "encoded_c" mencionado en la respuesta de cifrado Vigenere de @ smehmood debería ser "key_c".

Aquí están las funciones de codificación / decodificación en funcionamiento.

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

Descargo de responsabilidad: como se implica en los comentarios, esto no debe usarse para proteger datos en una aplicación real, a menos que lea esto y no le importe hablar con abogados:

¿Qué pasa con el cifrado XOR?


2
Muy útil, gracias. He publicado una versión de Python 3 a continuación (se veía feo en los comentarios)
Ryan Barrett

1
"Ley de Schneier" : cualquiera, desde el aficionado más despistado hasta el mejor criptógrafo, puede crear un algoritmo que él mismo no pueda romper. No uses esto, ni siquiera es seguro.
zaph

3
¡Excelente! Esta versión también funciona para cadenas con acentos, mientras que la versión de @ smehmood no. Ejemplo de uso: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)funciona bien.
Basj

2
Aquí hay un complemento de texto Sublime basado en este código, que permite una fácil codificación / decodificación de texto con CTRL + SHIFT + P y luego "Eeencode" o "Dddecode".
Basj

2
@basj Gracias por el ejemplo
sk03

49

Aquí hay una versión de Python 3 de las funciones de @qneill 's respuesta :

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc).encode()).decode()

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc).decode()
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

La codificación / decodificación adicional es necesaria porque Python 3 ha dividido cadenas / matrices de bytes en dos conceptos diferentes y actualizó sus API para reflejar eso.


4
Gracias Ryan, primero escribiste @qniell
qneill

3
Para aquellos que se preguntan, las diferencias son las .encode()).decode(). en la devolución de encode(), y .decode()en la segunda línea en decode().
RolfBly

2
Hmm, el código codificado no es realmente único, ejecuté una prueba y muestra cada clave de código de 11,22,33,44, ..., 88,99,111,222, ... siempre tiene otro mismo código que antes. Pero lo agradezco
Hzzkygcs

1
@Ryan Barrett, ¿es posible verificar la exactitud de la contraseña al decodificar? Digamos que le envío a uno una cadena codificada que conoce la clave, ¿y si ingresa la clave con un error tipográfico? La decodificación todavía le da una cadena "decodificada", pero no es la correcta, ¿cómo puede saberlo?
Heinz

26

Descargo de responsabilidad: como se menciona en los comentarios, esto no debe usarse para proteger datos en una aplicación real.

¿Qué pasa con el cifrado XOR?

/crypto/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


Como se ha mencionado, la biblioteca PyCrypto contiene un conjunto de cifrados. El "cifrado" XOR puede usarse para hacer el trabajo sucio si no quiere hacerlo usted mismo:

from Crypto.Cipher import XOR
import base64

def encrypt(key, plaintext):
  cipher = XOR.new(key)
  return base64.b64encode(cipher.encrypt(plaintext))

def decrypt(key, ciphertext):
  cipher = XOR.new(key)
  return cipher.decrypt(base64.b64decode(ciphertext))

El cifrado funciona de la siguiente manera sin tener que rellenar el texto sin formato:

>>> encrypt('notsosecretkey', 'Attack at dawn!')
'LxsAEgwYRQIGRRAKEhdP'

>>> decrypt('notsosecretkey', encrypt('notsosecretkey', 'Attack at dawn!'))
'Attack at dawn!'

Crédito a https://stackoverflow.com/a/2490376/241294 por las funciones de codificación / decodificación de base64 (soy un novato en Python).


nota: el módulo Crypto se instala en python3 mediante pycrptop instalado, no Crypto. sudo pip3 install pycrypto.
Nikhil VJ

2
nota: pycrypto no se pudo instalar en herokuapp al final. Encontré esta publicación ... parece indicar que el paquete pycrypto ha sido reemplazado por otro llamado pycryptodome insteal, y que el método XOR ha quedado obsoleto: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
Nunca use este método , tenga en cuenta la descripción de este 'cifrado' en la documentación : cifrado de juguete XOR, XOR es uno de los cifrados de flujo más simples. El cifrado y el descifrado se realizan mediante XOR-ing datos con un flujo de claves creado contactando la clave. ¡No lo use para aplicaciones reales! .
Martijn Pieters

@MartijnPieters tienes razón. Espero que mi edición haya dejado claro ese punto.
poida

12

Aquí hay una implementación de cifrado y descifrado seguros de URL utilizando AES (PyCrypto) y base64.

import base64
from Crypto import Random
from Crypto.Cipher import AES

AKEY = b'mysixteenbytekey' # AES key must be either 16, 24, or 32 bytes long

iv = Random.new().read(AES.block_size)

def encode(message):
    obj = AES.new(AKEY, AES.MODE_CFB, iv)
    return base64.urlsafe_b64encode(obj.encrypt(message))

def decode(cipher):
    obj2 = AES.new(AKEY, AES.MODE_CFB, iv)
    return obj2.decrypt(base64.urlsafe_b64decode(cipher))

Si tiene algún problema como este, https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) str(cipher)úselo mientras decodifica de la siguiente manera:

return obj2.decrypt(base64.urlsafe_b64decode(str(cipher)))

Prueba:

In [13]: encode(b"Hello World")
Out[13]: b'67jjg-8_RyaJ-28='

In [14]: %timeit encode("Hello World")
100000 loops, best of 3: 13.9 µs per loop

In [15]: decode(b'67jjg-8_RyaJ-28=')
Out[15]: b'Hello World'

In [16]: %timeit decode(b'67jjg-8_RyaJ-28=')
100000 loops, best of 3: 15.2 µs per loop

Bug con Windows x64 + Python 3.6 + PyCryptodome (como pycrypto está en desuso): TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@Basj aww lo siento ... No uso Windows, así que no puedo arreglarlo.
Todo Vаиітy

No genere el IV y lo almacene en el nivel del módulo, ¡debe incluir el IV en el mensaje de texto cifrado devuelto! Ahora introdujo dos problemas: reiniciar el proceso de Python le brinda un nuevo IV que hace imposible descifrar los mensajes previamente cifrados y, mientras tanto, está reutilizando el IV para varios mensajes, lo que reduce efectivamente la seguridad al nivel ECB.
Martijn Pieters

1
@ AllІѕVаиітy Resuelto con b'...', ¡ edité la respuesta para referencia futura!
Basj

8

Funciones de codificación / decodificación de trabajo en python3 (adaptado muy poco de la respuesta de qneill):

def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = (ord(clear[i]) + ord(key_c)) % 256
        enc.append(enc_c)
    return base64.urlsafe_b64encode(bytes(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + enc[i] - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

8

Gracias por unas excelentes respuestas. Nada original que agregar, pero aquí hay algunas reescrituras progresivas de la respuesta de qneill usando algunas instalaciones útiles de Python. Espero que esté de acuerdo en que simplifican y aclaran el código.

import base64


def qneill_encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def qneill_decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

enumerate()- emparejar los elementos de una lista con su índice

iterar sobre los caracteres en una cadena

def encode_enumerate(key, clear):
    enc = []
    for i, ch in enumerate(clear):
        key_c = key[i % len(key)]
        enc_c = chr((ord(ch) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def decode_enumerate(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i, ch in enumerate(enc):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(ch) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

construir listas usando una comprensión de listas

def encode_comprehension(key, clear):
    enc = [chr((ord(clear_char) + ord(key[i % len(key)])) % 256)
                for i, clear_char in enumerate(clear)]
    return base64.urlsafe_b64encode("".join(enc))


def decode_comprehension(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(ch) - ord(key[i % len(key)])) % 256)
           for i, ch in enumerate(enc)]
    return "".join(dec)

A menudo, en Python no hay ninguna necesidad de índices de lista. Elimine las variables de índice de bucle por completo utilizando zip y ciclo:

from itertools import cycle


def encode_zip_cycle(key, clear):
    enc = [chr((ord(clear_char) + ord(key_char)) % 256)
                for clear_char, key_char in zip(clear, cycle(key))]
    return base64.urlsafe_b64encode("".join(enc))


def decode_zip_cycle(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(enc_char) - ord(key_char)) % 256)
                for enc_char, key_char in zip(enc, cycle(key))]
    return "".join(dec)

y algunas pruebas ...

msg = 'The quick brown fox jumps over the lazy dog.'
key = 'jMG6JV3QdtRh3EhCHWUi'
print('cleartext: {0}'.format(msg))
print('ciphertext: {0}'.format(encode_zip_cycle(key, msg)))

encoders = [qneill_encode, encode_enumerate, encode_comprehension, encode_zip_cycle]
decoders = [qneill_decode, decode_enumerate, decode_comprehension, decode_zip_cycle]

# round-trip check for each pair of implementations
matched_pairs = zip(encoders, decoders)
assert all([decode(key, encode(key, msg)) == msg for encode, decode in matched_pairs])
print('Round-trips for encoder-decoder pairs: all tests passed')

# round-trip applying each kind of decode to each kind of encode to prove equivalent
from itertools import product
all_combinations = product(encoders, decoders)
assert all(decode(key, encode(key, msg)) == msg for encode, decode in all_combinations)
print('Each encoder and decoder can be swapped with any other: all tests passed')

>>> python crypt.py
cleartext: The quick brown fox jumps over the lazy dog.
ciphertext: vrWsVrvLnLTPlLTaorzWY67GzYnUwrSmvXaix8nmctybqoivqdHOic68rmQ=
Round-trips for encoder-decoder pairs: all tests passed
Each encoder and decoder can be swapped with any other: all tests passed

Muy bonito @Nick, buena progresión de pitonismos, y también pruebas de arranque. Para dar el crédito adecuado, solo estaba arreglando un error en la respuesta original de Smehmood stackoverflow.com/a/2490718/468252 .
qneill

4

Si quiere estar seguro, puede usar Fernet, que es criptográficamente sólido. Puede usar una "sal" estática si no desea almacenarla por separado; solo perderá la prevención de ataques de diccionario y arco iris. Lo elegí porque puedo elegir contraseñas largas o cortas, lo que no es tan fácil con AES.

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64

#set password
password = "mysecretpassword"
#set message
message = "secretmessage"

kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt="staticsalt", iterations=100000, backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)

#encrypt
encrypted = f.encrypt(message)
print encrypted

#decrypt
decrypted = f.decrypt(encrypted)
print decrypted

Si eso es demasiado complicado, alguien sugirió simplecrypt

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)

Simplemente genere una sal e inclúyala en el resultado del cifrado para que las contraseñas y los mensajes repetidos sigan generando una salida aleatoria. Incluya también el valor de las iteraciones para preparar el algoritmo para el futuro, pero aún así poder descifrar mensajes utilizando un recuento de iteraciones diferente.
Martijn Pieters

3

Quienquiera que haya venido aquí (y el más generoso) parecía estar buscando frases ingeniosas sin mucha configuración, que otras respuestas no proporcionan. Así que propongo base64.

Ahora, tenga en cuenta que esto es solo una ofuscación básica, y está en ** NINGUNA MANERA ACEPTABLE PARA LA SEGURIDAD ** , pero aquí hay algunas frases ingeniosas:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data, key):
    return urlsafe_b64encode(bytes(key+data, 'utf-8'))

def decode(enc, key):
    return urlsafe_b64decode(enc)[len(key):].decode('utf-8')

print(encode('hi', 'there')) # b'dGhlcmVoaQ=='
print(decode(encode('hi', 'there'), 'there')) # 'hi'

Algunas cosas a tener en cuenta:

  • querrá lidiar con más / menos codificación / decodificación de byte a cadena por su cuenta, dependiendo de su E / S. Mirar bytes()ybytes::decode()
  • base64 es fácilmente reconocible por los tipos de caracteres utilizados y, a menudo, termina con =caracteres. La gente como yo va por ahí decodificándolos en la consola javascript cuando los vemos en sitios web. Es tan fácil como btoa(string)(js)
  • el orden es clave + datos, como en b64, los caracteres que aparecen al final dependen de los caracteres que están al principio (debido a las compensaciones de bytes. Wikipedia tiene algunas explicaciones interesantes). En este escenario, el comienzo de la cadena codificada será el mismo para todo lo codificado con esa clave. La ventaja es que los datos estarán más confusos. Si lo hace al revés, la parte de datos será exactamente la misma para todos, independientemente de la clave.

Ahora, si lo que quería ni siquiera necesitaba una clave de ningún tipo, sino solo un poco de ofuscación, puede volver a usar base64, sin ningún tipo de clave:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data):
    return urlsafe_b64encode(bytes(data, 'utf-8'))

def decode(enc):
    return urlsafe_b64decode(enc).decode()

print(encode('hi')) # b'aGk='
print(decode(encode('hi'))) # 'hi'

2
Sí, si no le preocupa la seguridad, entonces base64 es mucho mejor que cifrar.
Martijn Pieters

Acerca de que las otras respuestas no son de una sola línea: ese no es el punto de la pregunta. Piden dos funciones para llamar. Y Fernet(key).encrypt(message)es solo una expresión como su llamada base64.
Martijn Pieters

Y usted debe quitar el keyconjunto. Las cargas de los desarrolladores van a copiar y pegar de desbordamiento de pila sin prestar atención y se asumen la clave para ser secreto. Si debe incluirlo, al menos no lo use y advierta o genere una excepción si se usa de todos modos. No subestime la estupidez de la cultura de copiar y pegar y sus responsabilidades para realizar funciones cuerdas.
Martijn Pieters

3

Daré 4 soluciones:

1) Uso de cifrado Fernet con cryptographybiblioteca

Aquí hay una solución usando el paquete cryptography, que puede instalar como de costumbre con pip install cryptography:

import base64
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

def cipherFernet(password):
    key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password)
    return Fernet(base64.urlsafe_b64encode(key))

def encrypt1(plaintext, password):
    return cipherFernet(password).encrypt(plaintext)

def decrypt1(ciphertext, password):
    return cipherFernet(password).decrypt(ciphertext)

# Example:

print(encrypt1(b'John Doe', b'mypass'))  
# b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg=='
print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'mypass')) 
# b'John Doe'
try:  # test with a wrong password
    print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'wrongpass')) 
except InvalidToken:
    print('Wrong password')

Puede adaptarse con su propia sal, conteo de iteraciones, etc. Este código no está muy lejos de la respuesta de @ HCLivess, pero el objetivo aquí es tener funciones encrypty listas para usar decrypt. Fuente: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Nota: use .encode()y en .decode()todas partes si desea cadenas en 'John Doe'lugar de bytes como b'John Doe'.


2) Cifrado AES simple con Cryptobiblioteca

Esto funciona con Python 3:

import base64
from Crypto import Random
from Crypto.Hash import SHA256
from Crypto.Cipher import AES

def cipherAES(password, iv):
    key = SHA256.new(password).digest()
    return AES.new(key, AES.MODE_CFB, iv)

def encrypt2(plaintext, password):
    iv = Random.new().read(AES.block_size)
    return base64.b64encode(iv + cipherAES(password, iv).encrypt(plaintext))

def decrypt2(ciphertext, password):
    d = base64.b64decode(ciphertext)
    iv, ciphertext = d[:AES.block_size], d[AES.block_size:]
    return cipherAES(password, iv).decrypt(ciphertext)

# Example:    

print(encrypt2(b'John Doe', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'wrongpass'))  # wrong password: no error, but garbled output

Nota: puede eliminar base64.b64encodey .b64decodesi no desea una salida legible en texto y / o si desea guardar el texto cifrado en el disco como un archivo binario de todos modos.


3) AES con una mejor función de derivación de clave de contraseña y la capacidad de probar si "se ingresó una contraseña incorrecta", con Cryptobiblioteca

La solución 2) con el "modo CFB" de AES está bien, pero tiene dos inconvenientes: el hecho de que SHA256(password)se puede forzar fácilmente con una tabla de búsqueda y que no hay forma de probar si se ha ingresado una contraseña incorrecta. Esto se resuelve aquí mediante el uso de AES en "modo GCM", como se explica en AES: ¿cómo detectar que se ha introducido una contraseña incorrecta?y ¿Es seguro este método para decir "La contraseña que ingresó es incorrecta"? :

import Crypto.Random, Crypto.Protocol.KDF, Crypto.Cipher.AES

def cipherAES_GCM(pwd, nonce):
    key = Crypto.Protocol.KDF.PBKDF2(pwd, nonce, count=100000)
    return Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_GCM, nonce=nonce, mac_len=16)

def encrypt3(plaintext, password):
    nonce = Crypto.Random.new().read(16)
    return nonce + b''.join(cipherAES_GCM(password, nonce).encrypt_and_digest(plaintext))  # you case base64.b64encode it if needed

def decrypt3(ciphertext, password):
    nonce, ciphertext, tag = ciphertext[:16], ciphertext[16:len(ciphertext)-16], ciphertext[-16:]
    return cipherAES_GCM(password, nonce).decrypt_and_verify(ciphertext, tag)

# Example:

print(encrypt3(b'John Doe', b'mypass'))
print(decrypt3(b'\xbaN_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\xa50', b'mypass'))
try:
    print(decrypt3(b'\xbaN_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\xa50', b'wrongpass'))
except ValueError:
    print("Wrong password")

4) Usando RC4 (no se necesita biblioteca)

Adaptado de https://github.com/bozhu/RC4-Python/blob/master/rc4.py .

def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        yield S[(S[i] + S[j]) % 256]

def encryptRC4(plaintext, key, hexformat=False):
    key, plaintext = bytearray(key), bytearray(plaintext)  # necessary for py2, not for py3
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    keystream = PRGA(S)
    return b''.join(b"%02X" % (c ^ next(keystream)) for c in plaintext) if hexformat else bytearray(c ^ next(keystream) for c in plaintext)

print(encryptRC4(b'John Doe', b'mypass'))                           # b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a'
print(encryptRC4(b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a', b'mypass'))   # b'John Doe'

(Desactualizado desde las últimas ediciones, pero guardado para referencia futura): Tuve problemas al usar Windows + Python 3.6 + todas las respuestas que involucran pycrypto(no puedo pip install pycryptoen Windows) o pycryptodome(las respuestas aquí from Crypto.Cipher import XORfallaron porque XORno es compatible con esta pycryptobifurcación; y las soluciones que usan ... AESfallaron también con TypeError: Object type <class 'str'> cannot be passed to C code). Además, la biblioteca simple-crypttiene una pycryptodependencia, por lo que no es una opción.


1
No desea codificar de forma rígida los recuentos de iteraciones y sal; generar una sal aleatoria y hacer que el recuento de iteraciones sea configurable en el cifrado, incluir esa información en el resultado del cifrado y extraer y usar los valores en el descifrado. La sal lo protege del reconocimiento trivial de contraseñas reutilizadas en un mensaje dado, el recuento de iteraciones prepara el algoritmo para el futuro.
Martijn Pieters

Sí, por supuesto @MartijnPieters, pero el objetivo aquí es tener un código simple para propósitos simples, como lo solicita OP, con dos parámetros : texto sin formato + contraseña. Por supuesto, para escenarios más complejos (es decir, una base de datos), utilizará todos estos parámetros adicionales.
Basj

¡No se necesitan parámetros adicionales! Incluyo esa información codificada en el valor de retorno opaco de password_encrypt().
Martijn Pieters

@MartijnPieters Buena solución de hecho.
Basj

2

Esto funciona, pero la longitud de la contraseña debe ser exacta 8. Esto es simple y requiere pyDes .

from pyDes import *

def encode(data,password):
    k = des(password, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
    d = k.encrypt(data)
    return d

def decode(data,password):
    k = des(password, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
    d = k.decrypt(data)
    return d

x = encode('John Doe', 'mypass12')
y = decode(x,'mypass12')

print x
print y

SALIDA:

³.\Þ\åS¾+æÅ`;Ê
John Doe

¡No use una vía intravenosa fija! Aleatorice el IV e inclúyalo con el texto cifrado. De lo contrario, también puede utilizar el modo ECB; Los mensajes de texto sin formato repetidos son, por lo demás, triviales de reconocer.
Martijn Pieters

Además, el proyecto pyDes parece estar muerto; la página de inicio desapareció y la última versión de PyPI tiene ahora 9 años.
Martijn Pieters

2

Otra implementación del código @qneill que incluye la suma de comprobación CRC del mensaje original, lanza una excepción si la comprobación falla:

import hashlib
import struct
import zlib

def vigenere_encode(text, key):
    text = '{}{}'.format(text, struct.pack('i', zlib.crc32(text)))

    enc = []
    for i in range(len(text)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(text[i]) + ord(key_c)) % 256)
        enc.append(enc_c)

    return base64.urlsafe_b64encode("".join(enc))


def vigenere_decode(encoded_text, key):
    dec = []
    encoded_text = base64.urlsafe_b64decode(encoded_text)
    for i in range(len(encoded_text)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(encoded_text[i]) - ord(key_c)) % 256)
        dec.append(dec_c)

    dec = "".join(dec)
    checksum = dec[-4:]
    dec = dec[:-4]

    assert zlib.crc32(dec) == struct.unpack('i', checksum)[0], 'Decode Checksum Error'

    return dec

2

Puede utilizar AES para cifrar su cadena con una contraseña. Sin embargo, querrá elegir una contraseña lo suficientemente fuerte para que la gente no pueda adivinar fácilmente cuál es (lo siento, no puedo evitarlo. Soy un aspirante a pito de seguridad).

AES es fuerte con un buen tamaño de clave, pero también es fácil de usar con PyCrypto.


3
Gracias Alan. Pero para aclarar, no estoy encriptando las contraseñas en sí. En el ejemplo anterior, estoy encriptando la cadena "John Doe" de acuerdo con la contraseña "mypass", que es una contraseña simple que uso en mi código fuente. Las contraseñas de usuario no están involucradas, ni ninguna otra información muy sensible. Edité mi pregunta para aclarar esto.
RexE

1
AES es excelente, si se usa correctamente. Sin embargo, es fácil usarlo incorrectamente; hay al menos una respuesta aquí que usa un modo de cifrado de bloque inseguro, otras dos que manipulan el valor IV. ¡Es mejor usar una buena biblioteca con una receta bien definida como Fernet!
Martijn Pieters

De hecho, es una observación muy astuta. Una vez me puse el intravenoso.
Alan

0

Las bibliotecas externas proporcionan algoritmos de cifrado de clave secreta.

Por ejemplo, el Cyphermódulo de PyCrypto ofrece una selección de muchos algoritmos de cifrado:

  • Crypto.Cipher.AES
  • Crypto.Cipher.ARC2
  • Crypto.Cipher.ARC4
  • Crypto.Cipher.Blowfish
  • Crypto.Cipher.CAST
  • Crypto.Cipher.DES
  • Crypto.Cipher.DES3
  • Crypto.Cipher.IDEA
  • Crypto.Cipher.RC5
  • Crypto.Cipher.XOR

MeTooCrypto es un Pythoncontenedor para OpenSSL y proporciona (entre otras funciones) una biblioteca de criptografía de uso general con toda su potencia. Se incluyen cifrados simétricos (como AES).


0

si desea un cifrado seguro:

para python 2, debe usar keyczar http://www.keyczar.org/

para python 3, hasta que keyczar esté disponible, he escrito simple-crypt http://pypi.python.org/pypi/simple-crypt

ambos utilizarán el fortalecimiento de claves que los hace más seguros que la mayoría de las otras respuestas aquí. y dado que son tan fáciles de usar, es posible que desee usarlos incluso cuando la seguridad no sea crítica ...


Desde el repositorio de Keyczar : Nota importante: Keyczar está obsoleto. Los desarrolladores de Keyczar recomiendan Tink , pero no existe una versión Python de Tink.
Martijn Pieters

0

Por lo tanto, como no se codifica nada crítico para la misión y solo desea cifrar para la ocultación .

Déjame presentarte el cifrado de Caeser

ingrese la descripción de la imagen aquí

El cifrado de César o cambio de César es una de las técnicas de cifrado más sencillas y conocidas. Es un tipo de cifrado de sustitución en el que cada letra del texto sin formato se reemplaza por una letra en un número fijo de posiciones en el alfabeto. Por ejemplo, con un desplazamiento a la izquierda de 3, D sería reemplazada por A, E se convertiría en B, y así sucesivamente.

Código de muestra para su referencia:

def encrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) + s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) + s - 97) % 26 + 97) 

        return result 

    def decrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) - s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) - s - 97) % 26 + 97) 

        return result 

    #check the above function 
    text = "ATTACKATONCE"
    s = 4
    print("Text  : " + text) 
    print("Shift : " + str(s)) 
    print("Cipher: " + encrypt(text,s))
    print("Original text: " + decrypt(encrypt(text,s),s))

Ventajas: cumple con sus requisitos y es simple y realiza la codificación.

Desventaja: se puede descifrar mediante simples algoritmos de fuerza bruta (es muy poco probable que alguien intente pasar por todos los resultados adicionales).


0

Agregar un código más con decodificar y codificar como referencia

import base64

def encode(key, string):
    encoded_chars = []
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    arr2 = bytes(encoded_string, 'utf-8')
    return base64.urlsafe_b64encode(arr2)

def decode(key, string):
    encoded_chars = []
    string = base64.urlsafe_b64decode(string)
    string = string.decode('utf-8')
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) - ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return encoded_string

def main():
    answer = str(input("EorD"))
    if(answer in ['E']):
        #ENCODE
        file = open("D:\enc.txt")
        line = file.read().replace("\n", " NEWLINEHERE ")
        file.close()
        text = encode("4114458",line)
        fnew = open("D:\\new.txt","w+")
        fnew.write(text.decode('utf-8'))
        fnew.close()
    else:
        #DECODE
        file = open("D:\\new.txt",'r+')
        eline = file.read().replace("NEWLINEHERE","\n")
        file.close()
        print(eline)
        eline = eline.encode('utf-8')
        dtext=decode("4114458",eline)
        print(dtext)
        fnew = open("D:\\newde.txt","w+")
        fnew.write(dtext)
        fnew.close

if __name__ == '__main__':
    main()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.