Maneira simples de codificar uma string de acordo com uma senha?

122

O Python tem uma maneira simples e integrada de codificar / decodificar strings usando uma senha?

Algo assim:

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

Portanto, a string "John Doe" é criptografada como 'sjkl28cn2sx0'. Para obter a string original, eu "desbloquearia" essa string com a chave 'mypass', que é uma senha no meu código-fonte. Gostaria que essa fosse a maneira como posso criptografar / descriptografar um documento do Word com uma senha.

Eu gostaria de usar essas strings criptografadas como parâmetros de URL. Meu objetivo é ofuscação, não forte segurança; nada de missão crítica está sendo codificado. Sei que poderia usar uma tabela de banco de dados para armazenar chaves e valores, mas estou tentando ser minimalista.

RexE
fonte
28
O termo "senha" aqui é inapropriado. Você está usando isso como uma chave criptográfica e deve usar essa terminologia para evitar confusão em suas perguntas, bem como em quaisquer documentos, comentários, especificações, planos de teste, etc.
Jim Dennis,
2
“Eu gostaria que esta fosse a maneira que eu pudesse criptografar / descriptografar um documento do Word com uma senha.”, O Word já tem uma opção embutida para criptografar seus documentos se você precisar apenas criptografar documentos do Word.
Byron Filer,
2
Curiosamente, de acordo com este artigo de pesquisa sobre armadilhas de armazenamento de senha como essa , os desenvolvedores que usam Stack Overflow tendem a produzir código menos seguro. Puxa, eu me pergunto por quê?
floresta
Além disso, deve-se ler esta resposta do security.SE
kelalaka
Não se implementa simplesmente codificação / decodificação simplesmente
luckyging3r

Respostas:

70

Supondo que você esteja procurando apenas uma ofuscação simples que irá obscurecer as coisas do observador muito casual, e você não está procurando usar bibliotecas de terceiros. Eu recomendaria algo como a cifra de Vigenere. É uma das mais fortes cifras antigas simples.

Cifra de Vigenère

É rápido e fácil de implementar. Algo como:

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)

Decodificar é praticamente o mesmo, exceto que você subtrai a chave.

É muito mais difícil quebrar se as strings que você está codificando forem curtas e / ou se for difícil adivinhar o comprimento da frase-senha usada.

Se você está procurando por algo criptográfico, PyCrypto é provavelmente sua melhor aposta, embora as respostas anteriores negligenciem alguns detalhes: O modo ECB no PyCrypto requer que sua mensagem seja um múltiplo de 16 caracteres. Então, você deve preencher. Além disso, se você quiser usá-los como parâmetros de URL, use base64.urlsafe_b64_encode(), em vez do padrão. Isso substitui alguns dos caracteres no alfabeto base64 por caracteres seguros para URL (como o nome sugere).

No entanto, você deve estar ABSOLUTAMENTE certo de que essa camada muito fina de ofuscação é suficiente para suas necessidades antes de usá-lo. O artigo da Wikipedia ao qual vinculei fornece instruções detalhadas para quebrar a cifra, de modo que qualquer pessoa com uma quantidade moderada de determinação poderia quebrá-la facilmente.

smehmood
fonte
6
Corrigi o script de smehmood e adicionei a função de decodificação gist.github.com/ilogik/6f9431e4588015ecb194
Adrian Mester
3
Atenção! O código de smehmood e a correção de Adrian Mester funcionam apenas para strings com caracteres da faixa ascii inferior! Veja a precedência do operador%, entrada unicode etc. Veja a resposta de qneill para o código de trabalho
le_m
2
@smehmood Estou recebendo o seguinte erro'str' object cannot be interpreted as an integer
Rohit Khatri
3
"for i in xrange (string)" pode precisar mudar para "for i in xrange (len (string))"
user3113626
2
codificador e decodificador para python 2 e 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
iman
71

Como você afirma explicitamente que deseja obscuridade e não segurança, evitaremos repreendê-lo pela fraqueza do que você sugere :)

Então, usando 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)

Se alguém conseguir seu banco de dados e sua base de código, será capaz de decodificar os dados criptografados. Mantenha seu secret_keyseguro!

