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


123

Python dispose-t-il d'un moyen simple et intégré d'encoder / décoder des chaînes à l'aide d'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 chiffrée sous la forme «sjkl28cn2sx0». Pour obtenir la chaîne d'origine, je "déverrouillerais" cette chaîne avec la clé 'mypass', qui est un mot de passe dans mon code source. J'aimerais que ce soit la façon dont je peux crypter / décrypter un document Word avec un mot de passe.

Je souhaite utiliser ces chaînes cryptées comme paramètres d'URL. Mon objectif est l'obscurcissement, pas une sécurité solide; rien de critique à la mission n'est encodé. Je me rends compte 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.


28
Le terme «mot de passe» ici est inapproprié. Vous l'utilisez comme clé cryptographique et vous devez utiliser cette terminologie pour éviter toute confusion dans vos questions, ainsi que dans tous les documents, commentaires, spécifications, plans de test, etc.
Jim Dennis

2
"J'aimerais que ce soit la façon dont je peux crypter / décrypter un document Word avec un mot de passe.", Word a déjà une option intégrée pour crypter vos documents si vous avez juste besoin de crypter des documents Word.
Byron Filer

2
Fait intéressant, selon ce document de recherche sur les pièges du stockage des mots de passe comme celui-ci , les développeurs qui utilisent Stack Overflow ont tendance à produire du code moins sécurisé. Gee, je me demande pourquoi?
forest

Aussi, il faut lire cette réponse de security.SE
kelalaka

Réponses:


70

En supposant que vous ne recherchiez qu'une simple obfuscation qui obscurcira les choses pour l' observateur très occasionnel, et que vous ne cherchiez pas à utiliser des bibliothèques tierces. Je recommanderais quelque chose comme le chiffre Vigenere. C'est l'un des plus puissants des chiffrements anciens simples.

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)

Le décodage est à peu près le même, 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 de passe utilisée.

Si vous recherchez quelque chose de cryptographique, PyCrypto est probablement votre meilleur pari, bien que les réponses précédentes négligent certains détails: le mode ECB dans PyCrypto nécessite 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 standard. Cela remplace quelques-uns des caractères de l'alphabet base64 par des caractères sécurisés pour les URL (comme son nom l'indique).

Cependant, vous devez être absolument certain que cette très fine couche d'obfuscation suffit à vos besoins avant de l'utiliser. L'article de Wikipédia auquel j'ai lié fournit des instructions détaillées pour casser le chiffre, de sorte que toute personne avec une détermination modérée pourrait facilement le casser.


6
J'ai corrigé le script de smehmood et ajouté la fonction de décodage gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester

3
Attention! Le code de smehmood et le correctif d'Adrian Mester ne fonctionnent que pour les chaînes avec des caractères de la plage ascii inférieure! Voir la priorité de l'opérateur%, l'entrée Unicode etc. Voir la réponse de qneill pour le code de travail
le_m

2
@smehmood J'obtiens l'erreur suivante'str' object cannot be interpreted as an integer
Rohit Khatri

3
"for i in xrange (string)" peut avoir besoin de changer pour "for i in xrange (len (string))"
user3113626

2
encodeur et décodeur pour python 2 et 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman

72

Comme vous déclarez explicitement que vous voulez 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:

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 quelqu'un met la main sur votre base de données et votre base de code, il pourra décoder les données cryptées. Gardez votre secret_keysécurité!


3
Je ne pense pas que cela fonctionnera à moins que msg_text soit un multiple de 16 octets de longueur, car le cryptage AES nécessite des blocs multiples de 16 de longueur. Une implémentation fonctionnelle pour msg_text de longueur arbitraire devrait ajouter un remplissage à la chaîne pour obtenir un multiple de 16 de longueur.
tohster

6
Un exemple avec padding: paste.ubuntu.com/11024555 Cela fonctionne avec un mot de passe et une longueur de message arbitraires.
iman

3
@Ethan non, cette encryptfonction particulière est avec état dlitz.net/software/pycrypto/api/current/… donc vous ne devriez pas essayer de la réutiliser.
Sera

1
@Will +1 Merci pour les informations et le lien. M'a sauvé d'un correctif de bogue très coûteux à l'avenir.
Ethan

