이 AES GCM 파일 암호화가 좋은 방법입니까?

Nov 17 2020

나는 이것을 사용하여 파일을 암호화 한 다음 AES-GCM을 사용하여 파일을 해독합니다.

( pip install pycryptodome아직 설치되지 않은 경우 먼저 수행 )

import Crypto.Random, Crypto.Protocol.KDF, Crypto.Cipher.AES

def cipherAES_GCM(pwd, nonce):
    key = Crypto.Protocol.KDF.PBKDF2(pwd, nonce, count=100_000)
    return Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_GCM, nonce=nonce)    

# encrypt
plaintext = b'HelloHelloHelloHelloHelloHelloHello'  # in reality, read from a file
key = b'mykey'
nonce = Crypto.Random.new().read(16)
c, tag = cipherAES_GCM(key, nonce).encrypt_and_digest(plaintext)
ciphertext = nonce + tag + c     # write ciphertext to disk as the "encrypted file"

# decrypt
nonce, tag, c = ciphertext[:16], ciphertext[16:32], ciphertext[32:]  # read from the "encrypted file" on disk
plain = cipherAES_GCM(key, nonce).decrypt_and_verify(c, tag).decode()
print(plain)  # HelloHelloHelloHelloHelloHelloHello

이것이 좋은 암호화 관행으로 간주되며이 파일 암호화 구현의 잠재적 인 약점은 무엇입니까?


비고 : 암호화 할 파일이 10,000 개 있습니다. 매번 파일을 암호화 할 때 KDF (높은 count값)를 호출하면 매우 비효율적입니다!
더 나은 해결책은 다음과 같습니다. KDF를 한 번만 호출하고 (사용하여 nonce1) 각 파일에 대해 다음을 수행합니다.

nonce2 = Crypto.Random.new().read(16)
cipher, tag = AES.new(key, AES.MODE_GCM, nonce=nonce2).encrypt_and_digest(plain)

그러나 이것은 nonce1 | nonce2 | ciphertext | tag각 파일에 대해 디스크에 써야 함 을 의미 합니까? 이렇게 nonce1하면 각 파일에 16 바이트 가 추가 됩니다.

답변

2 Topaco Nov 19 2020 at 21:18

코드 개선을위한 제안은 GCM에 12 바이트 임시 값을 적용하는 것입니다. 현재 16 바이트 논스 사용이 변경되어야 참조 여기 초. 참고 , 여기 .

GCM의 보안을 위해 중요한 것은 키 / 비어스 쌍이 두 번 이상 사용되지 않는다는 것 입니다. 각 암호화에 대한 코드에서 임의의 임시 값이 생성되므로이 문제가 방지됩니다.

코드는이 같은 키 / 넌스 페어의 여러 사용으로 이어질하지 않는 한 원칙적으로 어떤 보안 문제에 키 유도, 소금으로도 비표를 적용 여기 .

그러나 이것의 단점은 솔트 길이가 nonce 길이에 의해 결정된다는 것입니다. 이것이 바람직하지 않은 경우 (예를 들어 더 큰 솔트를 사용해야하는 경우), 대체 접근 방식은 각 암호화에 대해 임의 솔트를 생성하여 KDF를 통해 키와 임시 값을 모두 파생시키는 것 입니다. 이 시나리오에서는 연결된 데이터 salt | ciphertext | tag가 수신자에게 전달됩니다. 또 다른 대안은 논 스와 키 생성을 완전히 분리하고 각 암호화에 대해 키 생성을위한 랜덤 논 스와 랜덤 솔트를 모두 생성하는 것입니다. 이 경우 연결된 데이터 salt | nonce | ciphertext | tag는 수신자에게 전달되어야합니다. nonce 및 태그와 마찬가지로 salt도 비밀이 아니므로 암호문과 함께 전송할 수 있습니다.

이 코드는 100,000의 반복 횟수를 적용합니다. 일반적으로 다음 사항이 적용됩니다. 여기서 허용 가능한 성능을 유지하면서 반복 횟수는 사용자 환경에 허용되는만큼 높아야 합니다 . 100,000이 환경에 대한이 기준을 충족하면 괜찮습니다.

사용하는 연결 순서는 nonce | tag | ciphertext입니다. 양측이 이것을 알고있는 한 이것은 문제가되지 않습니다. 종종 규칙에 따라 nonce | ciphertext | tag순서가 사용됩니다 (예 : Java 가 암호 텍스트에 태그를 암시 적으로 추가 함).이 규칙을 고수하려는 경우 코드에서도 사용할 수 있습니다.

PyCryptodome의 경우와 같은 최신 유지 관리 라이브러리를 사용하는 것도 중요합니다 (전임자 인 기존 PyCrypto와 달리 전혀 사용해서는 안 됨).

편집 :
PyCryptodome의 PBKDF2 구현은 생성 된 키의 길이에 기본적으로 16 바이트를 사용하며 이는 AES-128에 해당합니다. 다이제스트의 경우 HMAC / SHA1이 기본적으로 적용됩니다. 게시 된 코드는 이러한 표준 매개 변수를 사용하며 안전하지 않은 매개 변수는 없지만 필요한 경우 여기에서 변경할 수 있습니다 .
참고 : SHA1 자체가 불안하지만, 이것은 PBKDF2 또는 HMAC의 맥락에서 적용되지 않습니다 여기 . 그러나 생태계에서 SHA1의 멸종을 지원하기 위해 SHA256을 사용할 수 있습니다.


편집 : (질문 업데이트에 관하여) :