Vai
fonte
3
Eu não acho que isso funcionará a menos que msg_text seja um múltiplo de 16 bytes de comprimento, uma vez que a criptografia AES requer blocos que são múltiplos de 16 de comprimento. Uma implementação funcional para msg_text de comprimento arbitrário precisaria adicionar preenchimento à string para obter um múltiplo de 16 de comprimento.
tohster
6
Um exemplo com preenchimento: paste.ubuntu.com/11024555 Ele funciona com senha arbitrária e comprimento de mensagem.
iman
3
@ Ethan não, esta encryptfunção em particular é dlitz.net/software/pycrypto/api/current/… então você não deve tentar reutilizá-la.
Será em
1
@Will +1 Obrigado pela informação e pelo link. Me salvou de uma correção de bug muito cara no futuro.
Ethan
3
re - "nunca use ECB em sistemas fortes obviamente": Estou apenas experimentando isso para minha própria diversão. mas eu vi o comentário acima em seu código. para aqueles de nós com experiência mínima de segurança / criptografia / teoria da informação, por que o "nunca usar"? talvez precise de outra pergunta ... ou talvez haja um link nele.
Trevor Boyd Smith
68

Python não tem esquemas de criptografia embutidos, não. Você também deve levar a sério o armazenamento de dados criptografados; esquemas de criptografia triviais que um desenvolvedor entende como inseguros e um esquema de brinquedo pode muito bem ser confundido com um esquema seguro por um desenvolvedor menos experiente. Se você criptografar, criptografe corretamente.

No entanto, você não precisa fazer muito trabalho para implementar um esquema de criptografia adequado. Em primeiro lugar, não reinvente a roda da criptografia , use uma biblioteca de criptografia confiável para lidar com isso para você. Para Python 3, essa biblioteca confiável écryptography .

Eu também recomendo que a criptografia e descriptografia se apliquem a bytes ; codifique as mensagens de texto em bytes primeiro; stringvalue.encode()codifica para UTF8, facilmente revertido novamente usando bytesvalue.decode().

Por último, mas não menos importante, ao criptografar e descriptografar, falamos sobre chaves , não senhas. Uma chave não deve ser memorável por humanos, é algo que você armazena em um local secreto, mas legível por máquina, ao passo que uma senha geralmente pode ser lida e memorizada por humanos. Você pode derivar uma chave de uma senha, com um pouco de cuidado.

Mas para um aplicativo da web ou processo em execução em um cluster sem atenção humana para mantê-lo em execução, você deseja usar uma chave. As senhas são para quando apenas um usuário final precisa de acesso às informações específicas. Mesmo assim, você geralmente protege o aplicativo com uma senha e, em seguida, troca informações criptografadas usando uma chave, talvez uma anexada à conta do usuário.

Criptografia de chave simétrica

Fernet - AES CBC + HMAC, fortemente recomendado

A cryptographybiblioteca inclui a receita Fernet , uma receita de práticas recomendadas para usar criptografia. Fernet é um padrão aberto , com implementações prontas em uma ampla gama de linguagens de programação e pacotes de criptografia AES CBC para você com informações de versão, um carimbo de data / hora e uma assinatura HMAC para evitar adulteração de mensagens.

Fernet torna muito fácil criptografar e descriptografar mensagens e mantê-lo seguro. É o método ideal para criptografar dados com um segredo.

Eu recomendo que você use Fernet.generate_key() para gerar uma chave segura. Você também pode usar uma senha (próxima seção), mas uma chave secreta completa de 32 bytes (16 bytes para criptografar, mais outros 16 para a assinatura) será mais segura do que a maioria das senhas que você possa imaginar.

A chave que o Fernet gera é um bytesobjeto com URL e caracteres de base64 de arquivo seguro, portanto, imprimível:

from cryptography.fernet import Fernet

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

Para criptografar ou descriptografar mensagens, crie uma Fernet()instância com a chave fornecida e chame Fernet.encrypt()ou Fernet.decrypt(), a mensagem de texto simples a ser criptografada e o token criptografado são bytesobjetos.

encrypt()e as decrypt()funções seriam semelhantes a:

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)

Demo:

>>> 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 com senha - chave derivada de senha, enfraquece um pouco a segurança

Você pode usar uma senha em vez de uma chave secreta, desde que use um método de derivação de chave forte . Em seguida, você deve incluir o salt e a contagem de iteração HMAC na mensagem, para que o valor criptografado não seja mais compatível com Fernet sem primeiro separar salt, count e token 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)