3
re - "ne jamais utiliser ECB dans des systèmes puissants évidemment": je fais juste des expériences avec ça pour mon propre plaisir. mais j'ai vu le commentaire ci-dessus dans votre code. pour ceux d'entre nous avec un minimum de sécurité / cryptage / théorie de l'information, pourquoi le "ne jamais utiliser"? peut-être a besoin d'une autre question ... ou peut-être qu'il y a un lien dessus.
Trevor Boyd Smith

69

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; des schémas de cryptage triviaux qu'un développeur considère comme non sécurisés 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 cryptez, cryptez correctement.

Cependant, vous n'avez pas besoin de beaucoup de travail pour implémenter un schéma de cryptage approprié. Tout d'abord, ne réinventez pas la roue de cryptographie , utilisez une bibliothèque de cryptographie de confiance pour gérer cela pour vous. Pour Python 3, cette bibliothèque de confiance est cryptography.

Je recommande également que le cryptage et le décryptage s'appliquent aux octets ; encoder d'abord les messages texte en octets; stringvalue.encode()encode en UTF8, facilement rétabli en utilisant bytesvalue.decode().

Enfin, 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 par un humain, c'est quelque chose que vous stockez dans un endroit secret mais lisible par machine, alors qu'un mot de passe peut souvent être lisible et mémorisé par l'homme. Vous pouvez dériver une clé 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 qu'une attention humaine ne soit nécessaire pour continuer à l'exécuter, vous souhaitez utiliser une clé. Les mots de passe sont utilisés lorsque seul un utilisateur final a besoin d'accéder aux informations spécifiques. Même dans ce cas, vous sécurisez généralement l'application avec un mot de passe, puis échangez des informations cryptées à l'aide d'une clé, peut-être attachée au compte utilisateur.

Chiffrement à clé symétrique

Fernet - AES CBC + HMAC, fortement recommandé

La cryptographybibliothèque comprend la recette Fernet , une recette des meilleures pratiques pour l'utilisation de la cryptographie. Fernet est un standard ouvert , avec des implémentations prêtes dans un large éventail de langages de programmation et il intègre le cryptage AES CBC pour vous avec des informations de version, un horodatage et une signature HMAC pour empêcher la falsification des messages.

Fernet facilite le chiffrement et le déchiffrement des messages et vous protège. C'est la méthode idéale pour crypter 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 également utiliser un mot de passe (section suivante), mais une clé secrète complète de 32 octets (16 octets pour chiffrer avec, plus 16 autres pour la signature) sera plus sécurisée que la plupart des mots de passe auxquels vous pourriez penser.

La clé que Fernet génère est un bytesobjet avec des caractères base64 URL et fichier sûr, donc imprimable:

from cryptography.fernet import Fernet

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

Pour crypter ou décrypter des messages, créez une Fernet()instance avec la clé donnée et appelez Fernet.encrypt()ou Fernet.decrypt(), le message en clair à crypter et le jeton crypté sont des bytesobjets.

encrypt()et les decrypt()fonctions ressembleraient à:

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 d' utiliser une méthode de dérivation de clé forte . Vous devez ensuite inclure le salt et le nombre d'itérations HMAC dans le message, de sorte que la valeur chiffrée n'est plus compatible avec Fernet sans d'abord séparer salt, count et Fernet token:

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 chiffrée est entièrement aléatoire, quelle que soit la réutilisation du mot de passe ou la répétition du message. L'inclusion du nombre d'itérations garantit que vous pouvez ajuster les performances du processeur au fil du temps sans perdre la capacité de déchiffrer les anciens messages.

Un mot de passe seul peut être aussi sûr qu'une clé aléatoire Fernet de 32 octets, à condition de générer un mot de passe correctement aléatoire à partir d'un pool de taille similaire. 32 octets vous donne un nombre de 256 ^ 32 touches, donc si vous utilisez un alphabet de 74 caractères (26 majuscules, 26 minuscules, 10 chiffres et 12 symboles possibles), votre mot de passe doit comporter au moins math.ceil(math.log(256 ** 32, 74))== 42 caractères. Cependant, un plus grand nombre d'itérations HMAC bien sélectionnées peut atténuer quelque peu le manque d'entropie, car cela rend beaucoup plus coûteux pour un attaquant de se frayer un chemin.

Sachez simplement que le choix d'un mot de passe plus court mais toujours raisonnablement sécurisé ne paralysera pas ce schéma, il réduit simplement le nombre de valeurs possibles qu'un attaquant par force brute devrait rechercher; assurez-vous de choisir un mot de passe suffisamment fort pour vos exigences de sécurité .

