PDF 암호화 엔진을 Python에서 작업하는 방법
Aspose.PDF FOSS for Python (import aspose_pdf)는 aspose_pdf.engine.encryption에 있는 작은 내부 엔진 클래스인 EncryptionUtils을 통해 바이트 수준에서 PDF 비밀번호 보호를 구현합니다. 여기에는 AES-CBC 암호와 PDF 표준 보안 핸들러의 키 파생 알고리즘(ISO 32000 개정 2~6)이 실제로 존재하며, 고수준 Document API와는 독립적입니다. 이 가이드는 패키지를 설치하고 세 가지 엔진 작업을 단계별로 안내합니다: AES-CBC을 사용한 원시 바이트 암호화 및 복호화, 비밀번호에 대한 표준 보안 핸들러 키 파생(개정 2-4), 그리고 개정 5/6 문서에 대한 AES-256 키 파생.
단계별 가이드
Step 2: 필요한 클래스 가져오기
EncryptionUtils은 최상위 aspose_pdf 패키지에서 재내보내지 않으므로 — engine.encryption 서브모듈에서 직접 가져오세요:
import os
from aspose_pdf.engine.encryption import EncryptionUtilsStep 3: AES-CBC으로 데이터 암호화 및 복호화
encrypt_aes_cbc와 decrypt_aes_cbc은 비밀번호 기반 키 파생을 거치지 않고 16, 24, 또는 32바이트 키(AES-128, AES-192, 또는 AES-256)에 직접 작동합니다. encrypt_aes_cbc는 내부적으로 PKCS7 패딩을 적용하고, decrypt_aes_cbc는 이를 제거합니다.
key = os.urandom(32) # AES-256; 16- and 24-byte keys are also accepted
plaintext = b"Confidential PDF content"
ciphertext = EncryptionUtils.encrypt_aes_cbc(key, plaintext)
decrypted = EncryptionUtils.decrypt_aes_cbc(key, ciphertext)
assert decrypted == plaintext
print("Roundtrip OK, ciphertext length:", len(ciphertext))단계 4: 비밀번호에 대한 표준 보안 핸들러 키 도출 (수정 2-4)
compute_owner_key_v4는 알고리즘 3.3에 따라 소유자 비밀번호 값(O)을 계산하고, compute_user_key_v4는 알고리즘 3.4/3.5에 따라 사용자 비밀번호 값(U) 및 파일 암호화 키를 계산합니다. verify_password_v4는 후보 비밀번호에서 키를 다시 도출하여 문서를 열 수 있는지 확인한 후 신뢰할 수 있게 합니다.
owner_password = "owner-secret"
user_password = "user-secret"
key_length = 16 # 128-bit (AES/RC4); Revision 2 uses 5 (40-bit)
revision = 4
file_id = os.urandom(16)
permissions = -4 # standard PDF permissions value
o_value = EncryptionUtils.compute_owner_key_v4(
owner_password, user_password, key_length, revision
)
u_value, encryption_key = EncryptionUtils.compute_user_key_v4(
user_password, o_value, permissions, file_id, key_length, revision
)
verified_key = EncryptionUtils.verify_password_v4(
user_password, u_value, o_value, permissions, file_id, key_length, revision
)
assert verified_key == encryption_key
ciphertext = EncryptionUtils.encrypt_aes_cbc(verified_key, b"Confidential PDF content")
decrypted = EncryptionUtils.decrypt_aes_cbc(verified_key, ciphertext)
assert decrypted == b"Confidential PDF content"o_value와 u_value는 key_length와 관계없이 항상 32바이트입니다. 비밀번호가 일치하지 않을 때 verify_password_v4는 None를 반환합니다 — 예외가 아니라 —.
단계 5: 수정 5/6 문서에 대한 AES-256 키 도출
수정 5 및 6 보안 핸들러(AES-256)는 compute_owner_key_v4/compute_user_key_v4을 사용하지 않으며 — 비밀번호를 compute_hash_v5와 함께 해시합니다. 이는 ISO 32000-2의 “Algorithm 2.B"를 구현합니다. 새 파일 식별자를 위해 generate_file_id을 사용하고, 파일 암호화 키를 특정 간접 객체 하나에 대한 키로 변환하려면 derive_object_key을 사용하십시오.
password = b"SecurePassword123"
salt = os.urandom(8)
password_hash = EncryptionUtils.compute_hash_v5(password, salt)
assert len(password_hash) == 32
file_id = EncryptionUtils.generate_file_id()
assert len(file_id) == 16
file_key = os.urandom(32)
object_key = EncryptionUtils.derive_object_key(file_key, 12, 0, use_aes=True)
assert len(object_key) == 16derive_object_key는 객체 번호와 생성 번호를 파일 키에 혼합하므로, 파일 키가 공유되더라도 모든 간접 객체가 고유한 키를 갖게 됩니다.
일반적인 문제 및 해결책
encrypt_aes_cbc/decrypt_aes_cbc “AES key must be 16, 24, or 32 bytes” 오류가 발생합니다. 키 인수는 원시 비밀번호 문자열이 아니라 실제 AES 키여야 합니다. 다음으로 생성하십시오 os.urandom(16 | 24 | 32), 또는 먼저 다음을 사용하여 파생하십시오 compute_user_key_v4 (Revisions 2-4) 또는 compute_hash_v5 (개정 5/6).
verify_password_v4 returns None 예외를 발생시키는 대신. 이것은 비밀번호가 일치하지 않음을 의미합니다 — 잡아야 할 오류 조건이 아니라 확인해야 할 결과일 뿐입니다. 또한 다음을 확인하십시오 permissions, file_id, key_length, 그리고 revision 은(는) 문서가 처음 계산될 때 사용된 정확히 동일한 값들 o_value and u_value 원래 계산되었습니다; 그 중 하나라도 일치하지 않으면 조용히 잘못된 키를 생성하고 크게 오류를 발생시키지는 않습니다.
compute_owner_key_v4 소유자 비밀번호가 비어 있는 경우. Passing "" for owner_password 은(는) 사양에 정의된 동작이며 버그가 아닙니다 — 알고리즘이 사용자 비밀번호를 사용하도록 대체되므로 소유자 비밀번호와 사용자 비밀번호가 동일해집니다.
잘못된 리비전 또는 키 길이로 파생된 키들. 리비전 2는 40비트(5바이트) RC4 키를 사용하고, 리비전 3/4는 128비트(16바이트) RC4/AES 키를 사용하며, 리비전 5/6은 compute_hash_v5 대신에 compute_owner_key_v4/compute_user_key_v4 전혀 그렇지 않습니다. 이를 섞어도 예외가 발생하지 않고 단지 문서를 열 수 없는 키를 생성할 뿐입니다.
compute_hash_v5 출력이 예기치 않게 변경됩니다. 세 번째 인수 (user_key)가 해시와 섞입니다. 전달하십시오 b"" 평문 비밀번호 검증 해시가 필요할 때 명시적으로 사용하십시오. 생략하는 것은 모든 호출 지점에서 빈 값을 전달하는 것과 동일하지 않기 때문입니다.
자주 묻는 질문
왜 EncryptionUtils를 aspose_pdf에서 직접 가져올 수 없나요?
문서 수준 비밀번호 보호의 내부 구현 세부 사항이기 때문에 engine 서브패키지에 존재합니다. 고수준 공개 API 표면의 일부가 아닙니다. aspose_pdf.engine.encryption에서 명시적으로 가져오세요.
compute_hash_v5은(는) 단일 SHA-256 호출과 동일합니까?
아니요. ISO 32000-2의 Algorithm 2.B를 구현하며, 중간값에 의해 선택된 서로 다른 해시 알고리즘을 사용해 여러 라운드를 수행합니다 — 단일 해시 호출보다 고의적으로 더 비용이 많이 듭니다.
generate_file_id()은(는) 매번 동일한 바이트를 반환합니까?
아니요 — 매 호출마다 새롭고 무작위인 16바이트 값을 반환합니다. 파일 식별자가 다르면, 비밀번호가 동일하더라도 파생되는 모든 키가 변경됩니다.
암호화된 PDF의 모든 간접 객체가 하나의 키를 공유합니까?
아니요. derive_object_key은 객체 번호와 생성 번호를 파일 암호화 키와 결합하므로, 각 간접 객체는 고유한 객체별 키를 갖게 됩니다.
애플리케이션 코드가 PDF에 비밀번호를 설정하기 위해 EncryptionUtils을 직접 호출해야 합니까?
보통은 아닙니다. EncryptionUtils은 문서 수준 비밀번호 API가 기반으로 하는 저수준 엔진입니다. 대부분의 애플리케이션은 문서 수준 API을 통해 PDF를 보호해야 하며, EncryptionUtils은 맞춤형 도구나 암호화된 파일에 대한 포렌식 검사를 위해서만 사용해야 합니다.