Demo:

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

Incluir o salt na saída torna possível usar um valor salt aleatório, o que, por sua vez, garante que a saída criptografada seja totalmente aleatória, independentemente da reutilização da senha ou da repetição da mensagem. Incluir a contagem de iterações garante que você possa ajustar os aumentos de desempenho da CPU com o tempo, sem perder a capacidade de descriptografar mensagens mais antigas.

Uma senha sozinha pode ser tão segura quanto uma chave aleatória Fernet de 32 bytes, desde que você gere uma senha aleatória apropriada de um pool de tamanho semelhante. 32 bytes fornece 256 ^ 32 números de chaves, portanto, se você usar um alfabeto de 74 caracteres (26 superior, 26 inferior, 10 dígitos e 12 símbolos possíveis), sua senha deve ter pelo menos math.ceil(math.log(256 ** 32, 74))== 42 caracteres. No entanto, um número maior e bem selecionado de iterações HMAC pode mitigar um pouco a falta de entropia, pois isso torna muito mais caro para um invasor entrar com força bruta.

Saiba que escolher uma senha mais curta, mas ainda razoavelmente segura, não prejudicará esse esquema, apenas reduzirá o número de valores possíveis que um invasor de força bruta teria que pesquisar; certifique-se de escolher uma senha forte o suficiente para seus requisitos de segurança .

Alternativas

Obscurecedor

Uma alternativa é não criptografar . Não fique tentado a usar apenas uma cifra de baixa segurança ou uma implementação caseira do, digamos Vignere. Não há segurança nessas abordagens, mas podem dar a um desenvolvedor inexperiente que recebe a tarefa de manter seu código no futuro a ilusão de segurança, que é pior do que nenhuma segurança.

Se tudo o que você precisa é obscuridade, apenas base64 os dados; para requisitos seguros de URL, a base64.urlsafe_b64encode()função é adequada. Não use uma senha aqui, apenas codifique e pronto. No máximo, adicione alguma compressão (como 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))

Isso se transforma b'Hello world!'em b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Integridade apenas

Se tudo o que você precisa é uma maneira de se certificar de que os dados podem ser confiáveis ​​e inalterados após terem sido enviados a um cliente não confiável e recebidos de volta, então você deseja assinar os dados, você pode usar a hmacbiblioteca para isso com SHA1 (ainda considerado seguro para assinatura HMAC ) ou melhor:

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)

Use-o para assinar dados e, em seguida, anexe a assinatura aos dados e envie-a ao cliente. Ao receber os dados de volta, divida os dados e a assinatura e verifique. Eu defini o algoritmo padrão para SHA256, então você precisará de uma chave de 32 bytes:

key = secrets.token_bytes(32)

Você pode querer dar uma olhada na itsdangerousbiblioteca , que empacota tudo isso com serialização e desserialização em vários formatos.

Usando criptografia AES-GCM para fornecer criptografia e integridade

Fernet baseia-se no AEC-CBC com uma assinatura HMAC para garantir a integridade dos dados criptografados; um invasor mal-intencionado não pode alimentar seu sistema com dados absurdos para manter seu serviço ocupado em execução em círculos com entrada incorreta, porque o texto cifrado está assinado.

A cifra de bloco do modo Galois / Contador produz texto cifrado e uma etiqueta para servir ao mesmo propósito, portanto, pode ser usada para os mesmos fins. A desvantagem é que, ao contrário do Fernet, não existe uma receita única e fácil de usar para reutilizar em outras plataformas. O AES-GCM também não usa preenchimento, portanto, esse texto cifrado de criptografia corresponde ao comprimento da mensagem de entrada (enquanto o Fernet / AES-CBC criptografa as mensagens em blocos de comprimento fixo, obscurecendo um pouco o comprimento da mensagem).

AES256-GCM usa o segredo usual de 32 bytes como uma chave:

key = secrets.token_bytes(32)

então use

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

Incluí um carimbo de data / hora para oferecer suporte aos mesmos casos de uso de tempo de vida que o Fernet oferece.

Outras abordagens nesta página, em Python 3

AES CFB - como CBC, mas sem a necessidade de preenchimento