Alternatives

Obscurcissant

Une alternative est de ne pas crypter . Ne soyez pas tenté d'utiliser simplement un chiffrement à faible sécurité ou une implémentation à domicile de, disons Vignere. Il n'y a pas de sécurité dans ces approches, mais peuvent donner à un développeur inexpérimenté qui a la tâche de maintenir votre code à l'avenir l'illusion de la sécurité, ce qui est pire que pas de sécurité du tout.

Si tout ce dont vous avez besoin est d'obscurité, base64 les données; pour les exigences de sécurité URL, la base64.urlsafe_b64encode()fonction est correcte. N'utilisez pas de mot de passe ici, encodez simplement et vous avez terminé. Tout 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))

Cela se transforme b'Hello world!'en b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Intégrité uniquement

Si tout ce dont vous avez besoin est un moyen de vous assurer que les données peuvent être fiables pour ne pas être modifiées après avoir été envoyées à un client non approuvé et reçues en retour, alors vous voulez signer les données, vous pouvez utiliser la hmacbibliothèque pour cela avec SHA1 (toujours considéré comme sécurisé 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 les données, puis attachez la signature avec les données et envoyez-la au client. Lorsque vous recevez les données, divisez 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 la itsdangerousbibliothèque , qui contient 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 nourrir votre système de données absurdes pour garder votre service occupé à tourner en rond avec une mauvaise entrée, car le texte chiffré est signé.

Le chiffrement par blocs en mode Galois / Counter produit un texte chiffré et une étiquette pour servir le même but, donc peut être utilisé pour servir les mêmes buts. L'inconvénient est que, contrairement à Fernet, il n'y a pas de recette universelle facile à utiliser à réutiliser sur d'autres plates-formes. AES-GCM n'utilise pas non plus de remplissage, donc ce texte chiffré de chiffrement correspond à la longueur du message d'entrée (alors que Fernet / AES-CBC crypte les messages en blocs de longueur fixe, obscurcissant quelque peu la longueur du message).

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

key = secrets.token_bytes(32)

puis utilisez

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 pour prendre en charge les mêmes cas d'utilisation de durée de vie que Fernet prend en charge.

Autres approches sur cette page, en Python 3

AES CFB - comme CBC mais sans besoin de pad

C'est l'approche que suit All Іѕ Vаиітy , quoique incorrectement. Ceci est la cryptographyversion, mais notez que j'inclus l'IV dans le texte chiffré , il ne doit 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 re-généré la prochaine invocation Python, rendant tout le texte chiffré indéchiffrable):

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

Il n'y a pas de blindage supplémentaire d'une signature HMAC et il n'y a pas d'horodatage; vous devrez les ajouter vous-même.

Ce qui précède illustre également à quel point il est facile de combiner incorrectement les blocs de base de la cryptographie; Toute manipulation incorrecte de la valeur IV par Vаиітy peut entraîner une violation de données ou tous les messages cryptés sont illisibles parce que l'IV est perdu. L'utilisation de Fernet vous protège à la place de telles erreurs.

AES ECB - non sécurisé

Si vous avez précédemment implémenté le chiffrement AES ECB et que vous devez toujours le prendre en charge dans Python 3, vous pouvez toujours le faire avec cryptography. Les mêmes mises en garde s'appliquent, ECB n'est pas suffisamment sécurisé pour les applications réelles . Ré-implémenter cette réponse pour Python 3, en ajoutant la gestion 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()

Encore une fois, 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 ce qui cryptographypeut gérer les blocs de construction cryptographiques courants, même ceux que vous ne devriez pas utiliser.


51

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

Voici les fonctions d'encodage / décodage fonctionnelles.

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)

Avertissement: comme le suggèrent les commentaires, cela ne doit pas être utilisé pour protéger les données dans une application réelle, sauf si vous lisez ceci et que cela ne vous dérange pas de parler avec des avocats:

Quel est le problème avec le cryptage XOR?


2
Très utile, merci. J'ai posté une version de Python 3 ci-dessous (elle avait l'air moche dans les commentaires)
Ryan Barrett

1
"Loi de Schneier" : N'importe qui, de l'amateur le plus désemparé au meilleur cryptographe, peut créer un algorithme qu'il ne peut pas casser lui-même. Ne l'utilisez pas, ce n'est même pas près d'être sécurisé.
zaph

