Étonnamment difficile de trouver une réponse directe à cette question sur Google.
Je veux collecter un morceau de texte et un message d'un utilisateur tel que 1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc
hello world
.
Ensuite, je veux pouvoir crypter/décrypter le message avec le texte d'une manière ou d'une autre pour pouvoir l'enregistrer dans ma base de données et ne pas m'inquiéter des données exposées si mon site Web est piraté, encrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', 'hello world')
decrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', <encrypted_text>)
Existe-t-il un moyen simple de réaliser cela avec python et s'il vous plaît, quelqu'un peut-il me donner/me diriger vers un exemple?.
Peut-être un exemple de création de paires de clés publique/privée en utilisant une valeur de départ telle que '1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc'
?
Merci d'avance :)
EDIT: Juste pour être clair, je cherche un moyen de chiffrer les données de mes utilisateurs d’une manière déterministe et non d’obscurcir le message.
Si cela signifie que je dois générer une paire de clés PGP/GPG pub / pri à la volée en utilisant le texte 1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc
comme valeur de départ, tout va bien, mais quelle est la méthode pour ce faire?
Voici comment le faire correctement en mode CBC, y compris le remplissage de PKCS # 7:
import base64
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto import Random
def encrypt(key, source, encode=True):
key = SHA256.new(key).digest() # use SHA-256 over our key to get a proper-sized AES key
IV = Random.new().read(AES.block_size) # generate IV
encryptor = AES.new(key, AES.MODE_CBC, IV)
padding = AES.block_size - len(source) % AES.block_size # calculate needed padding
source += bytes([padding]) * padding # Python 2.x: source += chr(padding) * padding
data = IV + encryptor.encrypt(source) # store the IV at the beginning and encrypt
return base64.b64encode(data).decode("latin-1") if encode else data
def decrypt(key, source, decode=True):
if decode:
source = base64.b64decode(source.encode("latin-1"))
key = SHA256.new(key).digest() # use SHA-256 over our key to get a proper-sized AES key
IV = source[:AES.block_size] # extract the IV from the beginning
decryptor = AES.new(key, AES.MODE_CBC, IV)
data = decryptor.decrypt(source[AES.block_size:]) # decrypt
padding = data[-1] # pick the padding value from the end; Python 2.x: ord(data[-1])
if data[-padding:] != bytes([padding]) * padding: # Python 2.x: chr(padding) * padding
raise ValueError("Invalid padding...")
return data[:-padding] # remove the padding
Il est configuré pour fonctionner avec les données bytes
. Par conséquent, si vous souhaitez chiffrer des chaînes ou utiliser des mots de passe de chaîne, assurez-vous de les encode()
avec un codec approprié avant de les transmettre aux méthodes. Si vous laissez le paramètre encode
à True
, la sortie de encrypt()
sera une chaîne codée en base64, et la source de decrypt()
devrait également être une chaîne base64.
Maintenant, si vous le testez en tant que:
my_password = b"secret_AES_key_string_to_encrypt/decrypt_with"
my_data = b"input_string_to_encrypt/decrypt"
print("key: {}".format(my_password))
print("data: {}".format(my_data))
encrypted = encrypt(my_password, my_data)
print("\nenc: {}".format(encrypted))
decrypted = decrypt(my_password, encrypted)
print("dec: {}".format(decrypted))
print("\ndata match: {}".format(my_data == decrypted))
print("\nSecond round....")
encrypted = encrypt(my_password, my_data)
print("\nenc: {}".format(encrypted))
decrypted = decrypt(my_password, encrypted)
print("dec: {}".format(decrypted))
print("\ndata match: {}".format(my_data == decrypted))
votre sortie serait semblable à:
key: b'secret_AES_key_string_to_encrypt/decrypt_with'
data: b'input_string_to_encrypt/decrypt'
enc: 7roSO+P/4eYdyhCbZmraVfc305g5P8VhDBOUDGrXmHw8h5ISsS3aPTGfsTSqn9f5
dec: b'input_string_to_encrypt/decrypt'
data match: True
Second round....
enc: BQm8FeoPx1H+bztlZJYZH9foI+IKAorCXRsMjbiYQkqLWbGU3NU50OsR+L9Nuqm6
dec: b'input_string_to_encrypt/decrypt'
data match: True
Prouver que la même clé et les mêmes données produisent toujours un cryptogramme différent à chaque fois.
Maintenant, c'est beaucoup mieux que la BCE mais ... si vous allez utiliser ça pour la communication - ne le faites pas! C’est plus pour expliquer comment il devrait être construit, ne pas vraiment être utilisé dans un environnement de production et surtout pas pour la communication car il lui manque un ingrédient crucial - l’authentification de message. N'hésitez pas à jouer avec, mais vous ne devriez pas lancer votre propre crypto, il existe des protocoles bien vérifiés qui vous aideront à éviter les pièges courants et que vous devriez utiliser.
Basé sur les réponses de zwer mais résout un petit bug lorsque la source est exactement un multiple de 16.
Code:
from builtins import bytes
import base64
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto import Random
def encrypt(string, password):
"""
It returns an encrypted string which can be decrypted just by the
password.
"""
key = password_to_key(password)
IV = make_initialization_vector()
encryptor = AES.new(key, AES.MODE_CBC, IV)
# store the IV at the beginning and encrypt
return IV + encryptor.encrypt(pad_string(string))
def decrypt(string, password):
key = password_to_key(password)
# extract the IV from the beginning
IV = string[:AES.block_size]
decryptor = AES.new(key, AES.MODE_CBC, IV)
string = decryptor.decrypt(string[AES.block_size:])
return unpad_string(string)
def password_to_key(password):
"""
Use SHA-256 over our password to get a proper-sized AES key.
This hashes our password into a 256 bit string.
"""
return SHA256.new(password).digest()
def make_initialization_vector():
"""
An initialization vector (IV) is a fixed-size input to a cryptographic
primitive that is typically required to be random or pseudorandom.
Randomization is crucial for encryption schemes to achieve semantic
security, a property whereby repeated usage of the scheme under the
same key does not allow an attacker to infer relationships
between segments of the encrypted message.
"""
return Random.new().read(AES.block_size)
def pad_string(string, chunk_size=AES.block_size):
"""
Pad string the peculirarity that uses the first byte
is used to store how much padding is applied
"""
assert chunk_size <= 256, 'We are using one byte to represent padding'
to_pad = (chunk_size - (len(string) + 1)) % chunk_size
return bytes([to_pad]) + string + bytes([0] * to_pad)
def unpad_string(string):
to_pad = string[0]
return string[1:-to_pad]
def encode(string):
"""
Base64 encoding schemes are commonly used when there is a need to encode
binary data that needs be stored and transferred over media that are
designed to deal with textual data.
This is to ensure that the data remains intact without
modification during transport.
"""
return base64.b64encode(string).decode("latin-1")
def decode(string):
return base64.b64decode(string.encode("latin-1"))
Tests:
def random_text(length):
def Rand_lower():
return chr(randint(ord('a'), ord('z')))
string = ''.join([Rand_lower() for _ in range(length)])
return bytes(string, encoding='utf-8')
def test_encoding():
string = random_text(100)
assert encode(string) != string
assert decode(encode(string)) == string
def test_padding():
assert len(pad_string(random_text(14))) == 16
assert len(pad_string(random_text(15))) == 16
assert len(pad_string(random_text(16))) == 32
def test_encryption():
string = random_text(100)
password = random_text(20)
assert encrypt(string, password) != string
assert decrypt(encrypt(string, password), password) == string
Comme vous l'avez écrit "Je veux être capable de chiffrer/déchiffrer le message", j'attache une source python simple (testée sous 2.7) pour encr/decr en utilisant Blowfish.
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import os
from Crypto.Cipher import Blowfish # pip install pycrypto
BS = 8
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
def doEncrypt(phrase, key):
c1 = Blowfish.new(key, Blowfish.MODE_ECB)
return c1.encrypt(pad(phrase))
def doDecrypt(phrase, key):
c1 = Blowfish.new(key, Blowfish.MODE_ECB)
return unpad(c1.decrypt(phrase))
def testing123(phrase, key):
encrypted = doEncrypt(phrase, key)
decrypted = doDecrypt(encrypted, key)
assert phrase == decrypted, "Blowfish ECB enc/dec verification failed"
print ("Blowfish ECB enc/dec verified ok")
print ('phrase/key(hex)/enc+dec: {}/{}/{}'.format(phrase, key.encode('hex'), decrypted))
if __name__== "__main__":
phrase= 'Ala ma kota, a kot ma AIDS.'
key= os.urandom(32)
testing123(phrase, key)
Vous pouvez le faire en utilisant deux des fonctions intégrées à la bibliothèque Python standard. Le premier est la fonction ord () , qui prend un caractère de chaîne unicode en tant que paramètre d'entrée unique et le convertit en son code unicode correspondant (un entier). Deux exemples simples d'utilisation de cette fonction sont fournis:
>>> ord('a')
97
>>> ord('b')
98
Ensuite, vous avez également la fonction inverse de ord (): chr () . Et comme vous pouvez l’imaginer, cela fonctionne parfaitement: il a un code Unicode en entrée (entier) et obtient le caractère Unicode correspondant (chaîne):
>>> chr(97)
'a'
>>> chr(98)
'b'
Ensuite, vous pouvez faire une simple inscription en ajoutant ou en soustrayant par un nombre entier arbitraire ... dans ce cas, le nombre 2:
REMARQUE: Faites attention de ne pas énoncer de très grandes valeurs, sinon vous obtiendrez un identifiant d'erreur qui vous permettra d'atteindre un nombre négatif, par exemple.
def encrypt(message):
newS=''
for car in message:
newS=newS+chr(ord(car)+2)
return newS
print(encrypt('hello world'))
Et en conséquence:
jgnnq"yqtnf
Vous pouvez maintenant copier et coller la même fonction et générer la fonction de déchiffrement. Dans ce cas, il faut évidemment soustraire par 2:
def decrypt(message):
newS=''
for car in message:
newS=newS+chr(ord(car)-2)
return newS
print(decrypt('jgnnq"yqtnf'))
Et le résultat sera à nouveau le message d'origine:
'hello world'
Ce serait un excellent moyen de chiffrer les messages destinés aux non-programmeurs. Cependant, toute personne ayant un minimum de connaissances en programmation peut écrire un programme qui modifie le nombre entier que nous avons utilisé jusqu'à ce que nous découvrions que nous venons d'ajouter (2) aux caractères unicode pour chiffrer le code ...
Pour éviter cela, je proposerais deux alternatives plus complexes.
1. Le premier est le plus simple: il consiste à appliquer une valeur de somme différente à la fonction chr en fonction de la position du caractère (par exemple, l'ajout de 2 à chaque code unicode lorsqu'il occupe une position paire chaîne et soustraction 3 quand est assis sur une position impaire).
2. Le second générera une sécurité maximale. Cela consistera à ajouter ou à soustraire chaque code unicode pour un numéro qui sera généré aléatoirement pour chaque caractère. Il faudra stocker un tableau de valeurs pour décrypter le message. Assurez-vous donc que ce tableau de valeurs n'est pas disponible pour les tiers.
Voilà une solution possible pour 1 .:
def encryptHard(message):
newS=''
for i in range(len(message)):
if i%2==0:
newS=newS+chr(ord(message[i])+2)
else:
newS=newS+chr(ord(message[i])-3)
return newS
print(encryptHard('hello world'))
Et le résultat serait:
jbniqyltif
Avec les informations fournies par les présentes, le script de déchiffrement est évident, je ne vais donc pas vous déranger pour faire face, modifier et modifier deux valeurs.
Enfin, passons à une analyse approfondie de la deuxième alternative plus complexe. Avec celui-ci, nous pouvons dire que l'inscription sera presque indéfendable. L'idée est de faire varier la valeur ajoutée ou soustraite à chaque code unicode par un nombre aléatoire compris entre 0 et 255 (il s'agit de la plage de nombres que la fonction chr () admet, n'essayez donc pas de jouer avec d'autres nombres définitivement avoir une erreur).
Dans ce cas, ma proposition randomise également l’opération (somme ou soustraction) et évite que le nombre final soit un 0 (c’est-à-dire que nous aurions un caractère original). Enfin, il retourne également une liste des nombres auxquels il a été soustrait, ce dont vous aurez besoin pour déchiffrer le message.
Les chances que vous receviez le même message crypté si vous appelez deux fois cette fonction en utilisant le même message de longueur n sont assez proches de 255 ^ n ... Alors ne vous inquiétez pas (je dis un peu , car l’algorithme créé générerait en réalité davantage de valeurs répétées sur la plage de valeurs inférieure ou supérieure, par exemple, au cas où les caractères les plus fréquents ne seraient pas centrés dans cet ensemble de caracrer de distribution unicode (de 0 à 255), qui Cependant, le programme, bien que pas parfait, fonctionne parfaitement et protège les informations.
import random as r
def encryptSuperHard(message):
newS=''
l_trans=[]
for car in message:
code=ord(car)
add_subtract=r.choice([True,False])
if add_subtract:
transpose=r.randint(0,code-1)
newS=newS+chr(code-transpose)
l_trans=l_trans+[-transpose]
else:
transpose=r.randint(code+1,255)
newS=newS+chr(code+transpose)
l_trans=l_trans+[transpose]
return newS, l_trans
print(encryptSuperHard('hello world'))
Dans ce cas, le script de chiffrement aléatoire que j'ai créé a renvoyé ces deux valeurs, où la première valeur est le message chiffré et la seconde valeur qui a "transposé" chaque caractère dans l'ordre d'apparition.
('A0ŤłY\x10řG;,à', [-39, -53, 248, 214, -22, -16, 226, -40, -55, -64, 124])
Le déchiffrement, dans ce cas, devrait prendre le message chiffré et la liste et procéder comme suit:
def decryptSuperHard(encriptedS,l):
newS=''
for i in range(len(l)):
newS=newS+chr(ord(encriptedS[i])-l[i])
return newS
print(decryptSuperHard('A0ŤłY\x10řG;,à', [-39,-53,248,214,-22,-16,226,-40,-55,-64,124]))
Et le résultat remonte à:
bonjour le monde
print(deccryptSuperHard('A0ŤłY\x10řG;,à', [-39, -53, 248, 214, -22, -16, 226, -40, -55, -64, 124])