Esta é a abordagem que All Іѕ Vаиітy segue, embora incorretamente. Esta é a cryptographyversão, mas observe que incluo o IV no texto cifrado , ele não deve ser armazenado como um global (reutilizar um IV enfraquece a segurança da chave e armazená-lo como um módulo global significa que ele será regenerado a próxima invocação do Python, tornando todo o texto cifrado indecifrável):

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

Isso não tem a proteção adicional de uma assinatura HMAC e não há carimbo de data / hora; você mesmo teria que adicioná-los.

O texto acima também ilustra como é fácil combinar blocos de construção básicos de criptografia incorretamente; O manuseio incorreto do valor IV de All Іѕ Vаиітy pode levar a uma violação de dados ou a ilegibilidade de todas as mensagens criptografadas porque o IV foi perdido. Em vez disso, usar o Fernet protege você de tais erros.

AES ECB - não seguro

Se você implementou a criptografia AES ECB anteriormente e ainda precisa suportá-la no Python 3, você ainda pode fazer isso com cryptographytambém. As mesmas ressalvas se aplicam, o ECB não é seguro o suficiente para aplicativos da vida real . Reimplementando essa resposta para Python 3, adicionando tratamento automático de preenchimento:

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

Novamente, falta a assinatura HMAC e você não deve usar o ECB de qualquer maneira. O texto acima existe apenas para ilustrar que cryptographypode lidar com os blocos de construção criptográficos comuns, mesmo aqueles que você não deveria realmente usar.

Martijn Pieters
fonte
51

O "encoded_c" mencionado na resposta da cifra Vigenere de @smehmood deve ser "key_c".

Aqui estão as funções de codificação / decodificação de trabalho.

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)

Isenção de responsabilidade: conforme implícito nos comentários, isso não deve ser usado para proteger dados em um aplicativo real, a menos que você leia isto e não se importe em falar com advogados:

O que há de errado com a criptografia XOR?

qneill
fonte
2
Muito útil, obrigado. Eu postei uma versão do Python 3 abaixo (parecia feia nos comentários)
Ryan Barrett
1
"Lei de Schneier" : Qualquer um, do amador mais desinformado ao melhor criptógrafo, pode criar um algoritmo que ele mesmo não consegue quebrar. Não use isso, não está nem perto de ser seguro.
zaph
3
Ótimo! Esta versão também funciona para strings com acentos, enquanto a versão de @smehmood não. Exemplo de uso: encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)funciona bem.
Basj
2
Aqui está um plugin de texto Sublime baseado neste código, permitindo fácil codificação / decodificação de texto com CTRL + SHIFT + P e então "Eeencode" ou "Dddecode".
Basj
2
@basj Obrigado pelo exemplo
sk03
49

Aqui está uma versão Python 3 das funções de 's @qneill resposta :

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)

A codificação / decodificação extras são necessárias porque o Python 3 dividiu strings / matrizes de bytes em dois conceitos diferentes e atualizou suas APIs para refletir isso.

Ryan Barrett
fonte
4
Obrigado Ryan, fwiw você digitou @qniell
qneill
3
Para quem está se perguntando, as diferenças são as .encode()).decode(). no retorno de encode()e .decode()na segunda linha em decode().
RolfBly
2
Hmm, o código codificado não é realmente exclusivo, eu executei um teste e ele mostrou cada chave de código de 11,22,33,44, ..., 88,99,111,222, ... sempre tem outro código igual ao de antes. Mas eu agradeço
Hzzkygcs
1
@Ryan Barrett, é possível verificar a exatidão da senha durante a decodificação. Digamos que eu envie a alguém uma string codificada que conhece a chave, e se ele digitar a chave com um erro de digitação? A decodificação ainda dá a ele uma string "decodificada", mas não é a certa, como ele pode saber?
Heinz
26

Isenção de responsabilidade: conforme mencionado nos comentários, isso não deve ser usado para proteger dados em um aplicativo real.

O que há de errado com a criptografia XOR?

/crypto/56281/breaking-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


Como foi mencionado, a biblioteca PyCrypto contém um conjunto de cifras. A "cifra" XOR pode ser usada para fazer o trabalho sujo se você não quiser fazer isso sozinho:

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

A cifra funciona da seguinte maneira, sem ter que preencher o texto simples:

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

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

