web-dev-qa-db-fra.com

Un moyen simple d’encoder une chaîne en fonction d’un mot de passe?

Est-ce que Python a un moyen simple et intégré d’encoder/décoder des chaînes en utilisant un mot de passe?

Quelque chose comme ça:

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

Ainsi, la chaîne "John Doe" est cryptée en tant que "sjkl28cn2sx0". Pour obtenir la chaîne d'origine, je "déverrouille" cette chaîne avec la clé "mypass", qui est un mot de passe dans mon code source. J'aimerais que ce soit comme ça que je puisse chiffrer/déchiffrer un document Word avec un mot de passe.

Je voudrais utiliser ces chaînes cryptées comme paramètres d'URL. Mon but est l’obscurcissement, pas une sécurité forte; rien de critique n'a été encodé. Je réalise que je pourrais utiliser une table de base de données pour stocker des clés et des valeurs, mais j'essaie d'être minimaliste.

108
RexE

En supposant que vous seulement recherchiez un simple obscurcissement qui obscurcirait les choses du très observateur occasionnel, et vous ne cherchez pas à utiliser des bibliothèques tierces. Je recommanderais quelque chose comme le chiffrement de Vigenere. C'est l'un des plus puissants des vieux chiffrements anciens.

Chiffre de Vigenère

C'est rapide et facile à mettre en œuvre. Quelque chose comme:

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)

Decode est à peu près la même chose, sauf que vous soustrayez la clé.

Il est beaucoup plus difficile de casser si les chaînes que vous encodez sont courtes et/ou s'il est difficile de deviner la longueur de la phrase secrète utilisée.

Si vous recherchez quelque chose de cryptographique, PyCrypto est probablement votre meilleur choix, bien que les réponses précédentes négligent certains détails: le mode ECB dans PyCrypto exige que votre message soit un multiple de 16 caractères. Donc, vous devez pad. De plus, si vous souhaitez les utiliser comme paramètres d'URL, utilisez base64.urlsafe_b64_encode(), plutôt que le paramètre standard. Ceci remplace quelques caractères de l'alphabet base64 par des caractères sûrs pour les URL (comme son nom l'indique).

Cependant, vous devez être ABSOLUMENT certain que cette très couche mince d’obscurcissement suffit à vos besoins avant de l’utiliser. L'article de Wikipédia auquel je me suis référencé fournit des instructions détaillées pour rompre le chiffrement, de sorte que toute personne ayant une détermination moyenne peut facilement le rompre.

64
smehmood

Lorsque vous déclarez explicitement que vous souhaitez l'obscurité et non la sécurité, nous éviterons de vous réprimander pour la faiblesse de ce que vous suggérez :)

Donc, en utilisant 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 quelqu'un récupère votre base de données et votre base de code, ils seront en mesure de décoder les données cryptées. Gardez votre secret_key en sécurité!

65
Will

Le "encoded_c" mentionné dans la réponse du chiffre de Vigenere de @ smehmood devrait être "key_c".

Ici fonctionnent les fonctions d'encodage/décodage.

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

Voici une version Python 3 des fonctions de @qneill's réponse :

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)

Les encodages/décodages supplémentaires sont nécessaires car Python 3 divise les tableaux de chaînes/octets en deux concepts différents et met à jour leurs API pour refléter cela.

46
Ryan Barrett

Python n'a pas de schémas de chiffrement intégrés, non. Vous devez également prendre au sérieux le stockage de données cryptées. Les schémas de cryptage triviaux qu'un développeur comprend sans certitude et un schéma de jouet peut très bien être confondu avec un schéma sécurisé par un développeur moins expérimenté. Si vous chiffrez, chiffrez correctement.

Cependant, vous n'avez pas besoin de faire beaucoup de travail pour mettre en place un schéma de chiffrement approprié. Tout d’abord, ne réinventez pas la roue de cryptographie, utilisez une bibliothèque de cryptographie de confiance pour gérer cela à votre place. Pour Python 3, cette bibliothèque de confiance est cryptography .

