Простий спосіб кодування рядка відповідно до пароля?


123

Чи має Python вбудований простий спосіб кодування / декодування рядків за допомогою пароля?

Щось на зразок цього:

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

Отже рядок "John Doe" шифрується як "sjkl28cn2sx0". Щоб отримати початковий рядок, я б "розблокував" цю рядок ключем "mypass", який є паролем у моєму вихідному коді. Я хотів би, щоб це було способом шифрування / розшифрування Word-документа з паролем.

Я б хотів використовувати ці зашифровані рядки як параметри URL-адреси. Моя мета - затуплення, а не сильна безпека; жодна критична місія не кодується. Я розумію, що міг би використовувати таблицю бази даних для зберігання ключів та значень, але намагаюся бути мінімалістичним.


28
Термін "пароль" тут недоречний. Ви використовуєте це як криптографічний КЛЮЧ, і вам слід використовувати цю термінологію, щоб уникнути плутанини у ваших запитаннях, а також будь-яких документах, коментарях, специфікаціях, тестових планах тощо
Джим Денніс

2
"Я хотів би, щоб це було способом шифрування / розшифрування документу Word з паролем.", У Word вже є вбудована опція для шифрування ваших документів, якщо вам просто потрібно зашифрувати текстові документи.
Байрон Філер

2
Цікаво, що згідно з даним дослідженням про такі підводні камені для зберігання паролів , розробники, які використовують Stack Overflow, як правило, створюють менш захищений код. Гей, мені цікаво чому?
ліс

Крім того , слід прочитати цю відповідь від security.SE
kelalaka

Не просто реалізувати кодування / декодування просто
luckyging3r

Відповіді:


70

Якщо припустити, що ви шукаєте лише просту затьмарення, яка затьмарить речі від дуже випадкового спостерігача, і ви не хочете використовувати сторонні бібліотеки. Я б рекомендував щось на зразок шифру Vigenere. Це одна з найсильніших з простих древніх шифрів.

Шифр Віньєра

Це швидко та легко здійснити. Щось на зразок:

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)

Розшифровка майже однакова, за винятком того, що ви віднімаєте ключ.

Набагато складніше розірвати, якщо рядки, які ви кодуєте, короткі, та / або якщо важко відгадати довжину використовуваної парольної фрази.

Якщо ви шукаєте щось криптографічне, PyCrypto - це, мабуть, найкраща ставка, хоча попередні відповіді ігнорують деякі деталі: режим ECB в PyCrypto вимагає, щоб ваше повідомлення було кратним 16 символів. Отже, ви повинні прокладати. Крім того, якщо ви хочете використовувати їх як параметри URL-адреси, використовуйте base64.urlsafe_b64_encode(), а не стандартний. Це замінює кілька символів алфавіту base64 на захищені URL-адресами символи (як випливає з назви).

Однак ви повинні бути АБСОЛЮТНО впевнені, що цей дуже тонкий шар затухання достатній для ваших потреб перед його використанням. Стаття у Вікіпедії, до якої я посилався, містить детальні вказівки щодо розбиття шифру, тому кожен, хто має помірковану рішучість, може легко його зламати.


6
Я виправив сценарій smehmood і додав функцію декодування gist.github.com/ilogik/6f9431e4588015ecb194
Адріан Местер

3
Увага! код smehmood та виправлення Адріана Местера працюють лише для рядків із символами нижнього діапазону ascii! Дивіться перевагу оператора%, введення unicode тощо. Див. Відповідь qneill на робочий код
le_m

2
@smehmood Я отримую таку помилку'str' object cannot be interpreted as an integer
Rohit Khatri

3
"для i in xrange (string)" може знадобитися перейти на "for i in xrange (len (string))"
користувач3113626

2
кодер і декодер для Python 2 і 3: gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a
іману

71

Оскільки ви прямо заявляєте, що хочете невідомості, а не безпеки, ми уникатимемо догани вам за слабкість запропонованого вами :)

Отже, використовуючи 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)

Якщо хтось захопить вашу базу даних та вашу кодову базу, він зможе розшифрувати зашифровані дані. Бережіть secret_key!


3
Я не думаю, що це буде працювати, якщо msg_text не буде кратним 16 байтам, оскільки для шифрування AES потрібні блоки, кратні 16. Робочій реалізації для msg_text довільної довжини потрібно буде додати прокладку до рядка, щоб отримати її кратну довжині 16.
tohster