Crédito para https://stackoverflow.com/a/2490376/241294 pelas funções de codificação / decodificação de base64 (sou um novato em Python).

poida
fonte
nota: o módulo Crypto é instalado em python3 por pycrptop instalado, não Crypto. sudo pip3 install pycrypto.
Nikhil VJ
2
nota: pycrypto falhou ao instalar no herokuapp no ​​meu lado. Eu encontrei esta postagem .. parece dizer que o pacote pycrypto foi substituído por outro chamado pycryptodome insteal, e que o método XOR foi descontinuado: github.com/digitalocean/netbox/issues/1527
Nikhil VJ
2
Nunca use este método , observe a descrição desta 'cifra' na documentação : Cifra de brinquedo XOR, XOR é uma das cifras de fluxo mais simples. A criptografia e a descriptografia são executadas por dados XOR-ing com um fluxo de chave feito por contatenating a chave. Não o use para aplicações reais! .
Martijn Pieters
@MartijnPieters você está certo. Espero que minha edição tenha deixado esse ponto claro.
poida
12

Aqui está uma implementação de criptografia e descriptografia segura de URL usando AES (PyCrypto) e 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))

Se você enfrentar algum problema como este https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode), use str(cipher)durante a decodificação da seguinte forma:

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

Teste:

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
All Іѕ Vаиітy
fonte
Bug com Windows x 64 + 3,6 + Python PyCryptodome (como PyCrypto é preterido): TypeError: Object type <class 'str'> cannot be passed to C code.
Basj
@Basj aww desculpe .. Eu não uso windows, então não posso consertar.
Tudo desde
Não gere o IV e armazene-o no nível do módulo, você precisa incluir o IV na mensagem de texto cifrado retornada! Agora você introduziu dois problemas: reiniciar o processo Python fornece um novo IV, tornando impossível descriptografar mensagens criptografadas anteriormente, e, enquanto isso, você está reutilizando o IV para várias mensagens, o que efetivamente reduz a segurança de volta ao nível de ECB.
Martijn Pieters
1
@ AllІѕVаиітy Resolvido com b'...', editei a resposta para referência futura!
Basj
8

Trabalhar funções de codificação / decodificação em python3 (adaptado muito pouco da resposta 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)
cristão
fonte
8

Obrigado por algumas ótimas respostas. Nada original a acrescentar, mas aqui estão algumas reescritas progressivas da resposta de qneill usando alguns recursos úteis do Python. Espero que você concorde que eles simplificam e esclarecem o código.

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()- emparelhe os itens em uma lista com seu índice

iterar sobre os caracteres em uma string

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)

construir listas usando uma compreensão de lista

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)

Freqüentemente, em Python, não há necessidade de índices de lista. Elimine variáveis ​​de índice de loop inteiramente usando zip e ciclo:

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)

e alguns testes ...

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
usuario
fonte
Muito bom @Nick, boa progressão de pitonismos e testes para inicializar. Para dar o devido crédito, eu estava corrigindo um bug na resposta original de Smehmood stackoverflow.com/a/2490718/468252 .
qneill
4

Se você quiser estar seguro, pode usar o Fernet, que é criptograficamente correto. Você pode usar um "salt" estático se não quiser armazená-lo separadamente - você só perderá o dicionário e a prevenção de ataques de arco-íris. Escolhi porque posso escolher senhas longas ou curtas´, o que não é tão fácil com 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

Se isso for muito complicado, alguém sugeriu criptografar simples

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
HCLivess
fonte
Basta gerar um salt e incluí-lo no resultado da criptografia para que senhas e mensagens repetidas ainda resultem em uma saída aleatória. Inclua o valor das iterações também para tornar o algoritmo à prova de futuro, mas ainda ser capaz de descriptografar mensagens usando uma contagem de iteração diferente.
Martijn Pieters
3

Quem quer que tenha vindo aqui (e o bountier) parecia estar procurando por uma linha sem muita configuração, que outras respostas não fornecem. Portanto, estou apresentando base64.

Agora, lembre-se de que isso é apenas ofuscação básica e está em ** NENHUM CAMINHO OK PARA A SEGURANÇA ** , mas aqui estão algumas frases simples :

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'

