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

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

Algo como esto:

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

Así que la cadena “John Doe” se encripta 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 sea la forma en que puedo cifrar / descifrar un documento de Word con una contraseña.

Me gustaría usar estas cadenas cifradas como parámetros de URL. Mi objective es la ofuscación, no la seguridad fuerte; nada de misión crítica está siendo codificado. 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.

Suponiendo que solo está buscando una ofuscación simple que oculte las cosas del observador muy casual, y no está buscando utilizar bibliotecas de terceros. Yo recomendaría algo como el cifrado Vigenere. Es uno de los cifrados antiguos más fuertes.

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

La deencoding es más o menos igual, excepto que restas 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 un múltiplo de 16 caracteres de longitud. Por lo tanto, debe rellenar. Además, si desea usarlos como parámetros de URL, use 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 su nombre lo sugiere).

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 he vinculado proporciona instrucciones detalladas para romper el cifrado, de modo que cualquier persona con una cantidad moderada de determinación podría fácilmente romperlo.

Como declara explícitamente que desea seguridad y no seguridad, evitaremos reprenderlo por la debilidad de lo que sugiere 🙂

Entonces, usando PyCrypto:

 from Crypto.Cipher import AES import base64 msg_text = 'test some plain text here'.rjust(32) secret_key = '1234567890123456' # create new & store somewhere safe cipher = AES.new(secret_key,AES.MODE_ECB) # never use ECB in strong systems obviously encoded = base64.b64encode(cipher.encrypt(msg_text)) # ... decoded = cipher.decrypt(base64.b64decode(encoded)) print decoded.strip() 

Si alguien obtiene una retención de su base de datos y su base de código, podrá decodificar los datos cifrados. ¡Mantenga su secret_key a salvo!

El “encoded_c” mencionado en la respuesta del cifrado Vigenere de @ smehmood debe ser “key_c”.

Aquí están trabajando las funciones de encoding / deencoding.

 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) 

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

 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 encoding / deencoding adicional es necesaria porque Python 3 tiene arreglos de cadenas / bytes divididos en dos conceptos diferentes, y actualizó sus API para reflejar eso.

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?

https://crypto.stackexchange.com/questions/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


Como se ha mencionado, la biblioteca de PyCrypto contiene un conjunto de cifrados. El “cifrado” XOR se puede utilizar para hacer el trabajo sucio si no desea 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 simple:

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

Gracias a https://stackoverflow.com/a/2490376/241294 por las funciones de encoding / deencoding base64 (soy un novato de Python).

Python no tiene esquemas de cifrado incorporados, no. También debe tomar en serio el almacenamiento de datos cifrados; esquemas de encriptación triviales que un desarrollador entiende como inseguros y un esquema de juguete bien puede ser confundido con un esquema seguro por un desarrollador menos experimentado. Si encriptas, encripta adecuadamente.

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 de confianza para manejar esto por usted. Para Python 3, esa biblioteca confiable es la cryptography .

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

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

Pero para que una aplicación web o un proceso se ejecute en un clúster sin atención humana para seguir ejecutándolo, usted desea usar una clave. Las contraseñas son para cuando solo un usuario final necesita acceder a la información específica. Incluso entonces, usualmente protege la aplicación con una contraseña, luego intercambia información encriptada usando una clave, tal vez una adjunta a la cuenta del usuario.

Cifrado de clave simétrica

Fernet – AES CBC + HMAC, muy recomendado

La biblioteca de cryptography incluye la receta de Fernet , una receta de mejores prácticas para el uso de la criptografía. Fernet es un estándar abierto , con implementaciones listas en una amplia gama de lenguajes de progtwigción y paquetes de 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 uses Fernet.generate_key() para generar una clave segura. También puede usar una contraseña (sección siguiente), 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 objeto de bytes con URL y caracteres base64 seguros para archivos, tan imprimible:

 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 instancia de Fernet() con la clave dada, y llame a Fernet.encrypt() o Fernet.decrypt() , tanto el mensaje de texto plano para cifrar como el token cifrado son objetos de bytes .

encrypt() funciones encrypt() y decrypt() 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 algo la seguridad

Puede usar una contraseña en lugar de una clave secreta, siempre que use un método de derivación de clave segura . Luego tiene que incluir el recuento de iteración HMAC y sal en el mensaje, por lo que el valor cifrado ya no es compatible con Fernet sin separar primero el token, el recuento y el 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' 