3
Génial! Cette version fonctionne également pour les chaînes avec des accents, contrairement à la version de @ smehmood. Exemple d'utilisation:, encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)cela fonctionne très bien.
Basj

2
Voici un plugin de texte Sublime basé sur ce code, permettant un encodage / décodage facile du texte avec CTRL + SHIFT + P puis "Eeencode" ou "Dddecode".
Basj

2
@basj Merci pour l'exemple
sk03

49

Voici une version Python 3 des fonctions de l » @qneill 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)

Le codage / décodage supplémentaire est nécessaire car Python 3 a divisé les chaînes / tableaux d'octets en deux concepts différents et a mis à jour leurs API pour refléter cela.


4
Merci Ryan, fwiw vous avez tapé @qniell
qneill

3
Pour ceux qui se demandent, les différences sont les .encode()).decode(). dans le retour de encode(), et .decode()dans la deuxième ligne dans decode().
RolfBly

2
Hmm, le code encodé n'est pas vraiment unique, j'ai fait un test et il montre chaque clé de code de 11,22,33,44, ..., 88,99,111,222, ... toujours avoir un autre code que précédemment. Mais je l'apprécie
Hzzkygcs

1
@Ryan Barrett, est-il possible de vérifier l'exactitude du mot de passe lors du décodage. Disons que j'envoie une chaîne codée qui connaît la clé, que se passe-t-il s'il entre la clé avec une faute de frappe? Le décodage lui donne toujours une chaîne "décodée", mais ce n'est pas la bonne, comment il peut le dire?
Heinz

26

Avertissement: comme mentionné dans les commentaires, cela ne doit pas être utilisé pour protéger des données dans une application réelle.

Quel est le problème avec le cryptage XOR?

/crypto/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 "chiffrement" XOR 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 à remplir le texte en clair:

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

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

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


note: le module Crypto est installé dans python3 par pycrptop installé, pas Crypto. sudo pip3 install pycrypto.
Nikhil VJ

2
Remarque: pycrypto n'a pas réussi à installer sur herokuapp de mon côté. J'ai trouvé cette publication .. semble dire que le paquet pycrypto a été remplacé par un autre appelé pycryptodome insteal, et que la méthode XOR est obsolète: github.com/digitalocean/netbox/issues/1527
Nikhil VJ

2
N'utilisez jamais cette méthode , notez la description de ce 'chiffrement' dans la documentation : XOR toy cipher, XOR est l'un des chiffrements de flux les plus simples. Le cryptage et le décryptage sont effectués par XOR-ing des données avec un flux de clé réalisé en contaténant la clé. Ne l'utilisez pas pour de vraies applications! .
Martijn Pieters

@MartijnPieters vous avez raison. J'espère que ma modification a clarifié ce point.
poida

12

Voici une implémentation du cryptage et du décryptage URL Safe utilisant AES (PyCrypto) et 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 vous rencontrez un problème comme celui-ci, utilisez https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode) str(cipher)lors du décodage comme suit:

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

Tester:

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 avec Windows x64 + Python 3.6 + PyCryptodome (comme pycrypto est dépréciée): TypeError: Object type <class 'str'> cannot be passed to C code.
Basj

@Basj aww désolé .. Je n'utilise pas Windows, donc je ne peux pas faire de solution.
Tout le temps

Ne générez pas l'IV et ne le stockez pas au niveau du module, vous devez inclure l'IV dans le message chiffré renvoyé! Vous avez maintenant introduit deux problèmes: le redémarrage du processus Python vous donne un nouvel IV rendant impossible le déchiffrement des messages précédemment chiffrés, et en attendant, vous réutilisez l'IV pour plusieurs messages, ce qui réduit efficacement la sécurité au niveau ECB.
Martijn Pieters

1
@ AllІѕVаиітy Résolu avec b'...', j'ai édité la réponse pour référence future!
Basj

8

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

Merci pour ces bonnes réponses. Rien d'original à ajouter, mais voici quelques réécritures progressives de la réponse de qneill en utilisant des fonctionnalités Python utiles. J'espère que vous conviendrez qu'ils 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()- associer les éléments d'une liste avec 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, en Python, il n'y a pas du tout besoin d'index de liste. Éliminez 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

Très joli @Nick, bonne progression des pythonismes, et des tests aussi boot. Pour donner un crédit approprié, je réparais juste un bogue dans la réponse originale de Smehmood stackoverflow.com/a/2490718/468252 .
qneill

4