6
Приклад з padding: paste.ubuntu.com/11024555 Він працює з довільною паролем та довжиною повідомлення.
iman

3
@Якщо ні, ця особлива encryptфункція є надзвичайною dlitz.net/software/pycrypto/api/current/…, тому не слід намагатися повторно використовувати її.
Буде чи

1
@Will +1 Дякуємо за інформацію та посилання. Врятувало мене від дуже дорогого виправлення помилок у майбутньому.
Етан

3
повторно "ніколи не використовуй ЕЦБ у сильних системах очевидно": я просто експериментую з цим для власної забави. але я побачив вищезазначений коментар у вашому коді. для тих із нас, хто має мінімальний рівень безпеки / шифрування / теорії інформації, чому "ніколи не використовувати"? можливо, потрібне ще одне запитання ... а може на це є посилання.
Тревор Бойд Сміт

69

У Python немає вбудованих схем шифрування, немає. Ви також повинні серйозно поставитися до зберігання зашифрованих даних; тривіальні схеми шифрування, які один розробник вважає небезпечними, а схема іграшок може бути помилково прийнята за безпечну схему менш досвідченим розробником. Якщо ви шифруєте, шифруйте належним чином.

Для впровадження правильної схеми шифрування не потрібно робити багато роботи. Перш за все, не вигадуйте колесо криптографії , використовуйте довірену бібліотеку криптовалют, щоб впоратися з цим. Для Python 3 ця надійна бібліотека є cryptography.

Я також рекомендую, щоб шифрування та дешифрування стосувалися байтів ; кодувати текстові повідомлення спочатку в байти; stringvalue.encode()кодує до UTF8, легко повертається знову за допомогою bytesvalue.decode().

Не в останню чергу, при шифруванні та розшифровці ми говоримо про ключі , а не паролі. Ключ не повинен бути запам'ятовуваним людиною, це щось, що ви зберігаєте в секретному місці, але машиночитабельне, тоді як пароль часто може бути прочитаний і запам'ятований людиною. Ви можете отримати ключ із пароля, дотримуючись певної обережності.

Але для того, щоб веб-додаток або процес, запущений у кластері без уваги людини, продовжував працювати, потрібно використовувати ключ. Паролі призначені для того, коли лише кінцевому користувачеві потрібен доступ до певної інформації. Навіть тоді ви зазвичай захищаєте програму паролем, а потім обмінюєтесь зашифрованою інформацією за допомогою ключа, можливо, такого, який додається до облікового запису користувача.

Симетричне шифрування ключа

Фернет - AES CBC + HMAC, настійно рекомендується

cryptographyБібліотека включає рецепт Fernet , рецепт передових методів для використання криптографії. Фернет є це відкритий стандарт із готовими реалізаціями в широкому діапазоні мов програмування, і він пакує AES CBC-шифрування для вас інформацією про версію, часовою позначкою та підписом HMAC для запобігання підробці повідомлень.

Fernet дозволяє дуже легко шифрувати та розшифровувати повідомлення та захищати вас. Це ідеальний метод для шифрування даних секретом.

Я рекомендую використовувати Fernet.generate_key()для створення захищеного ключа. Ви також можете використовувати пароль (наступний розділ), але повний секретний ключ з 32 байтами (16 байт для шифрування, плюс 16 для підпису) буде більш безпечним, ніж більшість паролів, про які ви могли придумати.

Ключовим фактором, який створює Fernet, є bytesоб’єкт з URL-адресами та безпечними файлами base64 символів, тому друкований:

from cryptography.fernet import Fernet

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

Щоб зашифрувати або розшифрувати повідомлення, створіть Fernet()екземпляр за допомогою даного ключа та зателефонуйте до Fernet.encrypt()або Fernet.decrypt(), як повідомлення прямого тексту для шифрування, так і зашифрований маркер - bytesоб'єкти.

encrypt()і decrypt()функції виглядатимуть так:

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)

Демонстрація:

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

Фернет з паролем - ключ, отриманий із пароля, дещо послаблює захист

Ви можете використовувати пароль замість секретного ключа за умови використання сильного методу виведення ключа . Тоді вам доведеться включати в повідомлення сіль та кількість ітерацій HMAC, тому зашифроване значення більше не сумісне з Фернетом без попереднього розділення солі, підрахунку та маркера 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)