Je recommande également que le chiffrement et le déchiffrement s’appliquent à octets; encoder les messages texte en octets en premier; stringvalue.encode() code en UTF8, facilement rétabli en utilisant bytesvalue.decode().

Enfin et surtout, lors du cryptage et du décryptage, nous parlons de clés, pas de mots de passe. Une clé ne doit pas être mémorable, c'est quelque chose que vous stockez dans un emplacement secret, mais lisible par une machine, alors qu'un mot de passe peut souvent être lu et mémorisé. Vous pouvez dérivez une clé à partir d'un mot de passe, avec un peu de soin.

Mais pour une application Web ou un processus s'exécutant dans un cluster sans intervention humaine, vous souhaitez utiliser une clé. Les mots de passe sont utilisés lorsque seul l'utilisateur final a besoin d'accéder à des informations spécifiques. Même dans ce cas, vous sécurisez généralement l'application avec un mot de passe, puis vous échangez des informations chiffrées à l'aide d'une clé, éventuellement associée au compte de l'utilisateur.

Cryptage par clé symétrique

Fernet - AES CBC + HMAC, fortement recommandé

La bibliothèque cryptography comprend la recette de Fernet , une recette de meilleure pratique pour l'utilisation de la cryptographie. Fernet est n standard ouvert , pouvant être implémenté dans une grande variété de langages de programmation. Il encapsule pour vous le cryptage AES CBC avec des informations sur la version, un horodatage et une signature HMAC pour empêcher la falsification des messages.

Fernet facilite le cryptage et le décryptage des messages et vous protègent. C'est la méthode idéale pour chiffrer des données avec un secret.

Je vous recommande d'utiliser Fernet.generate_key() pour générer une clé sécurisée. Vous pouvez aussi utiliser un mot de passe (section suivante), mais une clé secrète complète de 32 octets (16 octets à chiffrer, plus 16 pour la signature) sera plus sécurisée que la plupart des mots de passe auxquels vous pouvez penser.

La clé générée par Fernet est un objet bytes avec des caractères base64 sécurisés pour les fichiers et les URL, donc imprimables:

from cryptography.fernet import Fernet

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

Pour chiffrer ou déchiffrer des messages, créez une instance Fernet() avec la clé donnée et appelez le Fernet.encrypt() ou Fernet.decrypt() , le message en texte brut à chiffrer et le jeton chiffré sont des objets bytes.

Les fonctions encrypt() et decrypt() ressemblent à ceci:

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)

Démo:

>>> 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 avec mot de passe - clé dérivée du mot de passe, affaiblit quelque peu la sécurité

Vous pouvez utiliser un mot de passe au lieu d'une clé secrète, à condition que vous tilisez une méthode de dérivation de clé forte . Vous devez ensuite inclure le sel et le nombre d'itérations HMAC dans le message, de sorte que la valeur chiffrée n'est plus compatible avec Fernet sans avoir au préalable séparé le sel, le compte et le jeton 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)

Démo:

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

L'inclusion du sel dans la sortie permet d'utiliser une valeur de sel aléatoire, ce qui garantit à son tour que la sortie cryptée est entièrement aléatoire, indépendamment de la réutilisation du mot de passe ou de la répétition du message. L'inclusion du nombre d'itérations vous permet de vous adapter aux augmentations de performances du processeur sans perdre la possibilité de déchiffrer les messages plus anciens.

Un mot de passe seul peut être aussi sûr qu'une clé aléatoire de 32 octets de Fernet, à condition que vous génériez un mot de passe correctement aléatoire à partir d'un pool de taille similaire. 32 octets vous donne 256 ^ 32 nombre de clés. Si vous utilisez un alphabet de 74 caractères (26 majuscules, 26 minuscules, 10 chiffres et 12 symboles possibles), votre mot de passe doit être au moins math.ceil(math.log(256 ** 32, 74)) == 42 caractères de long. Cependant, un plus grand nombre d'itérations HMAC bien sélectionné peut atténuer le manque d'entropie, car il est beaucoup plus coûteux pour un attaquant d'entrer brutalement par la force.