편집 된 질문에 제시된 사용 사례는 10,000 개 파일의 암호화입니다. 게시 된 코드는 각 파일에 대해 실행되므로 KDF를 통해 해당하는 수의 키가 생성되어 성능 손실이 발생합니다. 이것은 매우 비효율적 이라고 설명합니다 . 그러나 현재 코드가 보안에 초점을 맞추고 성능에 덜 초점을 맞추고 있다는 사실을 잊지 말아야합니다. 내 대답에서 반복 횟수는 특정 제한 내에서 성능과 보안 사이의 조정을 허용하는 매개 변수라는 점을 지적했습니다.

PBKDF (암호 기반 키 파생 기능)를 사용하면 취약한 암호에서 키를 추출 할 수 있습니다. 암호화를 안전하게 유지하기 위해 공격자가 강력한 키 (이상적으로)보다 취약한 암호를 더 빨리 해독 할 수 없도록 파생 시간을 의도적으로 늘립니다. 파생 시간이 단축되면 (예 : 반복 횟수를 줄이거 나 동일한 키를 두 번 이상 사용하여) 일반적으로 보안이 감소합니다. 간단히 말해, 성능 향상 (빠른 PBKDF에 의한)은 일반적으로 보안을 감소시킵니다. 이로 인해 더 성능이 좋은 (그러나 더 약한) 솔루션을위한 특정 여유가 생깁니다.

더 성능이 좋은 솔루션은 다음과 같습니다. 이전과 마찬가지로 파일 에 대해 임의의 임시 값이 생성 됩니다. 그러나 파일을 자체 키로 암호화하는 대신 모든 파일이 동일한 키로 암호화됩니다 . 이를 위해 무작위 솔트가 한 번 생성 되며이 키는 KDF를 통해 파생됩니다. 이것은 실제로 상당한 성능 향상을 의미합니다. 그러나 이것은 자동으로 보안 감소를 수반합니다. 공격자가 키를 얻는 데 성공하면 공격자는 모든 파일을 해독 할 수 있습니다 ( 원래 시나리오에서와 같이 하나만 해독 할 수 없음 ). 그러나이 단점은 보안 요구 사항의 범위 내에서 허용되는 경우 필수 제외 기준이 아닙니다 (여기에 해당됨).

더 성능이 좋은 솔루션은 정보 salt | nonce | ciphertext | tag를받는 사람에게 보내야합니다. 수신자가 PBKDF를 통해 키를 파생하기 위해 솔트가 필요하기 때문에 솔트가 중요하며 누락되어서는 안됩니다. 수신자가 키를 결정하면 암호문을 태그로 인증하고 임시 값을 사용하여 해독 할 수 있습니다. 수신자가 각 파일에 동일한 키를 사용한다는 데 동의 한 경우 수신자가 PBKDF를 통해 키를 한 번만 추출하면 충분합니다 . 그렇지 않으면 각 파일에 대해 키를 파생해야합니다.

16 바이트의 솔트가 원치 않는 경우 (이 접근 방식의 모든 파일에 대해 동일하므로) 대체 아키텍처를 고려할 수 있습니다. 예를 들어 하이브리드 체계를 사용할 수 있습니다. 임의의 대칭 키가 생성되고 공개 키 인프라를 사용하여 교환됩니다. 또한 여기에서 모든 파일을 동일한 키로 암호화하거나 각 파일을 자체 키로 암호화 할 수 있습니다.

그러나 디자인 제안에 대한보다 구체적인 제안을 위해 사용 사례를 더 자세히 설명해야합니다 (예 : 파일 관련). 파일 크기는? 스트림 / 청크 처리가 필요합니까? 또는 수신자 관련 : 수신자가 몇 명입니까? 받는 사람과 일치하는 것은 무엇입니까? 기타

1 KhaledGaber Nov 19 2020 at 19:34

이것은 괜찮아 보이지만 암호화 및 키 파생에 동일한 nonce를 사용하지 않는 것이 좋습니다 (nonce는 동일한 nonce를 사용하여 한 번만 사용되는 키를 나타내므로 md5원하지 않는 경우 nonce 의 해시를 암호화 기능에 전달할 수 있습니다 . 다른 nonce (IV)를 사용합니다. 두 번째 cryptography로 더 나은 보안에 관심 이 있으면 전환 할 수 있다고 생각합니다 . 이것은 cryptography암호화하는 모듈을 사용하는 예제 코드 128-bit이며 안전한 키를 사용하여 암호화하는 장점 이 있으며 나머지는 처리합니다. 등 IV(논스), 암호 해독 및 검증 (사용하여 수행됩니다 HMAC). 적은 복잡성 때문에 틀림없이 더 안전한 코드로 이어질이 몇 줄 요약 할 수있다 그래서 모든 코드 위.

from cryptography.fernet import Fernet
plaintext = b"hello world"
key = Fernet.generate_key()
ctx = Fernet(key)
ciphertext = ctx.encrypt(plaintext)
print(ciphertext)
decryption = ctx.decrypt(ciphertext)
print(decryption)

편집 : nonce가 암호 텍스트와 함께 전송되기 때문에 사용하는 nonce는 키를 약화시킬 것입니다. 이제 소금 PBKDF은 무의미하며 이제 공격자는 암호를 추측해야합니다 (기본 수를 사용한다고 가정). 아주 간단한 것, 무차별 대입은 26^5시도 보다 오래 걸리지 않습니다 (총 길이가 5 인 경우 소문자 알파벳의 합계).