Демонстрація:

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

Включення солі у висновок дає можливість використовувати випадкове значення солі, що, в свою чергу, гарантує, що зашифрований вихід гарантовано є повністю випадковим, незалежно від повторного використання пароля чи повторення повідомлення. У тому числі кількість ітерацій гарантує, що ви можете з часом налаштувати продуктивність процесора, не втрачаючи можливості розшифровувати старі повідомлення.

Сам пароль може бути таким же безпечним, як і 32-байтовий випадковий ключ Fernet, за умови, що ви генеруєте належним чином випадковий пароль із пулу аналогічних розмірів. 32 байти дає 256 ^ 32 кількість клавіш, тому якщо ви використовуєте алфавіт із 74 символів (26 верхніх, 26 нижніх, 10 цифр та 12 можливих символів), то ваш пароль повинен бути не менше math.ceil(math.log(256 ** 32, 74))== 42 символів. Однак, a вдало підібрана більша кількість ітерацій HMAC може дещо пом’якшити відсутність ентропії, оскільки це набагато дорожче для зловмисника змусити пробитися.

Просто знайте, що вибір коротшого, але все-таки досить безпечного пароля не призведе до каліцтва цієї схеми, це просто зменшує кількість можливих значень, через які зловмисникові доведеться шукати; не забудьте вибрати досить надійний пароль для ваших вимог безпеки .

Альтернативи

Затемнення

Альтернативою є не шифрування . Не спокушайтеся просто використовувати шифр із низьким рівнем захисту або реалізацію домашньої розкрутки, скажімо Віньєре. У цих підходах немає ніякої безпеки, але це може надати недосвідченому розробнику, якому дано завдання підтримувати ваш код у майбутньому ілюзією безпеки, що гірше, ніж взагалі ніякої безпеки.

Якщо все, що вам потрібно, - це неясність, просто зафіксуйте дані64; для вимог щодо безпеки URL-адреси base64.urlsafe_b64encode()функція прекрасна. Не використовуйте тут пароль, просто кодуйте і все закінчено. Максимум, додайте стиснення (як 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))

Це перетворюється b'Hello world!'на b'eNrzSM3JyVcozy_KSVEEAB0JBF4='.

Тільки цілісність

Якщо все, що вам потрібно, це спосіб переконатися, що дані можуть бути недостовірними після того, як вони будуть надіслані ненадійним клієнтом і отримані назад, тоді ви хочете підписати дані, ви можете використовувати hmacбібліотеку для цього з SHA1 (все ще вважається безпечним для підписання HMAC ) або краще:

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)

Використовуйте це для підписання даних, потім прикріпіть підпис із даними та надішліть їх клієнту. Коли ви отримаєте дані назад, розділіть дані та підписи та підтвердіть. Я встановив алгоритм за замовчуванням на SHA256, тому вам знадобиться 32-байтний ключ:

key = secrets.token_bytes(32)

Ви можете поглянути на itsdangerousбібліотеку , яка пакує все це серіалізацією та десеріалізацією в різних форматах.

Використання AES-GCM-шифрування для забезпечення шифрування та цілісності

Fernet ґрунтується на AEC-CBC з підписом HMAC для забезпечення цілісності зашифрованих даних; зловмисний зловмисник не може подати дані вашої системної дурниці, щоб ваш сервіс був зайнятий в кругах з неправильним введенням, оскільки шифротекст підписаний.

Блоковий режим Галуа / Лічильник шифр виробляє шифротекст і тег , щоб служити тієї ж мети, тому можуть бути використані , щоб служити тієї ж мети. Мінус полягає в тому, що на відміну від Fernet не існує простого у використанні рецепта, що відповідає одному розміру, для повторного використання на інших платформах. AES-GCM також не використовує прокладки, тому цей шифротекст шифрування відповідає довжині вхідного повідомлення (тоді як Fernet / AES-CBC шифрує повідомлення до блоків з фіксованою довжиною, дещо затемнюючи довжину повідомлення).

AES256-GCM бере в якості основного секретного 32-байтного секрету:

key = secrets.token_bytes(32)

потім використовуйте

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

Я включив позначку часу, щоб підтримувати ті самі випадки використання, які підтримує Fernet.