Al incluir la sal en la salida, es posible utilizar un valor de sal aleatorio, que a su vez garantiza que la salida cifrada se garantiza que es totalmente aleatoria, independientemente de la reutilización de la contraseña o la repetición del mensaje. Al incluir el recuento de iteraciones, se puede ajustar el rendimiento de la CPU a lo largo del tiempo sin perder la capacidad de descifrar mensajes antiguos.

Una contraseña sola puede ser tan segura como una clave aleatoria de Fernet de 32 bytes, siempre que genere una contraseña aleatoria adecuada de un grupo de tamaño similar. 32 bytes le da 256 ^ 32 número de claves, por lo que si usa un alfabeto de 74 caracteres (26 en la parte superior, 26 en la parte inferior, 10 dígitos y 12 símbolos posibles), su contraseña debe ser al menos math.ceil(math.log(256 ** 32, 74)) == 42 caracteres de largo. Sin embargo, un número mayor y bien seleccionado de iteraciones HMAC puede mitigar la falta de entropía, ya que esto hace que sea mucho más costoso para un atacante forzar su entrada.

Simplemente sepa que elegir una contraseña más corta pero aún razonablemente segura no paralizará este esquema, solo reduce la cantidad 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 solo un cifrado de baja seguridad o una implementación casera de, diga Vignere. No hay seguridad en estos enfoques, pero puede dar a un desarrollador sin experiencia a quien se le asigna la tarea de mantener su código en el futuro la ilusión de seguridad, que es peor que nada de seguridad.

Si todo lo que necesita es oscuridad, solo baste 64 los datos; para los requisitos de URL segura, la función base64.urlsafe_b64encode() está bien. No use una contraseña aquí, simplemente codifique y listo. A lo sumo, agrega 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 vuelve b'Hello world!' en b'eNrzSM3JyVcozy_KSVEEAB0JBF4=' .

Solo integridad

Si todo lo que necesita es una forma de asegurarse de que se puede confiar en que los datos no se alterarán después de haber sido enviados a un cliente que no es de confianza y recibidos nuevamente, entonces desea firmar los datos, puede usar la biblioteca hmac para esto con SHA1 ( todavía 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íela al cliente. Cuando reciba los datos nuevamente, divida los datos y la firma y verifique. He establecido 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 biblioteca itsdangerous , que itsdangerous todo esto con la serialización y la itsdangerous en varios formatos.

Uso del 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 los datos sin sentido de su sistema para mantener su servicio ocupado en círculos con información incorrecta, porque el texto cifrado está firmado.

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

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

 key = secrets.token_bytes(32) 

entonces 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 All Vѕ Vаиітy sigue, aunque sea incorrectamente. Esta es la versión de cryptography , pero tenga en cuenta que incluyo la IV en el texto cifrado , no debe almacenarse como un global (reutilizar una IV debilita la seguridad de la clave, y almacenarla como un módulo global significa que se volverá a utilizar). generó la siguiente invocación de Python, haciendo que todo el texto cifrado no se pueda descifrar):

 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 de la armadura adicional de una firma HMAC y no hay una marca de tiempo; tendrías que agregarlos tú mismo.

Lo anterior también ilustra lo fácil que es combinar incorrectamente bloques de construcción criptográficos básicos; El manejo incorrecto de todos los valores de IV puede llevar a una violación de datos o a que todos los mensajes cifrados sean ilegibles porque la IV se ha perdido. El uso de Fernet en cambio te protege de tales errores.

AES ECB - no seguro

Si anteriormente implementó el cifrado AES ECB y necesita seguir admitiendo esto en Python 3, también puede hacerlo con cryptography . Las mismas advertencias se aplican, el BCE no es lo suficientemente seguro para aplicaciones de la vida real . Reimplementando esa respuesta para Python 3, agregando el manejo automático del 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 el BCE de todos modos. Lo anterior es simplemente para ilustrar que la cryptography puede manejar los bloques de construcción criptográficos comunes, incluso los que realmente no debería usar.

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

 import base64 from Crypto import Random from Crypto.Cipher import AES AKEY = '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 se enfrenta a un problema como este https://bugs.python.org/issue4329 (TypeError: la asignación de caracteres debe devolver entero, Ninguno o Unicode) use str (cifrado) mientras se decodifica de la siguiente manera

