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.
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.
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.
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é!
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)
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.
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.
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'
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é .
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='
.
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.
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.
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.
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.
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).
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
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)
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 indexité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
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)
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:
vous pouvez vous adapter avec votre propre sel, le nombre d'itérations, etc.
pas très loin de la réponse de @ HCLivess mais l'objectif est d'avoir des fonctions encrypt
et decrypt
prêtes à l'emploi
Source: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet
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
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
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.
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).
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:
bytes()
et bytes::decode()
=
. 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)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'
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 ...
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
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).