Інші підходи на цій сторінці в Python 3

AES CFB - як CBC, але без потреби прокладки

Це такий підхід, якого всі Іѕ Ваііти дотримуються , хоча і неправильно. Це cryptographyверсія, але зауважте, що я включаю IV у шифротекст , він не повинен зберігатися як глобальний (повторне використання IV послаблює безпеку ключа, а зберігання його як глобальний модуль означає, що він буде знову генерований наступне виклик Python, що робить увесь шифротекст незашифрованим):

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

Цьому бракує додаткової броні підпису HMAC, і немає часової позначки; вам доведеться їх додати самостійно.

Сказане також ілюструє, як легко комбінувати основні складові криптографії неправильно; Неправильне поводження з значенням Іі Vаіітy може призвести до порушення даних або не зачитати всі зашифровані повідомлення через те, що IV втрачено. Використання Fernet натомість захищає вас від таких помилок.

AES ECB - не захищено

Якщо ви раніше реалізували шифрування AES ECB і вам потрібно все-таки підтримувати це в Python 3, ви можете зробити це ще й із цим cryptography. Це ж застереження застосовується, ЄЦБ недостатньо захищений для реальних програм . Повторно реалізуючи цю відповідь для Python 3, додавши автоматичну обробку прокладки:

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

Знову ж таки, тут відсутній підпис HMAC, і ви не повинні використовувати ECB в будь-якому випадку. Сказане є лише для того, щоб проілюструвати, що cryptographyможе обробляти загальні криптографічні будівельні блоки, навіть ті, якими ви не повинні користуватися.


51

"Encoded_c", згаданий у шифрованій відповіді Vigenere @ smehmood, повинен бути "key_c".

Тут працюють функції кодування / декодування.

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)

Відмова від відповідальності: Як випливає з коментарів, це не повинно використовуватися для захисту даних у реальній програмі, якщо ви не читаєте це і не проти спілкуватися з юристами:

Що не так з шифруванням XOR?


2
Дуже корисно, дякую. Я опублікував версію Python 3 нижче (це виглядало некрасиво в коментарях)
Ryan Barrett

1
"Закон Шнайєра" : Кожен, від найрозумнішого аматора до найкращого криптографа, може створити алгоритм, який він сам не може зламати. Не використовуйте це, воно навіть близько не захищене.
зап

3
Чудово! Ця версія також працює для рядків з наголосами, тоді як версія @ smehmood не має. Приклад використання:, encodedmsg = encode('mypassword', 'this is the message éçàèç"') print encodedmsg print decode('mypassword', encodedmsg)це чудово працює.
Бась

2
Ось текстовий плагін Sublime на основі цього коду, що дозволяє легко кодувати / розшифровувати текст CTRL + SHIFT + P, потім "Eeencode" або "Dddecode".
Бась

2
@basj Дякую за приклад
sk03

49

Ось версія Python 3 функцій з @qneill «s відповіді :

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)

Додатковий кодування / декодування потрібен тому, що Python 3 розділив рядки / байтові масиви на дві різні концепції та оновив їх API, щоб відобразити це.


4
Дякую, Райан, fwiw ти typo'd @qniell
qneill

3
Для тих, хто цікавиться, відмінності є .encode()).decode(). у відповідь encode(), а .decode()в другому рядку в decode().
RolfBly

2
Хм, кодований код не дуже унікальний, я провів тест, і він показує, що кожен код ключа 11,22,33,44, ..., 88,99,111,222, ... завжди має інший той же код, що і раніше. Але я ціную це
Hzzkygcs

1
@Ryan Barrett, чи можна перевірити правильність пароля при розшифровці. Скажімо, я надсилаю один кодований рядок, який знає ключ, що робити, якщо він вводить ключ друкарською помилкою? Декодування все ще дає йому "розшифровану" рядок, але це не той правильний, як він це може сказати?
Хайнц

26

Відмова від відповідальності: Як зазначено в коментарях, це не повинно використовуватися для захисту даних у реальній програмі.

Що не так з шифруванням XOR?

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

https://github.com/hellman/xortool


Як уже згадувалося, бібліотека PyCrypto містить набір шифрів. "Шифр" XOR можна використовувати для брудної роботи, якщо ви не хочете робити це самостійно:

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

Шифр працює наступним чином, не потребуючи прокладки простого тексту:

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

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