Si vous voulez être en sécurité, vous pouvez utiliser Fernet, qui est cryptographiquement sain. Vous pouvez utiliser un «sel» statique si vous ne voulez pas le stocker séparément - vous ne perdrez que la prévention des attaques par dictionnaire et arc-en-ciel. 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)

Il suffit de générer un sel et de l'inclure dans le résultat du cryptage afin que les mots de passe et les messages répétés entraînent toujours une sortie aléatoire. Incluez également la valeur des itérations pour pérenniser l'algorithme tout en étant capable de déchiffrer les messages en utilisant un nombre d'itérations différent.
Martijn Pieters

4

Je vais donner 4 solutions:

1) Utilisation du cryptage Fernet avec la cryptographybibliothèque

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

Vous pouvez adapter avec votre propre sel, nombre d' itérations, etc. Ce code est pas très loin de la réponse de @ HCLivess mais le but ici est d'avoir prêt à l'emploi encryptet les decryptfonctions. Source: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Remarque: utilisez .encode()et .decode()partout si vous voulez des chaînes 'John Doe'au lieu d'octets comme b'John Doe'.


2) Chiffrement AES simple avec Cryptobibliothèque

Cela fonctionne avec 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

Remarque: vous pouvez supprimer base64.b64encodeet .b64decodesi vous ne voulez pas de sortie lisible en texte et / ou si vous voulez quand même enregistrer le texte chiffré sur le disque sous forme de fichier binaire.


3) AES utilisant une meilleure fonction de dérivation de clé de mot de passe et la possibilité de tester si "mauvais mot de passe entré", avec Cryptobibliothèque

La solution 2) avec AES "mode CFB" est correcte, mais présente deux inconvénients: le fait que cela SHA256(password)puisse être facilement forcé avec une table de recherche, et qu'il n'y a aucun moyen de tester si un mot de passe incorrect a été saisi. Ceci est résolu ici par l'utilisation d'AES en "mode GCM", comme discuté dans AES: comment détecter qu'un mot de passe incorrect a été saisi? et Cette méthode pour dire «Le mot de passe que vous avez entré est incorrect» est-elle sécurisée? :

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) Utilisation de RC4 (aucune bibliothèque nécessaire)

Adapté 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'

(Obsolète depuis les dernières modifications, mais conservé pour référence future): j'ai eu des problèmes avec Windows + Python 3.6 + toutes les réponses impliquant pycrypto(impossible pip install pycryptosur Windows) ou pycryptodome(les réponses ici ont from Crypto.Cipher import XORéchoué car elles XORne sont pas prises en charge par cette pycryptofourchette; et les solutions utilisant ont ... AESéchoué aussi avec TypeError: Object type <class 'str'> cannot be passed to C code). De plus, la bibliothèque simple-crypta une pycryptodépendance, ce n'est donc pas une option.


1
Vous ne voulez pas coder en dur le sel et l'itération compte; générer un sel aléatoire et rendre le nombre d'itérations configurable lors du chiffrement, inclure ces informations dans le résultat du chiffrement et extraire et utiliser les valeurs lors du déchiffrement. Le sel vous protège de la reconnaissance triviale des mots de passe réutilisés sur un message donné, le nombre d'itérations prouve l'algorithme pour l'avenir.
Martijn Pieters

Oui bien sûr @MartijnPieters, mais le but est ici d'avoir un code simple à des fins simples, comme demandé par OP, avec deux paramètres : texte brut + mot de passe. Bien entendu pour un scénario plus complexe (ie une base de données), vous utiliserez tous ces paramètres supplémentaires.
Basj

Il n'y a pas besoin de paramètres supplémentaires! J'inclus ces informations encodées dans la valeur de retour opaque de password_encrypt().
Martijn Pieters

@MartijnPieters Belle solution en effet.
Basj

3

Quiconque est venu ici (et le bountier) semblait chercher des one-liners avec peu de configuration, ce que les autres réponses ne fournissent pas. Je propose donc base64.

Maintenant, gardez à l'esprit qu'il s'agit d'un obscurcissement de base uniquement, et qu'il est pas possible 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 vous-même plus / moins d'encodage / décodage d'octet à chaîne, en fonction de vos E / S. Examinerbytes() etbytes::decode()
  • base64 est facilement reconnaissable par les types de caractères utilisés, et se termine souvent par = caractères. Les gens comme moi vont absolument les décoder dans la console javascript lorsque nous les voyons sur des sites Web. C'est aussi simple que btoa(string)(js)
  • l'ordre est clé + données, comme dans b64, les caractères qui apparaissent à la fin dépendent des caractères au début (à cause 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é. L'avantage est que les données seront plus obscurcies. Si vous procédez à l'inverse, la partie données sera exactement la même pour tout le monde, quelle que soit la clé.