Sachez simplement que le choix d’un mot de passe plus court, mais toujours raisonnablement sécurisé, ne réduira pas ce système, cela réduira simplement le nombre de valeurs possibles qu’un attaquant en force brute devrait rechercher; Assurez-vous de choisir un mot de passe assez fort pour vos besoins de sécurité .

Des alternatives

Obscurcissant

Une alternative est ne pas chiffrer. Ne soyez pas tenté d'utiliser simplement un chiffrement à faible sécurité, ou une implémentation maison de, dit Vignere. Ces approches n’offrent aucune sécurité, mais peuvent donner à un développeur inexpérimenté la tâche de maintenir votre code à l’avenir, l’illusion de sécurité, ce qui est pire que pas de sécurité du tout.

Si tout ce dont vous avez besoin est obscurité, basez simplement les données sur base64; Pour les besoins sécurisés par les URL, la fonction base64.urlsafe_b64encode() est correcte. N'utilisez pas de mot de passe ici, encodez et vous avez terminé. Au plus, ajoutez un peu de compression (comme 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))

Ceci transforme b'Hello world!' En b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Intégrité seulement

Si tout ce dont vous avez besoin est un moyen de vous assurer que les données peuvent être approuvées de manière à être inchangées après avoir été envoyées à un client non approuvé et reçues, vous voulez signer les données, vous pouvez utilisez la bibliothèque hmac avec SHA1 (still considérée comme sécurisée pour la signature HMAC ) ou mieux:

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)

Utilisez-le pour signer des données, puis attachez la signature aux données et envoyez-la au client. Lorsque vous recevez les données, séparez les données et la signature et vérifiez. J'ai défini l'algorithme par défaut sur SHA256, vous aurez donc besoin d'une clé de 32 octets:

key = secrets.token_bytes(32)

Vous voudrez peut-être regarder itsdangerous library , qui compense tout cela avec la sérialisation et la désérialisation dans divers formats.

Utilisation du cryptage AES-GCM pour assurer le cryptage et l'intégrité

Fernet s'appuie sur AEC-CBC avec une signature HMAC pour assurer l'intégrité des données cryptées; un attaquant malveillant ne peut pas alimenter votre système en données non-sens pour garder votre service occupé à tourner en rond avec une mauvaise entrée, car le texte chiffré est signé.

Le chiffrement de bloc en mode Galois/Compteur produit le texte chiffré et un tag pour servir le même but, donc peut être utilisé pour le même but. L'inconvénient est que, contrairement à Fernet, il n'existe pas de recette unique utilisable à la taille qui puisse être réutilisée sur d'autres plates-formes. De plus, AES-GCM n'utilise pas de remplissage, ce cryptage texte correspond donc à la longueur du message d'entrée (alors que Fernet/AES-CBC crypte les messages en blocs de longueur fixe, masquant quelque peu la longueur du message).

AES256-GCM prend comme clé le secret habituel de 32 octets:

key = secrets.token_bytes(32)

puis utiliser

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

J'ai inclus un horodatage prenant en charge les mêmes cas d'utilisation de la durée de vie que ceux pris en charge par Fernet.

Autres approches sur cette page, en Python 3

AES CFB - comme CBC mais sans avoir besoin de pad

C’est l’approche que All Are Vаиітy suit, bien qu’elle soit incorrecte. Ceci est la version cryptography, mais notez que je inclure le IV dans le texte chiffré, il ne devrait pas être stocké en tant que global (la réutilisation d'un IV affaiblit la sécurité de la clé, et le stocker en tant que module global signifie qu'il sera régénéré lors de la prochaine invocation Python, rendant tout le texte crypté indécryptable):

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

Cela manque de la protection supplémentaire d'une signature HMAC et il n'y a pas d'horodatage; vous devez les ajouter vous-même.