devuelve obj2.decrypt (base64.urlsafe_b64decode (str (cifrado)))

 In [13]: encode("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 

Trabajar funciones de encoding / deencoding 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) 

Gracias por algunas grandes respuestas. No hay nada original que agregar, pero aquí hay algunas reescrituras progresivas de la respuesta de Qneill utilizando algunas herramientas útiles de Python. Espero que estén 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() – empareja 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 utilizando una lista de comprensión

 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 se necesitan índices de lista. Elimine las variables de índice de bucle completamente usando 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 

Si quiere estar seguro, puede usar Fernet, que es criptográficamente sólido. Puede usar una “sal” estática si no quiere almacenarla por separado; solo perderá el diccionario y la prevención de ataques de 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 

If that’s too complicated, someone suggested simplecrypt

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

Note: I had problems using Windows + Python 3.6 + all the answers involving pycrypto (not able to pip install pycrypto on Windows) or pycryptodome (the answers here with from Crypto.Cipher import XOR failed because XOR is not supported by this pycrypto fork ; and the solutions using ... AES failed too with TypeError: Object type cannot be passed to C code ). Also, the library simple-crypt has pycrypto as dependency, so it’s not an option.


Here is a solution using the package cryptography , that you can install as usual with pip install cryptography :

 import base64 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 def encrypt(plaintext, password): f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode()))) return f.encrypt(plaintext.encode()).decode() def decrypt(ciphertext, password): f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode()))) return f.decrypt(ciphertext.encode()).decode() 

Uso:

 >>> encrypt('John Doe', password='mypass') 'gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g==' >>> decrypt('gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g==', password='mypass') 'John Doe' 

Nota:

This works but password length should be exactly 8 . This is simple and requires 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 

An other implementation of @qneill code which include CRC checksum of the original message, it throw an exception if the check fail:

 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 

You can use AES to encrypt your string with a password. Though, you’ll want to chose a strong enough password so people can’t easily guess what it is (sorry I can’t help it. I’m a wannabe security weenie).

AES is strong with a good key size, but it’s also easy to use with PyCrypto.

External libraries provide secret-key encryption algorithms.

For example, the Cypher module in PyCrypto offers a selection of many encryption algorithms:

  • 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 is a Python wrapper for OpenSSL , and provides (among other functions) a full-strength general purpose cryptography library. Included are symmetric ciphers (like AES).

Whoever came here (and the bountier) seemed to be looking for one-liners with not much setup, which other answers don’t provide. So I’m putting forward base64.

Now, keep in mind that this is basic obfuscation only, and is in **NO WAY OK FOR SECURITY** , but here are some one-liners:

 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' 

A few things to note:

  • you will want to deal with more/less byte-to-string encoding/decoding on your own, depending on your I/O. Look into bytes() and bytes::decode()
  • base64 is easily recognizable by the types of characters used, and often ending with = characters. People like me absolutely go around decoding them in the javascript console when we see them on websites. It’s as easy as btoa(string) (js)
  • the order is key+data, as in b64, what characters appear at the end depends on what characters are at the beginning (because of byte offsets. Wikipedia has some nice explanations). In this scenario, the beginning of the encoded string will be the same for everything encoded with that key. The plus is that the data will be more obfuscated. Doing it the other way around will result on the data part being exactly the same for everyone, regardless of key.

Now, if what you wanted didn’t even need a key of any kind, but just some obfuscation, you can yet again just use base64, without any kinds of key:

 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' 

if you want secure encryption:

for python 2, you should use keyczar http://www.keyczar.org/

for python 3, until keyczar is available, i have written simple-crypt http://pypi.python.org/pypi/simple-crypt

both these will use key strengthening which makes them more secure than most other answers here. and since they’re so easy to use you might want to use them even when security is not critical…

So, as nothing mission critical is being encoded , and you just want to encrypt for obsfuscation .

Let me present caeser’s cipher

introduzca la descripción de la imagen aquí

Caesar’s cipher or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on.

Sample code for your reference :

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

Advantages : it meets your requirements and is simple and does the encoding thing’y’.

Disadvantage : can be cracked by simple brute force algorithms (highly unlikely anyone would attempt to go through all extra results).