Maintenant, si ce que vous vouliez n'avait même pas besoin d'une clé d'aucune sorte, mais juste d'un obscurcissement, vous pouvez encore une fois simplement 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'

2
Oui, si vous n'êtes pas préoccupé par la sécurité, alors base64 est bien meilleur que de chiffrer.
Martijn Pieters

A propos des autres réponses n'étant pas des one-liners: ce n'est pas le but de la question. Ils demandent deux fonctions à appeler. Et Fernet(key).encrypt(message)c'est juste une expression comme votre appel base64.
Martijn Pieters

Et vous devez supprimerkey complètement le fichier. Les charges de développeurs vont sans prêter attention et copier - coller de débordement de la pile et ne prennent la clé secrète. Si vous devez l'inclure, alors à tout le moins ne pas l'utiliser et avertir ou lever une exception si elle est quand même utilisée. Ne sous-estimez pas la folie de la culture du copier-coller et vos responsabilités à remplir des fonctions saines.
Martijn Pieters

2

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

PRODUCTION:

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

N'utilisez pas de perfusion intraveineuse fixe! Aléatoire l'IV et l'inclure avec le texte chiffré à la place. Sinon, vous pouvez également utiliser le mode ECB; les messages répétés en texte brut sont par ailleurs faciles à reconnaître.
Martijn Pieters

En outre, le projet pyDes semble être mort; la page d'accueil a disparu et la dernière version sur PyPI a maintenant 9 ans.
Martijn Pieters

2

Une autre implémentation du code @qneill qui inclut la somme de contrôle CRC du message d'origine, elle lève 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

Vous pouvez utiliser AES pour crypter votre chaîne avec un mot de passe. Cependant, vous voudrez choisir un mot de passe assez fort pour que les gens ne puissent pas facilement deviner ce que c'est (désolé je ne peux pas m'en empêcher. Je suis un weenie de sécurité en herbe).

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


3
Merci Alan. Mais pour clarifier, je ne crypte pas les mots de passe eux-mêmes. Dans l'exemple ci-dessus, je crypte la chaîne "John Doe" en fonction du mot de passe "mypass", qui est un mot de passe simple que j'utilise dans mon code source. Les mots de passe des utilisateurs ne sont pas impliqués, ni aucune autre information très sensible. J'ai édité ma question pour clarifier cela.
RexE

1
AES est génial, s'il est utilisé correctement. Il est cependant facile de ne pas l'utiliser correctement; il y a au moins une réponse ici qui utilise un mode de chiffrement par bloc non sécurisé, deux autres qui manipulent la valeur IV. Mieux vaut utiliser une bonne bibliothèque avec une recette bien définie comme Fernet!
Martijn Pieters

En fait, c'est une observation très astucieuse. J'ai fouillé le IV une fois.
Alan

0

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

Par exemple, le Cyphermodule de PyCrypto propose une sélection de nombreux algorithmes de chiffrement:

  • 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 Pythonwrapper pour OpenSSL et fournit (entre autres fonctions) une bibliothèque de cryptographie polyvalente complète. Des chiffrements symétriques (comme AES) sont inclus.


0

si vous souhaitez un cryptage sécurisé:

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

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

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


Depuis le référentiel Keyczar : Remarque importante: Keyczar est obsolète. Les développeurs de Keyczar recommandent Tink , mais il n'y a pas de version Python de Tink.
Martijn Pieters

0

Donc, comme rien de critique à la mission n'est encodé , vous voulez simplement chiffrer pour l' obsfuscation .

Permettez-moi de vous présenter le chiffre de Céser

entrez la description de l'image ici

Le chiffrement de César ou changement de César, est l'une des techniques de chiffrement les plus simples et les plus connues. C'est un type de chiffrement de substitution dans lequel chaque lettre du texte brut est remplacée par une lettre à 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, et ainsi de suite.

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 "y".

Inconvénient: peut être craqué par de simples algorithmes de force brute (très peu probable que quiconque essaie de parcourir tous les résultats supplémentaires).


0

Ajout d'un autre code avec décoder et encoder pour référence

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()
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.