Ce qui précède illustre également à quel point il est facile de combiner de manière incorrecte des blocs de construction cryptographiques de base. Le traitement incorrect de la valeur de l'IV par tous les utilisateurs peut entraîner une violation des données ou la lisibilité de tous les messages chiffrés car l'IV est perdu. Utiliser plutôt Fernet vous protège de telles erreurs.

AES BCE - pas sécurisé

Si vous avez précédemment implémenté chiffrement AES ECB et que vous devez toujours le prendre en charge dans Python 3, vous pouvez le faire toujours avec cryptography également.) Les mêmes réserves s'appliquent, ECB is non suffisamment sécurisé pour des applications réelles. Réimplémentez cette réponse pour Python 3, en ajoutant le traitement automatique du remplissage:

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

Là encore, il manque la signature HMAC et vous ne devriez pas utiliser ECB de toute façon. Ce qui précède n’est là que pour illustrer le fait que cryptography peut gérer les blocs de construction cryptographiques courants, même ceux que vous ne devriez pas utiliser.

40
Martijn Pieters

Disclaimer: Comme mentionné dans les commentaires, ceci ne doit pas être utilisé pour protéger les données dans une application réelle.

Quel est le problème avec XOR cryptage?

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

https://github.com/hellman/xortool


Comme cela a été mentionné, la bibliothèque PyCrypto contient une suite de chiffrements. Le XOR "chiffre" peut être utilisé pour faire le sale boulot si vous ne voulez pas le faire vous-même:

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

Le chiffrement fonctionne comme suit sans avoir à compléter le texte en clair:

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

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

Merci de https://stackoverflow.com/a/2490376/241294 pour les fonctions d'encodage/décodage en base64 (je suis un python novice).

24
poida

Voici une implémentation du cryptage et du décryptage d'URL Safe en utilisant AES (PyCrypto) et 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 vous rencontrez un problème comme celui-ci https://bugs.python.org/issue4329 (TypeError: le mappage de caractères doit renvoyer un entier, aucun ou unicode), utilisez str (chiffrement) lors du décodage comme suit

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

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
10
All Іѕ Vаиітy

Fonctionnement des fonctions d'encodage/décodage en python3 (très peu adapté de la réponse 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
Christian

Merci pour d'excellentes réponses. Rien d’original à ajouter, mais voici quelques réécritures progressives de la réponse de qneill à l’aide de fonctions utiles Python. J'espère que vous conviendrez qu’elles simplifient et clarifient le code.

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()-- associe les éléments d'une liste à leur index

itérer sur les caractères d'une chaîne

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)

construire des listes en utilisant une compréhension de liste

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)

Souvent dans Python, les index de liste ne sont pas du tout nécessaires. Eliminez entièrement les variables d’index de boucle en utilisant Zip et cycle:

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)

et quelques tests ...

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
6
Nick

Si vous voulez être en sécurité, vous pouvez utiliser Fernet, qui est cryptographiquement correct. Vous pouvez utiliser un "sel" statique si vous ne voulez pas le stocker séparément. Vous ne perdrez que le dictionnaire et la prévention des attaques Rainbow. Je l'ai choisi parce que je peux choisir des mots de passe longs ou courts´, ce qui n'est pas si facile avec 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 c'est trop compliqué, quelqu'un a suggéré simplecrypt

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

Note: J'ai eu des problèmes avec Windows + Python 3.6 + toutes les réponses impliquant pycrypto (impossible de pip install pycrypto sous Windows) ou pycryptodome (les réponses ici avec from Crypto.Cipher import XOR ont échoué car XOR n'est pas pris en charge par ce pycrypto fork; et les solutions utilisant ... AES ont également échoué avec TypeError: Object type <class 'str'> cannot be passed to C code). De plus, la bibliothèque simple-crypt a pycrypto comme dépendance, donc ce n'est pas une option.


Voici une solution utilisant le paquet cryptography, que vous pouvez installer comme d’habitude avec 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()

Usage:

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

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

Remarque:

3
Basj