Подяка https://stackoverflow.com/a/2490376/241294 за основні функції кодування / декодування base64 (я новачок пітона).


Примітка: модуль Crypto встановлюється в python3 встановленим pycrptop, а не Crypto. sudo pip3 install pycrypto.
Nikhil VJ

2
Примітка: pycrypto не вдалося встановити на herokuapp в моєму кінці. Я знайшов цю публікацію. Здається, це говорить про те, що пакет пікрипто був замінений на інший під назвою пікриптодома інгаляція, і що метод XOR був устарений
Nikhil VJ

2
Ніколи не використовуйте цей метод , зверніть увагу на опис цього «шифру» в документації : XOR іграшковий шифр, XOR - це один із найпростіших потокових шифрів. Шифрування та дешифрування виконуються за допомогою XOR-ing даних за допомогою потоку ключів, зробленого контактуванням ключа. Не використовуйте його для реальних програм! .
Martijn Pieters

@MartijnPieters ти маєш рацію. Сподіваюся, моя редакція прояснила це питання.
poida

12

Ось реалізація безпечного шифрування та розшифровки URL-адрес за допомогою AES (PyCrypto) та 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))

Якщо у вас виникли такі проблеми, як це https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode), використовуйте str(cipher)під час розшифровки наступне:

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

Тест:

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

Помилка з Windows x64 + Python 3.6 + PyCryptodome (в PyCrypto засуджується) TypeError: Object type <class 'str'> cannot be passed to C code.
Бась

@Basj aww вибачте .. Я не використовую Windows, тому не можу виправити.
Усі Іѕ Vаіітy

Не генеруйте IV та зберігайте його на рівні модуля, вам потрібно включити IV у повідомлення, яке повертається до шифрованого тексту! Тепер ви запровадили дві проблеми: перезапуск процесу Python дає вам новий ІV, унеможливлюючи розшифрування раніше зашифрованих повідомлень, а тим часом ви повторно використовуєте IV для декількох повідомлень, що ефективно знижує безпеку до рівня ЄЦБ.
Мартійн Пітерс

1
@ AllІѕVаіітy Вирішено b'...', я відредагував відповідь для подальшої довідки!
Бась

8

Робочі функції кодування / декодування в python3 (адаптовані дуже мало з відповіді 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)

8

Дякую за чудові відповіді. Нічого оригінального додати, але ось кілька прогресивних переписувань відповіді qneill, використовуючи корисні засоби Python. Сподіваюся, ви погоджуєтесь, що вони спрощують та уточнюють код.

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()- сполучення елементів у списку з їх індексом

ітерація над символами в рядку

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)

складати списки, використовуючи розуміння списку

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)

Часто в Python взагалі немає потреби в індексах списків. Усуньте змінні індексу циклу повністю, використовуючи zip та цикл:

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)

і деякі тести ...

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

Дуже приємно @Nick, хороший прогрес пітонізмів і занадто завантажувані тести. Для того, щоб дати належну кредит я тільки виправлення помилок в оригінальному відповідь Smehmood в stackoverflow.com/a/2490718/468252 .
qneill

4

Якщо ви хочете бути в безпеці, ви можете використовувати Fernet, який криптографічно звучить. Ви можете використовувати статичну «сіль», якщо не хочете зберігати її окремо - ви втратите лише словник та запобігання атакам веселки. Я вибрав це, тому що можу вибрати довгі або короткі паролі´, що не так просто з 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

Якщо це занадто складно, хтось запропонував simplecrypt

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

Просто генеруйте сіль і включіть її в результат шифрування, тому повторні паролі та повідомлення все ще призводять до випадкового виведення. Включіть ітераційне значення також для підтвердження майбутнього алгоритму, але все-таки зможете розшифрувати повідомлення, використовуючи інше число ітерацій.
Martijn Pieters

3

Хто б не завітав сюди (і бунтер), здавалося, шукав одноколісні лайнери з не надто налаштованими налаштуваннями, які інші відповіді не дають. Тому я висуваю base64.

Тепер майте на увазі, що це лише основна затуманення, і вона знаходиться в ** НЕ СПОСІБНО ДЛЯ БЕЗПЕЧНОСТІ ** , але ось кілька одноразових:

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'

