web-dev-qa-db-ja.com

パスワードに従って文字列をエンコードする簡単な方法?

Pythonには、パスワードを使用して文字列を簡単にエンコード/デコードする組み込みの方法がありますか?

このようなもの:

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

したがって、文字列「John Doe」は「sjkl28cn2sx0」として暗号化されます。元の文字列を取得するには、ソースコードのパスワードであるキー「mypass」でその文字列を「ロック解除」します。パスワードでWord文書を暗号化/復号化できる方法になりたいです。

これらの暗号化された文字列をURLパラメーターとして使用したいと思います。私の目標は難読化であり、強力なセキュリティではありません。ミッションクリティカルなものは何もエンコードされていません。データベーステーブルを使用してキーと値を格納できることを理解していますが、最小限にしようとしています。

108
RexE

あなたがonlyであると仮定すると、very何気ないオブザーバーであり、サードパーティのライブラリを使用したくない場合。 Vigenere暗号のようなものをお勧めします。これは、単純な古代暗号の中で最も強力なものの1つです。

Vigenère暗号

迅速かつ簡単に実装できます。何かのようなもの:

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が最善の策です。ただし、以前の回答ではいくつかの詳細を見落としています。PyCryptoのECBモードでは、メッセージの長さが16の倍数であることが必要ですだから、パッドする必要があります。また、URLパラメータとして使用する場合は、標準ではなくbase64.urlsafe_b64_encode()を使用します。これにより、base64アルファベットの文字の一部がURLセーフ文字に置き換えられます(名前が示すとおり)。

ただし、これを使用する前に、このvery難読化の薄い層で十分であることを完全に確認する必要があります。私がリンクしたウィキペディアの記事には、暗号を破る詳細な手順が記載されているため、中程度の決意を持っている人なら誰でも簡単に破ることができます。

64
smehmood

セキュリティではなく不明瞭さを望むことを明示的に述べているように、あなたが提案するものの弱さを非難することは避けます:)

だから、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()

誰かがあなたのデータベースとあなたのコードベースを手に入れたら、彼らは暗号化されたデータを解読することができます。 secret_keyを安全に保管してください!

65
Will

@smehmoodのVigenere暗号の回答で言及されている「encoded_c」は「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)
47
qneill

@qneillの answer の関数のPython 3バージョンを以下に示します。

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は文字列/バイト配列を2つの異なる概念に分割し、それを反映するようにAPIを更新したため、追加のエンコード/デコードが必要です。

46
Ryan Barrett

Pythonには組み込みの暗号化スキームはありません。また、暗号化されたデータストレージを重要視する必要があります。ある開発者が安全でないと理解している小さな暗号化スキームと、おもちゃのスキームは、経験の浅い開発者によって安全なスキームと間違われる可能性があります。暗号化する場合は、適切に暗号化します。

ただし、適切な暗号化スキームを実装するために多くの作業を行う必要はありません。まず、暗号化ホイールを再発明しない、信頼できる暗号化ライブラリを使用してこれを処理します。 Python 3の場合、その信頼できるライブラリは cryptography です。

また、暗号化と復号化をbytes;に適用することをお勧めします。最初にテキストメッセージをバイトにエンコードします。 stringvalue.encode()はUTF8にエンコードされ、bytesvalue.decode()を使用して簡単に元に戻すことができます。

最後に大事なことを言い忘れましたが、暗号化と復号化の際には、パスワードではなくkeysについて話します。キーは人間が覚えやすいものであってはなりません。秘密の場所に保存しますが、機械で読み取り可能なものであるのに対し、パスワードは人間が読み取って記憶できることがよくあります。 canパスワードからキーを導出しますが、少し注意してください。

ただし、クラスターで実行するWebアプリケーションまたはプロセスを実行し続ける人間の注意がなければ、キーを使用する必要があります。パスワードは、エンドユーザーのみが特定の情報にアクセスする必要がある場合に使用します。それでも、通常はアプリケーションをパスワードで保護し、キー(おそらくユーザーアカウントに添付されているキー)を使用して暗号化された情報を交換します。

対称キー暗号化

Fernet – AES CBC + HMAC、強く推奨

cryptographyライブラリには、暗号化を使用するためのベストプラクティスレシピである Fernetレシピ が含まれています。 Fernetは オープンスタンダード であり、さまざまなプログラミング言語ですぐに実装でき、バージョン情報、タイムスタンプ、およびメッセージの改ざんを防ぐHMAC署名を使用してAES CBC暗号化をパッケージ化します。

Fernetを使用すると、メッセージの暗号化と復号化が非常に簡単になりますand安全性が確保されます。これは、データを秘密で暗号化するための理想的な方法です。

Fernet.generate_key() を使用して安全なキーを生成することをお勧めします。パスワードも使用できます(次のセクション)が、32バイトの完全な秘密キー(暗号化に16バイト、さらに署名に16バイト)を使用すると、考えられるほとんどのパスワードよりも安全になります。