Algumas coisas a serem observadas:

  • você vai querer lidar com mais / menos codificação / decodificação byte-para-string por conta própria, dependendo de sua E / S. Olhe para dentro bytes()ebytes::decode()
  • base64 é facilmente reconhecível pelos tipos de caracteres usados ​​e geralmente termina com =caracteres. Pessoas como eu absolutamente os decodificam no console javascript quando os vemos em sites. É tão fácil quanto btoa(string)(js)
  • a ordem é chave + dados, como em b64, quais caracteres aparecem no final depende de quais caracteres estão no início (por causa dos deslocamentos de byte. A Wikipedia tem algumas explicações interessantes). Neste cenário, o início da string codificada será o mesmo para tudo codificado com aquela chave. A vantagem é que os dados ficarão mais ofuscados. Fazer o contrário resultará na parte dos dados exatamente igual para todos, independentemente da chave.

Agora, se o que você queria nem precisava de uma chave de qualquer tipo, mas apenas um pouco de ofuscação, você pode mais uma vez apenas usar base64, sem nenhum tipo de chave:

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'
reboque
fonte
2
Sim, se você não se preocupa com segurança, então base64 é muito melhor do que criptografar.
Martijn Pieters
Sobre as outras respostas não serem pontuais: esse não é o ponto da questão. Eles pedem duas funções para chamar. E Fernet(key).encrypt(message)é apenas uma expressão como sua chamada de base64.
Martijn Pieters
E você deve remover o keycompletamente. Cargas de desenvolvedores estão indo para copiar e colar de estouro de pilha sem prestar atenção e vai assumir a chave para ser secreta. Se você deve incluí-lo, pelo menos não o use e avise ou gere uma exceção se usado de qualquer maneira. Não subestime a tolice da cultura de copiar e colar e suas responsabilidades de fornecer funções saudáveis.
Martijn Pieters
3

Vou dar 4 soluções:

1) Usando criptografia Fernet com cryptographybiblioteca

Aqui está uma solução usando o pacote cryptography, que você pode instalar normalmente com 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')

Você pode adaptar com seu próprio salt, contagem de iteração, etc. Este código não está muito longe da resposta de @HCLivess, mas o objetivo é ter funções encrypte prontas para uso decrypt. Fonte: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Nota: use .encode()e .decode()todo lugar se quiser strings em 'John Doe'vez de bytes como b'John Doe'.


2) Criptografia AES simples com Cryptobiblioteca

Isso funciona com 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

Observação: você pode remover base64.b64encodee .b64decodese não quiser saída legível em texto e / ou se quiser salvar o texto cifrado no disco como um arquivo binário de qualquer maneira.


3) AES usando uma função de derivação de chave de senha melhor e a capacidade de testar se "senha errada inserida", com Cryptobiblioteca

A solução 2) com AES "modo CFB" está ok, mas tem duas desvantagens: o fato de que SHA256(password)pode ser facilmente forçado com uma tabela de consulta e que não há como testar se uma senha errada foi inserida. Isso é resolvido aqui pelo uso de AES no "modo GCM", conforme discutido em AES: como detectar que uma senha incorreta foi inserida? e Este método diz “A senha que você digitou está errada” é seguro? :

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) Usando RC4 (nenhuma biblioteca necessária)

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

(Desatualizado desde as últimas edições, mas mantido para referência futura): Tive problemas ao usar Windows + Python 3.6 + todas as respostas envolvendo pycrypto(não consigo pip install pycryptono Windows) ou pycryptodome(as respostas aqui com from Crypto.Cipher import XORfalhou porque XORnão é suportado por este pycryptofork; e as soluções usando ... AEStambém falharam com TypeError: Object type <class 'str'> cannot be passed to C code). Além disso, a biblioteca simple-crypttem uma pycryptodependência, portanto não é uma opção.

Basj
fonte
1
Você não deseja codificar as contagens de sal e iteração; gere um salt aleatório e torne a contagem de iteração configurável na criptografia, inclua essa informação no resultado da criptografia e extraia e use os valores na descriptografia. O salt protege você do reconhecimento trivial de senhas reutilizadas em uma determinada mensagem, a contagem de iterações torna o algoritmo à prova de futuro.
Martijn Pieters
Sim, claro @MartijnPieters, mas o objetivo aqui é ter um código simples para fins simples, conforme solicitado pelo OP, com dois parâmetros : texto simples + senha. Obviamente, para cenários mais complexos (por exemplo, um banco de dados), você usará todos esses parâmetros adicionais.
Basj
Não há necessidade de parâmetros adicionais! Incluo essa informação codificada no valor de retorno opaco de password_encrypt().
Martijn Pieters
@MartijnPieters Ótima solução, de fato.
Basj
2

