Simple way to encode a string according to a password?

匿名 (未验证) 提交于 2019-12-03 01:25:01

问题:

Does Python have a built-in, simple way of encoding/decoding strings using a password?

Something like this:

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

So the string "John Doe" gets encrypted as 'sjkl28cn2sx0'. To get the original string, I would "unlock" that string with the key 'mypass', which is a password in my source code. I'd like this to be the way I can encrypt/decrypt a Word document with a password.

I would like to use these encrypted strings as URL parameters. My goal is obfuscation, not strong security; nothing mission critical is being encoded. I realize I could use a database table to store keys and values, but am trying to be minimalist.

回答1:

Assuming you are only looking for simple obfuscation that will obscure things from the very casual observer, and you aren't looking to use third party libraries. I'd recommend something like the Vigenere cipher. It is one of the strongest of the simple ancient ciphers.

Vigenère cipher

It's quick and easy to implement. Something like:

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 is pretty much the same, except you subtract the key.

It is much harder to break if the strings you are encoding are short, and/or if it is hard to guess the length of the passphrase used.

If you are looking for something cryptographic, PyCrypto is probably your best bet, though previous answers overlook some details: ECB mode in PyCrypto requires your message to be a multiple of 16 characters in length. So, you must pad. Also, if you want to use them as URL parameters, use base64.urlsafe_b64_encode(), rather than the standard one. This replaces a few of the characters in the base64 alphabet with URL-safe characters (as it's name suggests).

However, you should be ABSOLUTELY certain that this very thin layer of obfuscation suffices for your needs before using this. The Wikipedia article I linked to provides detailed instructions for breaking the cipher, so anyone with a moderate amount of determination could easily break it.



回答2:

As you explicitly state that you want obscurity not security, we'll avoid reprimanding you for the weakness of what you suggest :)

So, using 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() 

If someone gets a hold of your database and your code base, they will be able to decode the encrypted data. Keep your secret_key safe!



回答3:

The "encoded_c" mentioned in the @smehmood's Vigenere cipher answer should be "key_c".

Here are working encode/decode functions.

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) 


回答4:

Here's a Python 3 version of the functions from @qneill 's answer:

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) 

The extra encode/decodes are needed because Python 3 has split strings/byte arrays into two different concepts, and updated their APIs to reflect that..



回答5:

As has been mentioned the PyCrypto library contains a suite of ciphers. The XOR cipher can be used to do the dirty work if you don't want to do it yourself:

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

Even though it only provides minimal security I'd still recommend using a random looking key without any space characters (as XOR'ing an ASCII [a-zA-Z] character with a space just flips the case).

The cipher works as follows without having to pad the plaintext:

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

Credit to https://stackoverflow.com/a/2490376/241294 for the base64 encode/decode functions (I'm a python newbie).



回答6:

Here's an implementation of URL Safe encryption and Decryption using AES(PyCrypto) and base64.



回答7:

Working encode/decode functions in python3 (adapted very little from qneill's answer):

def encode(key, clear):     enc = []     for i in range(len(clear)):         key_c = key[i % len(key)]         enc_c = (ord(clear[i]) + ord(key_c)) % 256         enc.append(enc_c)     return base64.urlsafe_b64encode(bytes(enc))  def decode(key, enc):     dec = []     enc = base64.urlsafe_b64decode(enc)     for i in range(len(enc)):         key_c = key[i % len(key)]         dec_c = chr((256 + enc[i] - ord(key_c)) % 256)         dec.append(dec_c)     return "".join(dec) 


回答8:

Simple way is using the library, and PyCrypto is the good one.



回答9:

This works but password length should be exactly 8. This is simple and requires 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 

OUTPUT:



回答10:

Thanks for some great answers. Nothing original to add, but here are some progressive rewrites of qneill's answer using some useful Python facilities. I hope you agree they simplify and clarify the 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()-- pair the items in a list with their index

iterate over the characters in a 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) 

build lists using a list comprehension

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) 

Often in Python there's no need for list indexes at all. Eliminate loop index variables entirely using zip and 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) 

and some 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 


回答11:

External libraries provide secret-key encryption algorithms.

For example, the Cypher module in PyCrypto offers a selection of many encryption algorithms:

  • 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 is a Python wrapper for OpenSSL, and provides (among other functions) a full-strength general purpose cryptography library. Included are symmetric ciphers (like AES).



回答12:

if you want secure encryption:

for python 2, you should use keyczar http://www.keyczar.org/

for python 3, until keyczar is available, i have written simple-crypt http://pypi.python.org/pypi/simple-crypt

both these will use key strengthening which makes them more secure than most other answers here. and since they're so easy to use you might want to use them even when security is not critical...



回答13:

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 

If that's too complicated, someone suggested simplecrypt

from simplecrypt import encrypt, decrypt ciphertext = encrypt('password', plaintext) plaintext = decrypt('password', ciphertext) 


回答14:

You can use AES to encrypt your string with a password. Though, you'll want to chose a strong enough password so people can't easily guess what it is (sorry I can't help it. I'm a wannabe security weenie).

AES is strong with a good key size, but it's also easy to use with PyCrypto.



回答15:

An other implementation of @qneill code which include CRC checksum of the original message, it throw an exception if the check fail:

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 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!