Кілька речей, які слід зазначити:

  • ви хочете самостійно займатися більш / менш кодуваннями / декодуваннями байт-рядків, залежно від вашого вводу-виводу. Загляньте в bytes()іbytes::decode()
  • base64 легко впізнати за типами використовуваних символів і часто закінчується =символами. Такі люди, як я, абсолютно займаються розшифровкою їх на консолі javascript, коли ми бачимо їх на веб-сайтах. Це так просто, як btoa(string)(js)
  • порядок - ключ + дані, як у b64, які символи відображаються в кінці залежить від того, які символи знаходяться на початку (через зміщення байтів. У Вікіпедії є кілька приємних пояснень). У цьому сценарії початок кодованого рядка буде однаковим для всього, що кодується цим ключем. Плюс полягає в тому, що дані будуть більш неясними. Якщо це зробити навпаки, це призведе до того, що частина даних буде однаковою для всіх, незалежно від ключа.

Тепер, якщо те, що ви хотіли, навіть не потребував будь-якого ключа, а лише якесь придушення, ви знову можете просто використовувати base64, без будь-яких типів ключа:

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'

2
Так, якщо вас не хвилює безпека, тоді base64 - це набагато краще, ніж шифрувати.
Martijn Pieters

Щодо інших відповідей, які не є одноколірними: це не сенс питання. Вони просять викликати дві функції. І Fernet(key).encrypt(message)це лише один вираз, як і ваш call64 call.
Martijn Pieters

І ви повинні видалитиkey взагалі. Навантаження розробників збираються копіювати і вставляти з переповнення стека , не звертаючи уваги і будуть вважати ключ , щоб бути секретом. Якщо ви маєте включити його, то, принаймні, не використовуйте його та попередити або створити виняток, якщо він все-таки використовується. Не варто недооцінювати нерозумність культури копіювання та вставки та свої обов'язки щодо виконання функціональних можливостей.
Martijn Pieters

3

Я дам 4 рішення:

1) Використання шифрування Fernet з cryptographyбібліотекою

Ось рішення за допомогою пакета cryptography, яке ви можете встановити, як завжди, за допомогою 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')

Ви можете адаптуватися за допомогою власної солі, кількості ітерацій тощо. Цей код не дуже далекий від відповіді @ HCLivess, але мета тут - мати готові до використання encryptта decryptфункції. Джерело: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet .

Примітка: використовуйте .encode()та .decode()скрізь, якщо ви хочете, щоб рядки були 'John Doe'замість байтів b'John Doe'.


2) Просте шифрування AES з Cryptoбібліотекою

Це працює з 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

Примітка: ви можете видалити, base64.b64encodeі .b64decodeякщо ви не хочете, щоб текст читався, та / або якщо ви хочете зберегти шифротекст на диску у вигляді двійкового файлу.


3) AES, використовуючи кращу функцію виведення ключа пароля та можливість перевірити, чи "введено неправильний пароль", з Cryptoбібліотекою

Рішення 2) з AES "CFB mode" нормально, але має два недоліки: той факт, що SHA256(password)його можна легко посилити за допомогою таблиці пошуку, і що немає можливості перевірити, чи був введений неправильний пароль. Це вирішено тут за допомогою використання AES в "режимі GCM", як обговорювалося в AES: як виявити, що введено неправильний пароль? і чи безпечний цей спосіб сказати "пароль, який ви ввели, неправильно"? :

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) Використання RC4 (бібліотека не потрібна)

Адаптовано з 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'

(Застаріло з часу останнього редагування, але зберігається для подальшої довідки): У мене виникли проблеми з використанням Windows + Python 3.6 + всіх відповідей, що стосуються pycrypto(не вдається в pip install pycryptoWindows) або pycryptodome(відповіді тут from Crypto.Cipher import XORне виконані, оскільки XORця pycryptoвилка не підтримується ; і рішення, що використовуються також ... AESне вдалося TypeError: Object type <class 'str'> cannot be passed to C code). Також бібліотека simple-cryptмає pycryptoзалежність, тому це не варіант.


1
Ви не хочете жорстко кодувати кількість солі та ітерації; генерують випадкову сіль і роблять підрахунок ітерації під час шифрування, включають цю інформацію в результат шифрування та витягують та використовують значення при дешифруванні. Сіль захищає вас від тривіального розпізнавання повторно використаних паролів для заданого повідомлення, ітерація рахує майбутні докази алгоритму.
Martijn Pieters