Isso funciona, mas o comprimento da senha deve ser exatamente igual 8. Isso é simples e requer 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

RESULTADO:

³.\Þ\åS¾+æÅ`;Ê
John Doe
Pratik Deoghare
fonte
Não use um IV fixo! Randomize o IV e inclua-o com o texto cifrado. Caso contrário, você também pode usar o modo ECB; mensagens de texto simples repetidas são triviais de reconhecer.
Martijn Pieters
Além disso, o projeto pyDes parece estar morto; a página inicial se foi, e o último lançamento no PyPI agora tem 9 anos.
Martijn Pieters
2

Uma outra implementação do código @qneill que inclui a soma de verificação CRC da mensagem original, ela lança uma exceção se a verificação falhar:

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
ahmed
fonte
2

Você pode usar AES para criptografar sua string com uma senha. Porém, você vai querer escolher uma senha forte o suficiente para que as pessoas não consigam adivinhá-la facilmente (desculpe, não posso evitar. Sou um aspirante a gênio da segurança).

O AES é forte com um bom tamanho de chave, mas também é fácil de usar com o PyCrypto.

Alan
fonte
3
Obrigado Alan. Mas, para esclarecimento, não estou criptografando as próprias senhas. No exemplo acima, estou criptografando a string "John Doe" de acordo com a senha "mypass", que é uma senha simples que uso em meu código-fonte. As senhas dos usuários não estão envolvidas, nem qualquer outra informação muito sensível. Eu editei minha pergunta para esclarecer isso.
RexE
1
O AES é ótimo, se usado corretamente. No entanto, é fácil usá-lo incorretamente; há pelo menos uma resposta aqui que usa um modo de cifra de bloco inseguro, outras duas que se atrapalham ao lidar com o valor IV. Melhor usar uma boa biblioteca com uma receita bem definida como o Fernet!
Martijn Pieters
Na verdade, essa é uma observação muito astuta. Eu tentei o IV uma vez.
Alan
0

Bibliotecas externas fornecem algoritmos de criptografia de chave secreta.

Por exemplo, o Cyphermódulo em PyCrypto oferece uma seleção de muitos algoritmos de criptografia:

  • 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 é um Pythonwrapper para OpenSSL e fornece (entre outras funções) uma biblioteca de criptografia de propósito geral de força total. Estão incluídas cifras simétricas (como AES).

gimel
fonte
0

se você deseja criptografia segura:

para python 2, você deve usar keyczar http://www.keyczar.org/

para python 3, até que o keyczar esteja disponível, escrevi simple-crypt http://pypi.python.org/pypi/simple-crypt

ambos usarão reforço de chave, o que os torna mais seguros do que a maioria das outras respostas aqui. e como eles são tão fáceis de usar, você pode querer usá-los mesmo quando a segurança não for crítica ...

Andrew Cooke
fonte
Do repositório Keyczar : Nota importante: Keyczar está obsoleto. Os desenvolvedores do Keyczar recomendam o Tink , mas não existe uma versão em Python do Tink.
Martijn Pieters
0

Portanto, como nada de missão crítica está sendo codificado , você apenas deseja criptografar para ofuscação .

Deixe-me apresentar a cifra do céser

insira a descrição da imagem aqui

Cifra de César ou mudança de César, é uma das técnicas de criptografia mais simples e conhecidas. É um tipo de cifra de substituição em que cada letra do texto simples é substituída por uma letra com um número fixo de posições abaixo do alfabeto. Por exemplo, com um deslocamento à esquerda de 3, D seria substituído por A, E se tornaria B e assim por diante.

Código de amostra para sua referência:

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

Vantagens: atende às suas necessidades e é simples e faz toda a codificação.

Desvantagem: pode ser quebrada por algoritmos de força bruta simples (altamente improvável que alguém tentasse passar por todos os resultados extras).

Yash Mishra
fonte
0

Adicionar mais um código com decodificar e codificar para referência

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()
Sriram Suruliandi
fonte