Fernetが生成するキーは、URLとファイルセーフなbase64文字を持つbytesオブジェクトであるため、印刷可能です。

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'

パスワード付きのFernet – パスワードから派生したキー、セキュリティを多少弱めます

強力なキー派生メソッドを使用する であれば、秘密キーの代わりにパスワードを使用できます。その後、メッセージにソルトとHMAC反復カウントを含める必要があるため、最初にソルト、カウント、およびフェルネットトークンを分離しない限り、暗号化された値はフェルネット互換ではなくなります。

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'

出力にソルトを含めると、ランダムなソルト値を使用できます。これにより、パスワードの再利用やメッセージの繰り返しに関係なく、暗号化された出力が完全にランダムになることが保証されます。反復カウントを含めることにより、古いメッセージを復号化する機能を失うことなく、CPUパフォーマンスの経時的な増加を調整できます。

パスワードだけcanは、同様のサイズのプールから適切にランダムなパスワードを生成するという条件で、Fernet 32​​バイトランダムキーと同じくらい安全です。 32バイトは256 ^ 32個のキーを提供するため、74文字のアルファベット(上26、下26、10桁、12の可能な記号)を使用する場合、パスワードは少なくともmath.ceil(math.log(256 ** 32, 74)) == 42文字の長さにする必要があります。ただし、 適切に選択されたHMAC反復の数が多い を使用すると、エントロピーの不足を多少軽減できます。

短くても合理的に安全なパスワードを選択してもこのスキームが損なわれることはないことを知ってください。ブルートフォース攻撃者が検索しなければならない可能性のある値の数を減らすだけです。必ず セキュリティ要件に合った強力なパスワード を選択してください。

代替案

あいまい

代替手段は暗号化しないです。低セキュリティ暗号、またはVignereのホームスピン実装を使用するように誘惑されないでください。これらのアプローチにはセキュリティはありませんが、コードを将来維持するタスクを与えられた経験の浅い開発者には、セキュリティがないというよりも悪いセキュリティの幻想を与える可能性があります。

必要なものがあいまいな場合は、データをbase64にします。 URLセーフ要件の場合、 base64.urlsafe_b64encode() function で問題ありません。ここではパスワードを使用せず、エンコードするだけで完了です。せいぜい、いくつかの圧縮を追加します(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='に変わります。

整合性のみ

信頼できないクライアントに送信されて受信された後、データがnalteredであると信頼できるようにする方法だけが必要な場合は、データに署名したい場合は、 hmac library 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 library をご覧になるとよいでしょう。これは、さまざまな形式でのシリアル化と非シリアル化でこれをすべてパッケージ化します。

AES-GCM暗号化を使用して暗号化と整合性を提供する

Fernetは、HMAC署名を使用してAEC-CBC上に構築され、暗号化されたデータの整合性を確保します。暗号文が署名されているため、悪意のある攻撃者がシステムの無意味なデータを送り込んで、不正な入力のあるサークルでサービスをビジー状態に保つことはできません。

Galois/Counter mode block cipher は、暗号テキストとtagを生成し、同じ目的を果たすため、同じ目的を果たすために使用できます。欠点は、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に似ていますが、パディングする必要はありません

これは、間違っているとはいえ、 AllІѕVаиітy に従うアプローチです。これはcryptographyバージョンですが、I 暗号化テキストに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値のすべての不適切な処理は、IVが失われたため、データ侵害またはすべての暗号化されたメッセージが読めなくなる可能性があります。代わりにFernetを使用すると、このようなミスから保護されます。

AES ECB – 安全ではない

以前に AES ECB暗号化 を実装しており、Python 3でこれを引き続きサポートする必要がある場合は、cryptographyでもまだサポートできます。 同じ注意事項が適用されます。ECBは 実際のアプリケーションには十分ではありませんです。 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が一般的な暗号化ビルディングブロック(実際に使用してはならないものも含む)を処理できることを示すためのものです。

40
Martijn Pieters

免責事項:コメントで述べたように、このは使用すべきではありません実際のアプリケーションのデータを保護します。

XOR暗号化の何が問題になっていますか?

https://crypto.stackexchange.com/questions/56281/breaking-a-xor-cipher-of-known-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エンコード/デコード関数(私はpython newbie)に感謝します。

24
poida

AES(PyCrypto)とbase64を使用したURLセーフ暗号化と復号化の実装を次に示します。

import base64
from Crypto import Random
from Crypto.Cipher import AES

AKEY = '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:文字マッピングは整数、Noneまたはunicodeを返す必要があります)次のようにデコード中にstr(cipher)を使用します

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

In [13]: encode("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
10

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
Christian

すばらしい回答をありがとう。追加するオリジナルはありませんが、ここにいくつかの便利なPython機能を使用したqneillの答えの漸進的な書き換えがあります。コードの簡素化と明確化に同意していただければ幸いです。

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
6
Nick

安全にしたい場合は、暗号的に適切なFernetを使用できます。個別に保存したくない場合は、静的な「塩」を使用できます。辞書とRainbow攻撃の防止のみが失われます。長いパスワードまたは短いパスワードを選択できるため、これを選択しました。これは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)
4
HCLivess

注:Windows + Python 3.6 + pycrypto(Windowsではpip install pycryptoを使用できない)またはpycryptodomefrom Crypto.Cipher import XORの回答はXORはこのpycrypto forkではサポートされていません。また、... AESを使用するソリューションもTypeError: Object type <class 'str'> cannot be passed to C codeで失敗しました。また、ライブラリ simple-crypt は依存関係としてpycryptoを持っているため、オプションではありません。


パッケージcryptographyを使用したソリューションは、pip install cryptographyで通常どおりインストールできます。

import base64
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

def encrypt(plaintext, password):
    f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode())))
    return f.encrypt(plaintext.encode()).decode()