Une autre implémentation du code @qneill qui inclut la somme de contrôle CRC du message d'origine génère une exception si la vérification échoue:

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
ahmed

Cela fonctionne mais la longueur du mot de passe doit être exactement 8. Ceci est simple et nécessite 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

SORTIE:

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

Vous pouvez utiliser AES pour chiffrer votre chaîne avec un mot de passe. Cependant, vous aurez envie de choisir un mot de passe assez fort pour que les gens ne puissent pas le deviner facilement (désolé, je ne peux pas m'en empêcher. Je suis une pépite de la sécurité qui souhaite devenir wannabe).

AES est puissant avec une bonne taille de clé, mais il est également facile à utiliser avec PyCrypto.

2
Alan

Les bibliothèques externes fournissent des algorithmes de chiffrement à clé secrète.

Par exemple, le module Cypher de PyCrypto propose une sélection de nombreux algorithmes de cryptage:

  • 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 est un wrapper Python pour OpenSSL , et fournit (entre autres fonctions) une bibliothèque de cryptographie complète à usage général. Sont inclus les chiffrements symétriques (comme AES).

1
gimel

Celui qui est venu ici (et le bountier) semblait chercher des one-liners avec peu de configuration, ce que les autres réponses ne fournissent pas. Donc, je mets en avant base64.

Maintenant, gardez à l'esprit qu'il ne s'agit que d'une obfuscation de base et qu'elle se trouve dans ** AUCUN MOYEN POUR LA SÉCURITÉ ** , mais voici quelques 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'

Quelques points à noter:

  • vous voudrez gérer plus/moins d'encodage/décodage de chaîne en octets, en fonction de votre E/S. Regardez dans bytes() et bytes::decode()
  • base64 est facilement reconnaissable aux types de caractères utilisés et se termine souvent par des caractères =. Les gens comme moi vont absolument les décoder dans la console javascript lorsque nous les verrons sur des sites Web. C'est aussi simple que btoa(string) (js)
  • la commande est clé + données, comme dans b64, les caractères qui apparaissent à la fin dépendent de ceux qui sont au début (en raison des décalages d'octets. Wikipedia a quelques explications intéressantes). Dans ce scénario, le début de la chaîne codée sera le même pour tout ce qui est codé avec cette clé. Le plus, c'est que les données seront plus obscurcies. En procédant dans le sens contraire, la partie données sera identique pour tout le monde, quelle que soit la clé.

Maintenant, si ce que vous vouliez n’avait même pas besoin d’une clé quelconque, mais seulement d’un peu d’obscurcissement, vous pouvez encore utiliser base64, sans aucun type de clé:

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'
1
towc

si vous voulez un cryptage sécurisé:

pour python 2, vous devriez utiliser keyczar http://www.keyczar.org/

for python 3, jusqu'à ce que keyczar soit disponible, j'ai écrit simple-crypt http://pypi.python.org/pypi/simple-crypt

les deux utilisent le renforcement des clés, ce qui les rend plus sûrs que la plupart des autres réponses données ici. et comme ils sont si faciles à utiliser, vous voudrez peut-être les utiliser même lorsque la sécurité n'est pas critique ...

0
andrew cooke

Donc, comme , rien d’essentiel à la mission n’est encodé , et vous voulez juste chiffrer pour l’obsfuscation .

Me laisser présenter le chiffre de caeser

enter image description here

Le chiffrement de Caesar ou décalage de César est l'une des techniques de cryptage les plus simples et les plus connues. Il s'agit d'un type de chiffrement de substitution dans lequel chaque lettre du texte en clair est remplacée par une lettre représentant un nombre fixe de positions dans l'alphabet. Par exemple, avec un décalage à gauche de 3, D serait remplacé par A, E deviendrait B, etc.

Exemple de code pour votre référence:

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

Avantages: il répond à vos exigences et est simple et fait l’encodage.

Inconvénient: peut être craqué par de simples algorithmes de force brute (il est très improbable que quelqu'un tente de passer en revue tous les résultats supplémentaires).

0
Yash Mishra