Так, звичайно, @MartijnPieters, але мета полягає в тому, щоб мати простий код для простих цілей, як вимагає ОП, з двома параметрами : звичайний текст + пароль. Звичайно, для більш складного сценарію (тобто бази даних) ви будете використовувати всі ці додаткові параметри.
Бась

Немає необхідності в додаткових параметрах! Я включаю цю інформацію, закодовану в непрозоре повернене значення password_encrypt().
Martijn Pieters

@MartijnPieters Приємне рішення справді.
Бась

2

Це працює, але довжина пароля повинна бути точно 8. Це просто і вимагає 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

ВИХІД:

³.\Þ\åS¾+æÅ`;Ê
John Doe

Не використовуйте фіксований IV! Рандомізуйте IV та додайте його замість шифротексту. В іншому випадку ви можете також використовувати режим ECB; повторні звичайні текстові повідомлення в іншому випадку є тривіальними для розпізнавання.
Martijn Pieters

Також проект pyDes видається мертвим; домашня сторінка пішла, а останній реліз на PyPI зараз 9 років.
Martijn Pieters

2

Інша реалізація коду @qneill, що включає контрольну суму CRC оригінального повідомлення, викидає виняток, якщо перевірка не виконана:

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

2

Ви можете використовувати AES для шифрування рядка паролем. Хоча ви хочете вибрати досить надійний пароль, щоб люди не могли легко здогадатися, що це таке (вибачте, я не можу йому допомогти. Я хочу захистити віні).

AES сильний з хорошим розміром клавіш, але його також просто використовувати у PyCrypto.


3
Дякую Алану Але для уточнення я не шифрую самі паролі. У наведеному вище прикладі я шифрую рядок "John Doe" відповідно до пароля "mypass", який є простим паролем, який я використовую у своєму вихідному коді. Паролі користувачів не задіяні, як і будь-яка інша дуже чутлива інформація. Я відредагував своє запитання, щоб уточнити це.
RexE

1
AES чудово, якщо його правильно використовувати. Однак його легко використовувати неправильно; тут є щонайменше одна відповідь, яка використовує режим незахищеного блоку шифрування, ще два, які маніпулюють обробкою значення IV. Краще використовувати гарну бібліотеку з чітко визначеним рецептом, як Фернет!
Martijn Pieters

Насправді це дуже проникливе спостереження. Я мацав IV раз.
Алан

0

Зовнішні бібліотеки забезпечують алгоритми шифрування секретного ключа.

Наприклад, Cypherмодуль в PyCrypto пропонує вибір багатьох алгоритмів шифрування:

  • 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 є Pythonобгорткою для OpenSSL і забезпечує (серед інших функцій) повноцінну бібліотеку криптографії загального призначення. У комплекті є симетричні шифри (як AES).


0

якщо ви хочете захистити шифрування:

для python 2, ви повинні використовувати keyczar http://www.keyczar.org/

для python 3, поки keyczar не доступний, я написав простий-склеп http://pypi.python.org/pypi/simple-crypt

обидва вони використовуватимуть ключове посилення, що робить їх більш безпечними, ніж більшість інших відповідей тут. а оскільки вони такі прості у використанні, можливо, ви захочете використовувати їх, навіть якщо безпека не є критичною ...


З сховища Keyczar : Важлива примітка: Keyczar застарілий. Розробники Keyczar рекомендують Tink , але не існує Python-версії Tink.
Martijn Pieters

0

Отже, як ніщо критично важливе значення не кодується , і ви просто хочете зашифрувати для затемнення .

Дозвольте представити шифр caeser

введіть тут опис зображення

Шифр Цезаря або зміна Цезаря - одна з найпростіших і широко відомих методів шифрування. Це тип шифру заміщення, у якому кожна буква в простому тексті замінюється буквою деякої фіксованої кількості позицій вниз за алфавітом. Наприклад, при зсуві вліво 3 D буде замінено на A, E стане B і так далі.

Приклад коду для довідки:

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

Переваги: ​​він відповідає вашим вимогам і простий, і робить кодування речі'y '.

Недолік: може бути зламаний простими алгоритмами грубої сили (навряд чи хтось спробує пройти всі додаткові результати).


0

Додавання ще одного коду з декодуванням та кодуванням для довідки

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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.