def decrypt(ciphertext, password):
    f = Fernet(base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password.encode())))
    return f.decrypt(ciphertext.encode()).decode()

使用法:

>>> encrypt('John Doe', password='mypass')
'gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g=='

>>> decrypt('gAAAAABciC64VNUoeLVoPKut7HxlPcsqJZTFf99EMYTmnR9jFkZBNMxklIDc5WZ6k3TxfcFSDO-4PRZUsOvywtGLSFwOzToO_g==', password='mypass')
'John Doe'

注意:

3
Basj

元のメッセージのCRCチェックサムを含む@qneillコードの他の実装では、チェックが失敗した場合に例外をスローします。

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
ahmed

これは機能しますが、パスワードの長さは正確に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
2
Pratik Deoghare

AESを使用して、パスワードで文字列を暗号化できます。ただし、ユーザーが簡単に推測できないように、十分に強力なパスワードを選択することをお勧めします(申し訳ありませんが、私はそれを助けることができません。私はセキュリティウイニーになりたいです)。

AESは優れたキーサイズで強力ですが、PyCryptoでも簡単に使用できます。

2
Alan

外部ライブラリは、秘密鍵暗号化アルゴリズムを提供します。

たとえば、PyCryptoの Cypherモジュール は、多くの暗号化アルゴリズムの選択を提供します。

  • 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

MeTooCryptoOpenSSLPythonラッパーであり、(他の機能の中でも)強力な汎用暗号化ライブラリを提供します。対称暗号(AESなど)が含まれます。

1
gimel

ここに来た人(およびその恩恵を受けた人)は、セットアップがあまりないワンライナーを探しているように見えますが、他の答えは提供しません。だから私は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'

注意すべきいくつかの点:

  • i/Oに応じて、バイトから文字列へのエンコード/デコードをより多く/より少なく自分で処理する必要があります。 bytes() および bytes::decode() を調べます
  • base64は、使用される文字の種類によって簡単に認識され、多くの場合=文字で終わります。私のような人は、ウェブサイトでそれらを見ると、絶対にJavaScriptコンソールでデコードします。 btoa(string)(js)と同じくらい簡単です
  • b64のように、順序はキー+データです。末尾に表示される文字は、先頭にある文字によって異なります(バイトオフセットのため。 Wikipedia にはいくつかの素敵な説明があります)。このシナリオでは、エンコードされた文字列の先頭は、そのキーでエンコードされたすべてのものと同じになります。プラスは、データがより難読化されることです。逆に行うと、キーに関係なく、データ部分はすべてのユーザーでまったく同じになります。

さて、あなたが望むものがどんな種類のキーさえ必要とせず、いくらかの難読化さえ必要でなかったなら、あなたは再びどんな種類のキーもなしで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'
1
towc

安全な暗号化が必要な場合:

python 2の場合、keyczarを使用する必要があります http://www.keyczar.org/

python 3の場合、keyczarが使用可能になるまで、simple-crypt http://pypi.python.org/pypi/simple-crypt を作成しました。

これらはどちらもキー強化を使用するため、ここでの他のほとんどの回答よりも安全です。そして、それらは非常に使いやすいので、セキュリティが重要でない場合でも使用したいかもしれません...

0
andrew cooke

したがって、ミッションクリティカルなものは何もエンコードされていないため難読化のために暗号化するだけです

caeser's cipher

enter image description here

シーザーの暗号またはシーザーシフトは、最も単純で最も広く知られている暗号化技術の1つです。これは、平文の各文字がアルファベットの下の一定数の位置にある文字に置き換えられる置換暗号の一種です。たとえば、左シフトが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))

利点:それはあなたの要件を満たし、シンプルであり、エンコーディングのことを行います。

短所:単純なブルートフォースアルゴリズムによって解読される可能性があります(誰もがすべての追加の結果を試すことはほとんどありません)。

0
Yash Mishra