암호에 따라 문자열을 인코딩하는 간단한 방법?
Python은 암호를 사용하여 문자열을 인코딩/디코딩하는 간단한 내장 방식을 가지고 있습니까?
이와 같은 것:
>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'
그래서 "John Doe" 문자열은 'sjkl28cn2sx0'으로 암호화됩니다.원본 문자열을 얻으려면 소스 코드의 암호인 'mypass' 키로 해당 문자열을 "잠금 해제"합니다.이렇게 하면 암호로 Word 문서를 암호화/암호 해독할 수 있습니다.
이 암호화된 문자열을 URL 매개 변수로 사용하고 싶습니다.제 목표는 난독화이지 강력한 보안이 아닙니다. 미션 크리티컬한 것은 인코딩되지 않습니다.데이터베이스 테이블을 사용하여 키와 값을 저장할 수 있다는 것을 알고 있지만, 미니멀리스트가 되려고 노력하고 있습니다.
Python에는 암호화 체계가 내장되어 있지 않습니다.암호화된 데이터 스토리지도 심각하게 고려해야 합니다. 한 개발자가 안전하지 않다고 생각하는 사소한 암호화 체계와 장난감 체계는 경험이 적은 개발자가 안전한 체계로 오해할 수 있습니다.암호화하는 경우 제대로 암호화합니다.
그러나 적절한 암호화 체계를 구현하기 위해 많은 작업을 수행할 필요는 없습니다.우선, 암호화 휠을 다시 발명하지 말고 신뢰할 수 있는 암호화 라이브러리를 사용하여 이를 처리하십시오.Python 3의 경우 신뢰할 수 있는 라이브러리는 입니다.
또한 암호화 및 암호 해독을 바이트에 적용할 것을 권장합니다. 먼저 텍스트 메시지를 바이트로 인코딩합니다.stringvalue.encode()하고 UTF8을 사용하여 쉽게 되돌립니다.bytesvalue.decode().
마지막으로, 암호화 및 암호 해독 시 암호가 아닌 키에 대해 이야기합니다.키는 사람이 기억할 수 있는 것이 아니라 비밀 위치에 저장하지만 기계로 읽을 수 있는 것입니다. 반면 암호는 사람이 읽고 기억할 수 있는 경우가 많습니다.약간의 주의를 기울여 암호에서 키를 추출할 수 있습니다.
그러나 사용자의 주의 없이 클러스터에서 실행 중인 웹 응용 프로그램이나 프로세스가 계속 실행되는 경우에는 키를 사용해야 합니다.비밀번호는 최종 사용자만 특정 정보에 액세스해야 하는 경우에 사용됩니다.이 경우에도 일반적으로 암호로 응용프로그램을 보호한 다음 사용자 계정에 연결된 키를 사용하여 암호화된 정보를 교환합니다.
대칭 키 암호화
Fernet – AES CBC + HMAC, 강력 권장
그cryptography라이브러리에는 암호화를 사용하기 위한 모범 사례 레시피인 Fernet 레시피가 포함되어 있습니다.Fernet은 광범위한 프로그래밍 언어로 구현할 수 있는 개방형 표준이며 AES CBC 암호화를 버전 정보, 타임스탬프 및 HMAC 서명과 함께 패키지화하여 메시지 변조를 방지합니다.
Fernet을 사용하면 메시지를 매우 쉽게 암호화 및 해독할 수 있으며 사용자의 보안을 유지할 수 있습니다.이것은 비밀로 데이터를 암호화하는 이상적인 방법입니다.
보안 키를 생성하는 데 사용하는 것이 좋습니다.암호를 사용할 수도 있지만(다음 섹션) 전체 32바이트 암호 키(암호화할 16바이트와 서명할 16바이트)는 대부분의 암호보다 안전합니다.
는 Fernet 생성키는는입니다.bytesURL 및 파일 안전 base64 문자를 포함하는 객체로, 인쇄 가능:
from cryptography.fernet import Fernet
key = Fernet.generate_key() # store in a secure location
# PRINTING FOR DEMO PURPOSES ONLY, don't do this in production code
print("Key:", key.decode())
하려면 메지를암암면해려를 합니다.Fernet()지정된 키로 인스턴스를 만들고, 또는 를 호출합니다. 암호화할 일반 텍스트 메시지와 암호화된 토큰은 모두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'
>>> token = encrypt(message.encode(), key)
>>> print(token)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> decrypt(token, key).decode()
'John Doe'
암호를 사용하는 Fernet – 암호에서 파생된 키로 보안이 다소 약화됨
강력한 키 파생 방법을 사용하는 경우 비밀 키 대신 암호를 사용할 수 있습니다.그런 다음 먼저 salt, count 및 fernet 토큰을 구분하지 않고는 암호화된 값이 더 이상 Fernet과 호환되지 않도록 메시지에 salt 및 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 성능이 향상되도록 조정할 수 있습니다.
유사한 크기의 풀에서 적절한 임의의 암호를 생성할 경우 암호만으로도 Fernet 32바이트 임의 키만큼 안전할 수 있습니다.32바이트는 256^ 32개의 키를 제공하므로 74자(상위 26개, 하위 26개, 10자리 및 가능한 기호 12개)의 알파벳을 사용하는 경우 암호는 최소여야 합니다.math.ceil(math.log(256 ** 32, 74))42자입니다.그러나 잘 선택된 다수의 HMAC 반복은 공격자가 무차별적으로 침입하는 비용이 훨씬 더 많이 들기 때문에 엔트로피 부족을 다소 완화할 수 있습니다.
짧지만 안전한 암호를 선택하면 이 체계가 무력화되지 않으며, 무차별 대입 공격자가 검색해야 하는 가능한 값의 수를 줄일 수 있습니다. 보안 요구 사항에 적합한 강력한 암호를 선택해야 합니다.
대안
흐림
다른 방법은 암호화하지 않는 것입니다.Vignere는 보안 수준이 낮은 암호를 사용하거나 가정용으로 구현하려는 유혹을 느끼지 말라고 말합니다.이러한 접근 방식에는 보안이 없지만, 경험이 부족한 개발자에게 향후 코드를 유지 관리해야 하는 작업이 주어질 수 있으며, 이는 보안이 전혀 없는 것보다 더 나쁜 환상입니다.
당신이 필요한 것이 무명이라면, 데이터를 base64로 하면 됩니다. URL-safe 요구 사항에 대해서는 기능이 괜찮습니다.여기서 암호를 사용하지 말고 인코딩만 하면 됩니다.최대로 압축을 추가합니다(예: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='.
무결성만
신뢰할 수 없는 클라이언트로 전송된 후 데이터를 다시 수신한 후 데이터를 변경하지 않도록 신뢰할 수 있는 방법만 있으면 데이터에 서명할 수 있다면 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)
라이브러리를 보면 이 모든 것을 다양한 형식의 직렬화 및 직렬화 해제로 패키지화할 수 있습니다.
AES-GCM 암호화를 사용하여 암호화 및 무결성 제공
Fernet은 암호화된 데이터의 무결성을 보장하기 위해 HMAC 서명이 있는 AEC-CBC를 기반으로 합니다. 악의적인 공격자는 암호 텍스트가 서명되어 있기 때문에 잘못된 입력으로 서비스를 계속 실행하기 위해 시스템 넌센스 데이터를 제공할 수 없습니다.
Galois / Counter 모드 블록 암호는 동일한 목적을 위해 암호문과 태그를 생성하므로 동일한 목적을 위해 사용할 수 있습니다.단점은 Fernet과 달리 다른 플랫폼에서 재사용하기 쉬운 One-Size-Fit-All Recipe가 없다는 것입니다. 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와 유사하지만 패드가 필요하지 않음
이것은 모든 іа Vитіy가 따르는 접근법입니다. 비록 부정확하지만요.여기가 바로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 서명에 추가된 갑옷이 없고 타임스탬프가 없으므로 사용자가 직접 추가해야 합니다.
위의 내용은 기본 암호화 구성 요소를 잘못 결합하는 것이 얼마나 쉬운지를 보여줍니다. 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일반적인 암호화 구성 요소를 처리할 수 있습니다. 실제로 사용해서는 안 되는 구성 요소도 마찬가지입니다.
만약 당신이 아주 평범한 관찰자로부터 사물을 모호하게 만드는 단순한 난독화만을 찾고 있고, 타사 라이브러리를 사용할 생각은 없다고 가정합니다.비제네르 암호 같은 것을 추천합니다.그것은 단순한 고대 암호 중 가장 강력한 것 중 하나입니다.
빠르고 쉽게 구현할 수 있습니다.다음과 같은 것:
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 안전 문자로 대체됩니다(이름에서 알 수 있음).
그러나 이 난독화를 사용하기 전에 이 매우 얇은 층의 난독화가 사용자의 요구에 충분하다는 것을 절대적으로 확인해야 합니다.제가 링크한 위키백과 기사에는 암호를 해독하는 방법에 대한 자세한 지침이 나와 있어 적당한 결심을 가진 사람이라면 누구나 쉽게 해독할 수 있었습니다.
당신이 보안이 아닌 무명을 원한다고 명시적으로 언급했듯이, 우리는 당신이 제안하는 것의 약점에 대해 질책하는 것을 피하겠습니다 :)
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 안전해요!
다음은 @q neglus의 답변에서 나온 파이썬 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은 문자열/바이트 배열을 두 개의 서로 다른 개념으로 분할하고 API를 업데이트하여 이를 반영하기 때문에 추가 인코딩/디코드가 필요합니다.
@smemood의 Vigeneer 암호 답변에 언급된 "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)
고지 사항:코멘트에서 암시하는 바와 같이, 이 문서를 읽고 변호사와 대화하는 것을 꺼리지 않는 한, 실제 애플리케이션의 데이터를 보호하는 데 사용해서는 안 됩니다.
고지 사항:코멘트에서 언급한 것처럼, 이것은 실제 애플리케이션에서 데이터를 보호하는 데 사용되어서는 안 됩니다.
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!'
base64 인코딩/인코딩 기능에 대한 크레딧은 https://stackoverflow.com/a/2490376/241294 에 있습니다(저는 파이썬 초보자입니다).
AES(PyCrypto) 및 base64를 사용한 URL 안전한 암호화 및 암호 해독 구현입니다.
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
cryptocode암호로 문자열을 인코딩하고 디코딩하는 간단한 방법을 제공합니다.설치 방법은 다음과 같습니다.
pip install cryptocode
메시지 암호화(예: 코드):
import cryptocode
encoded = cryptocode.encrypt("mystring","mypassword")
## And then to decode it:
decoded = cryptocode.decrypt(encoded, "mypassword")
설명서는 여기에서 찾을 수 있습니다.
python3에서 인코딩/디코드 기능 작동(qnewl의 답변에서 거의 변경되지 않음):
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)
좋은 답변 감사합니다.추가할 독창적인 것은 없지만, 여기 몇 가지 유용한 파이썬 기능을 사용하여 Qnew의 답변을 점진적으로 고쳐 쓴 것이 있습니다.나는 당신이 그들이 코드를 단순화하고 명확화하는 것에 동의하기를 바랍니다.
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 및 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)
그리고 몇 가지 테스트...
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
만약 당신이 안전하기를 원한다면, 당신은 암호학적으로 안전한 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
그게 너무 복잡하다면, 누군가가 간단한 암호화를 제안했습니다.
from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
4가지 해결책을 제시하겠습니다.
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')
자신의 소금, 반복 횟수 등으로 적응할 수 있습니다.는 @ 멀지는 사용할 수 @HCLives @HCLives의 목표는 여기에 있습니다.encrypt그리고.decrypt기능들.출처: https://cryptography.io/en/latest/fernet/ #with-messages-with-messages.
고참: 사용을 합니다..encode()그리고..decode()'John Doe'같은 바이트 대신에b'John Doe'.
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텍스트로 읽을 수 있는 출력을 원하지 않거나 암호문을 디스크에 이진 파일로 저장하려는 경우.
더 비밀번호 키 과 " 입력"을 사용합니다.Crypto
AES 2 두 단점이 있습니다. AES "CFB 모드"는 2)의 .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")
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'
(최신 편집 이후에 만료되었지만 나중에 참조할 수 있도록 보관됨):+ 3+ + Python 3.6 + 관련된 하는 데 .pycrypto 수 ).pip install pycrypto) 또는 (으)로 됩니다.pycryptodome(서 (여기에답나있변은는와답은여▁(▁here▁with)로 from Crypto.Cipher import XOR때문에 실패했습니다.XOR.pycrypto 포크; 및을사솔루션한용다를 .... AES 했습니다.TypeError: Object type <class 'str'> cannot be passed to C code또한, 도서관은pycrypto의존성으로서, 그래서 그것은 선택사항이 아닙니다.
이것은 작동하지만 암호 길이는 정확해야 합니다.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
AES를 사용하여 암호로 문자열을 암호화할 수 있습니다.하지만 사람들이 쉽게 추측할 수 없도록 충분히 강력한 암호를 선택해야 할 것입니다(죄송합니다).저는 경비원이 되고 싶어요.
AES는 좋은 키 크기로 강력하지만 PyCrypto와 함께 사용하기도 쉽습니다.
이 대답은 보안에 좋지 않습니다. 중요한 것에는 사용하지 마십시오.
여기에 온 사람이 누구든(그리고 현상금을 주는 사람) 다른 대답들이 제공하지 않는 설정이 많지 않은 한 줄기를 찾는 것처럼 보였습니다.그래서 저는 베이스64를 내세웁니다.
이것은 기본적인 난독화일 뿐이며 보안에는 전혀 문제가 되지 않는다는 것을 명심하십시오. 하지만 여기 몇 가지 한 줄이 있습니다.
from base64 import urlsafe_b64encode, urlsafe_b64decode
def encode(data, key): # the key DOES NOT make this safe
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에 따라 바이트 대 문자열 인코딩/디코딩을 직접 처리할 수 있습니다.확인 및
- 는 사용되는할 수 , base64 사문유자형의에의해는수있쉽인며으, 다끝납니다로으음종종될용으로 .
=ㅠㅠ. 저 웹사이트에서 을 볼 때에서 그것들을 .저 같은 사람들은 우리가 웹사이트에서 그것들을 볼 때 자바스크립트 콘솔에서 그것들을 절대적으로 디코딩합니다.은 아주 .btoa(string)(js) - 순서는 key+data이며, 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'
참조용으로 디코딩 및 인코딩을 사용하여 코드 하나 추가
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()
원래 메시지의 CRC 체크섬을 포함하는 @q neilled 코드의 다른 구현체로, 검사가 실패할 경우 예외를 던집니다.
import struct
import zlib
import base64
def vigenere_encode(text, key):
text = text.encode() + struct.pack('i', zlib.crc32(text.encode()))
enc = []
for i in range(len(text)):
key_c = key[i % len(key)]
enc_c = chr((text[i] + ord(key_c)) % 256)
enc.append(enc_c)
enc = ''.join(enc).encode()
enc = base64.urlsafe_b64encode(enc)
return enc.decode()
def vigenere_decode(encoded_text, key):
dec = []
encoded_text = base64.urlsafe_b64decode(encoded_text).decode()
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]
crc = struct.pack('i', zlib.crc32(dec.encode()))
assert [int(i) for i in crc] == [ord(i) for i in checksum], 'Decode Checksum Error'
return dec
당신은 새로운 것을 사용할 수 있습니다.PyCryptodomex되지 않고 관리되지 않았기 입니다.
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
###################
class Cryptor:
def __init__(self, key):
self.SECRET_KEY = str(key).encode("utf-8")
self.BLOCK_SIZE = 32 # Bytes
self.CIPHER = AES.new(self.SECRET_KEY, AES.MODE_ECB) # never use ECB in strong systems obviously
def encrypt(self, text):
text = str(text).encode("utf-8")
return base64.b64encode(self.CIPHER.encrypt(pad(text, self.BLOCK_SIZE))).decode("utf-8")
def decrypt(self, encoded_text):
self.CIPHER = AES.new(self.SECRET_KEY, AES.MODE_ECB)
return unpad(self.CIPHER.decrypt(base64.b64decode(encoded_text)), self.BLOCK_SIZE).decode("utf-8")
cryptor = Cryptor("1234567890123456")
text = "hello world"
text = cryptor.encrypt(text)
print(text)
print(cryptor.decrypt(text))
참고: 키가 16바이트인지 확인합니다.
설할수있다와 함께 수.pip install pycryptodomex이것은 이전 PyCrypto 라이브러리로부터 독립된 유일한 버전이기 때문입니다.로 설치됩니다 예를 들어: "Cryptodome"은 Cryptodome 패키지입니다.
from Cryptodome.Cipher import AES
여기 https://www.pycryptodome.org/src/introduction 에서 공식 문서를 더 읽을 수 있습니다.
외부 라이브러리는 비밀 키 암호화 알고리즘을 제공합니다.
예를 들어 PyCrypto의 모듈은 다음과 같은 다양한 암호화 알고리즘을 제공합니다.
Crypto.Cipher.AESCrypto.Cipher.ARC2Crypto.Cipher.ARC4Crypto.Cipher.BlowfishCrypto.Cipher.CASTCrypto.Cipher.DESCrypto.Cipher.DES3Crypto.Cipher.IDEACrypto.Cipher.RC5Crypto.Cipher.XOR
미투크립토는PythonOpenSSL용 래퍼이며 (다른 기능 중에서도) 전체 강도의 범용 암호화 라이브러리를 제공합니다.대칭 암호(예: AES)가 포함됩니다.
보안 암호화를 원하는 경우:
python 2의 경우 keyczar http://www.keyczar.org/ 을 사용해야 합니다.
파이썬 3의 경우, 키차르가 이용 가능할 때까지, 나는 간단한 http://pypi.python.org/pypi/simple-crypt 을 작성했습니다.
둘 다 키 강화 기능을 사용하므로 여기 있는 대부분의 다른 답변보다 안전합니다. 사용하기가 매우 쉬우므로 보안이 중요하지 않은 경우에도 사용하고 싶을 수 있습니다.
따라서 미션 크리티컬한 것은 아무것도 인코딩되지 않으며, 암호화를 통해 해독할 수 있습니다.
시저의 암호를 제시하겠습니다.
시저의 암호 또는 시저 시프트는 가장 단순하고 널리 알려진 암호화 기술 중 하나입니다.대체 암호의 일종으로, 평문의 각 문자가 알파벳 아래의 일정한 수의 위치로 대체됩니다.예를 들어 왼쪽 이동이 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' 인코딩 작업을 수행합니다.
단점: 간단한 무차별 대입 알고리즘으로 인해 균열이 생길 수 있습니다(추가 결과를 모두 시도할 가능성은 매우 낮습니다).
언급URL : https://stackoverflow.com/questions/2490334/simple-way-to-encode-a-string-according-to-a-password
'codememo' 카테고리의 다른 글
| ggplot에서 범례(텍스트) 레이블 편집 (0) | 2023.06.27 |
|---|---|
| 다른 Firebase 계정으로 앱 전송 (0) | 2023.06.27 |
| 스프링 데이터 - Mongodb - 내포된 객체에 대한 방법으로 찾기 (0) | 2023.06.22 |
| 기존 MVC 웹 응용 프로그램에 웹 API를 추가한 후 404 오류 발생 (0) | 2023.06.22 |
| 최대 연결 풀 크기 (0) | 2023.06.22 |
