Como cifrar o texto com uma senha em python?

Surpreendentemente difícil encontrar uma resposta directa para isto no Google.

Quero recolher um pedaço de texto e uma mensagem de um utilizador como 1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc hello world.

Então eu quero ser capaz de encriptar/descriptografar a mensagem com o texto de alguma forma para que eu possa salvá-la na minha base de dados e não se preocupar com os dados sendo expostos se o meu site for hackeado, encrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', 'hello world') decrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', <encrypted_text>)

Existe uma maneira simples de conseguir isto com o python e por favor alguém pode fornecer/direccionar-me para um exemplo.

talvez um exemplo de como criar pares de chaves público/privado usando uma semente como '1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc'?

Muito obrigado antecipadamente.

EDIT: só para que fique claro, estou à procura de uma forma de encriptar os dados dos meus utilizadores de uma forma dissuasora, não ofuscando a mensagem.

Se isso significa que tenho de gerar um par de chaves PGP/GPG pub / pri em tempo real usando o texto {[[[0]} como uma semente então tudo bem, mas qual é o método para fazer isto?

Author: derrend, 2017-03-03

6 answers

Aqui está como fazê-lo corretamente no modo CBC, incluindo PKCS # 7 padding:
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

Está definido para funcionar com os dados bytes, por isso, se quiser cifrar cadeias de caracteres ou usar senhas de cadeias, certifique-se que as encode() têm um codificador adequado antes de As passar para os métodos. Se deixar o parâmetro encode em True, o resultado encrypt() será o texto codificado de base64, e decrypt() o código também deverá ser o texto de base64.

Agora, se o testares como:

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

A tua saída seria semelhante to:

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
Provando que a mesma chave e os mesmos dados ainda produzem diferentes cifrotexto de cada vez. Isto é muito melhor do que o BCE... se vais usar isto para comunicação, não o faças! Isto é mais para explicar como ele deve ser construído, não realmente para ser usado em um ambiente de produção e, especialmente, não para a comunicação como sua falta um ingrediente crucial - autenticação mensagem. Sinta-se livre para brincar com ele, mas você não deve rolar sua própria cripto, há bem vetado protocolos que irá ajudá-lo a evitar os perigos comuns e você deve usá-los.
 11
Author: zwer, 2017-10-13 00:13:07
  1. Se vai usar a base de dados mencionada para autorizar os utilizadores, deve usar traços ou digestões de mensagens das senhas do utilizador, em vez de algoritmos de encriptação de 2 vias, que tornariam os seus dados difíceis de usar, mesmo em caso de fuga de db.
  2. não pode usar o método acima para proteger os dados que precisam de ser descodificados em algum momento, mas mesmo assim poderá usar uma forma mais segura do que apenas cifrar as senhas do utilizador usando alguma chave fixa (que é o pior método). Dê uma olhada A Placa De Armazenamento De Password do OWASP .

Como escreveu "I want to be able to encrypt/decrypt the message", estou a anexar uma simples fonte python (testada em 2.7) para encr/decr usando 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)
 0
Author: internety, 2017-05-27 16:19:44

Baseado nas respostas do zwer mas resolve um pequeno erro quando a fonte é exactamente um múltiplo de 16.

Código:

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

Testes:

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
 0
Author: Ignacio Tartavull, 2018-02-04 01:20:04
Aqui está a minha solução para quem estiver interessado.
from Crypto.Cipher import AES  # pip install pycrypto
import base64

def cypher_aes(secret_key, msg_text, encrypt=True):
    # an AES key must be either 16, 24, or 32 bytes long
    # in this case we make sure the key is 32 bytes long by adding padding and/or slicing if necessary
    remainder = len(secret_key) % 16
    modified_key = secret_key.ljust(len(secret_key) + (16 - remainder))[:32]
    print(modified_key)

    # input strings must be a multiple of 16 in length
    # we achieve this by adding padding if necessary
    remainder = len(msg_text) % 16
    modified_text = msg_text.ljust(len(msg_text) + (16 - remainder))
    print(modified_text)

    cipher = AES.new(modified_key, AES.MODE_ECB)  # use of ECB mode in enterprise environments is very much frowned upon

    if encrypt:
        return base64.b64encode(cipher.encrypt(modified_text)).strip()

    return cipher.decrypt(base64.b64decode(modified_text)).strip()


encrypted = cypher_aes(b'secret_AES_key_string_to_encrypt/decrypt_with', b'input_string_to_encrypt/decrypt', encrypt=True)
print(encrypted)
print()
print(cypher_aes(b'secret_AES_key_string_to_encrypt/decrypt_with', encrypted, encrypt=False))
Resultado:
b'secret_AES_key_string_to_encrypt'
b'input_string_to_encrypt/decrypt '
b'+IFU4e4rFWEkUlOU6sd+y8JKyyRdRbPoT/FvDBCFeuY='

b'secret_AES_key_string_to_encrypt'
b'+IFU4e4rFWEkUlOU6sd+y8JKyyRdRbPoT/FvDBCFeuY=    '
b'input_string_to_encrypt/decrypt'
 -1
Author: derrend, 2017-05-27 00:36:35

Você pode fazer isso usando duas das funções incorporadas na biblioteca padrão Python. O primeiro é a função ord () , que toma um carácter de cadeia unicode como parâmetro único de entrada e converte-o para o seu código unicode correspondente (um inteiro). Dois exemplos simples do uso desta função são fornecidos:

>>> ord('a')
    97

>>> ord('b')
    98

Então, você também tem a função inversa de ord (): chr () . E como você pode imaginar funciona todo o caminho ao redor: tem um código unicode como uma entrada (inteiro) e recebe o caractere unicode correspondente (string):

>>> chr(97)
    'a'

>>> chr(98)
    'b'

Então você pode fazer uma encrição simples adicionando ou substracting por algum inteiro arbitrário... neste caso, o número 2:

Nota: cuidado em não dizer valores muito grandes ou você vai ter um id de erro que você chega a um nber negativo, por exemplo.

def encrypt(message):
    newS=''
    for car in message:
        newS=newS+chr(ord(car)+2)
    return newS


print(encrypt('hello world'))

E obter como resultado:

jgnnq"yqtnf

Agora você pode copiar e passar pela mesma função e gerar a função de descodificação. Neste caso, é necessário, obviamente, subtrair até 2:

def decrypt(message):
    newS=''
    for car in message:
        newS=newS+chr(ord(car)-2)
    return newS


print(decrypt('jgnnq"yqtnf'))

E o resultado será a mensagem original novamente:

'hello world'
Esta seria uma ótima maneira de encriptar mensagens para não programadores. No entanto, qualquer um com um pouco de conhecimento de programação poderia escrever um programa que variasse o número inteiro que usamos até que eles descobriram que acabamos de adicionar (2) aos caracteres unicode para criptografar o código... Para evitar isso, proponho duas mais complexas: alternativa.

1. o primeiro é o mais simples: consiste em aplicar um valor de soma diferente à função chr dependendo da posição do carácter (por exemplo, adicionando 2 a cada código unicode quando ocupa uma posição par na cadeia e substracting 3 quando se senta numa posição ímpar).

2.A segunda gerará a máxima segurança. Ele consistirá em Adicionar ou substractar cada código unicode para um número que será aleatoriamente gerado para cada personagem. Será necessário armazenar um array de valores para decriptar de volta a mensagem. Certifique-se, então, que este conjunto de valores não está disponível para terceiros.

Lá vai uma possível solução para 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'))

E o resultado seria:

jbniqyltif

Com a informação por este meio, desde que o script de descriptografia é óbvio, então eu não vou incomodá-lo em lidar, moldar e mudar dois valores.

Finalmente, vamos entrar num análise aprofundada da segunda alternativa mais complexa. Com este podemos dizer que a inscrição será quase inevitável. A ideia é variar o valor que adicionar ou subtrair para cada código unicode por um número aleatório comprized entre 0 e 255 (este é o intervalo de números a função chr( ) admite-se, portanto, não tente jogar com outros números, o que você vai definitivamente ter um erro). Neste caso, a minha proposta também aleatoriza a operação (soma ou subtracção), e evita que a Número ser um 0 (ou seja, obteríamos um caráter original). Finalmente, também devolve uma lista com os números a que foi subtraída, algo de que irá precisar para descodificar a mensagem de volta.

As hipóteses de obter a mesma mensagem encriptada se ligar duas vezes para esta função usando a mesma mensagem de comprimento n estão um pouco perto de 255^n {15]}... Então não se preocupe (eu digo um pouco, como o algoritmo criado realmente geraria valores mais repetidos no por exemplo, no caso de os caracteres mais frequentes não estarem centrados neste conjunto de caracrer unicode da distrubution (de 0 a 255), o que é o caso. No entanto, o programa, embora não perfeito, funciona perfeitamente e protege a informação.

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

Neste caso, este programa de cifragem aleatória que fiz devolveu esta tupla de dois valores, onde o primeiro valor é a mensagem encriptada e o segundo é o valor que "transpôs" cada carácter por ordem de apearance.

('A0ŤłY\x10řG;,à', [-39, -53, 248, 214, -22, -16,     226, -40, -55, -64, 124])

A descodificar, neste caso teria de levar a mensagem encriptada e a lista e proceder da seguinte forma:

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]))
E os resultados remontam a:
Olá, mundo.
print(deccryptSuperHard('A0ŤłY\x10řG;,à', [-39, -53, 248, 214, -22, -16,     226, -40, -55, -64, 124])
 -1
Author: americansanti, 2017-05-27 05:08:36
alpha = "abcdefghijklmnopqrstuvwxyz"
password = input("What is the password?:")
password = "".join([(str(ord(x)-96) if x.isalpha() else x) for x in list(password)])
password = int(password)

def encrypt(cleartext):
  cyphertext = ""
  for char in cleartext:
    if char in alpha:
      newpos = (alpha.find(char) + password) % 26
      cyphertext += alpha[newpos]
    else:
      cyphertext += char

  return cyphertext

def decrypt(cleartext):
  cyphertext = ""
  for char in cleartext:
    if char in alpha:
      newpos = (alpha.find(char) - password) % 26
      cyphertext += alpha[newpos]
    else:
      cyphertext += char

  return cyphertext

while True:
  cleartext = input("Cleartext:")
  cleartext = cleartext.lower()
  print(encrypt(cleartext))

  cleartext = input("Cyphertext:")
  cleartext = cleartext.lower()
  print(decrypt(cleartext))
Então é assim que eu faço!
 -1
Author: Walter Fager, 2018-05-15 00:59:05