PDF 잠금 해제·비밀번호 제거: 원리부터 실무까지
PDF 암호화 방식(사용자 암호 vs 소유자 암호), 온라인·오프라인 도구, 법적 고려사항을 종합적으로 분석하고 각 상황별 최적 솔루션을 제시합니다.
1. PDF 암호화 유형 및 제거 난이도
2. 온라인 도구 (무료, 설치 불필요)
3. 데스크톱 소프트웨어
4. CLI 도구 (배치 처리)
4.1 qpdf (가장 강력)
#!/bin/bash
# qpdf로 소유자 암호 제거 (사용자 암호 불필요)
qpdf --decrypt input.pdf output.pdf
# 사용자 암호로 파일 해제
qpdf --password=mypassword --decrypt input.pdf output.pdf
# 배치 처리
for file in *.pdf; do
qpdf --decrypt "$file" "unlocked_$file"
done
# 진행 상황 표시
for file in *.pdf; do
echo "처리 중: $file"
qpdf --decrypt "$file" "unlocked_$file" && echo "✓ $file"
done
4.2 pdftk (간단)
#!/bin/bash
# pdftk로 소유자 암호 제거
pdftk input.pdf output output.pdf
# 사용자 암호로 해제
pdftk input.pdf input_pw mypassword output output.pdf
# 배치 처리
for file in *.pdf; do
pdftk "$file" output "unlocked_$file"
done
4.3 Ghostscript (고급)
#!/bin/bash
# Ghostscript로 암호 제거 후 재저장
gs -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -sOutputFile=output.pdf input.pdf
# 사용자 암호 필요 시
gs -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -sInputPassword=mypassword -sOutputFile=output.pdf input.pdf
5. Python 라이브러리
5.1 pikepdf (권장)
import pikepdf
# 소유자 암호 제거 (가장 간단)
with pikepdf.open('input.pdf') as pdf:
pdf.save('output.pdf')
# 사용자 암호로 해제
with pikepdf.open('input.pdf', password='mypassword') as pdf:
pdf.save('output.pdf')
# 배치 처리
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def unlock_pdf(pdf_file):
try:
with pikepdf.open(pdf_file) as pdf:
output_file = pdf_file.parent / f"unlocked_{pdf_file.name}"
pdf.save(output_file)
return f"✓ {pdf_file.name}"
except Exception as e:
return f"✗ {pdf_file.name}: {e}"
pdf_files = list(Path('/home/user/pdfs').glob('*.pdf'))
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(unlock_pdf, pdf_files))
for result in results:
print(result)
5.2 PyPDF2
from PyPDF2 import PdfReader, PdfWriter
# 소유자 암호 제거
reader = PdfReader('input.pdf')
writer = PdfWriter()
# 모든 페이지 복사
for page in reader.pages:
writer.add_page(page)
# 암호 제거 후 저장
with open('output.pdf', 'wb') as f:
writer.write(f)
# 사용자 암호로 해제 (더 복잡함)
def unlock_with_password(input_pdf, password, output_pdf):
reader = PdfReader(input_pdf)
if reader.is_encrypted:
if not reader.decrypt(password):
raise ValueError("암호가 잘못됨")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
with open(output_pdf, 'wb') as f:
writer.write(f)
unlock_with_password('input.pdf', 'mypassword', 'output.pdf')
5.3 qpdf Python 바인딩
import subprocess
import os
def unlock_pdf_qpdf(input_pdf, output_pdf, password=None):
"""qpdf를 사용한 암호 제거 (가장 안정적)"""
cmd = ['qpdf']
if password:
cmd.extend([f'--password={password}'])
cmd.extend(['--decrypt', input_pdf, output_pdf])
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
print(f"✓ {input_pdf} → {output_pdf}")
return True
except subprocess.CalledProcessError as e:
print(f"✗ 오류: {e.stderr}")
return False
# 사용
unlock_pdf_qpdf('input.pdf', 'output.pdf')
6. 암호화 방식 진단 및 제거
6.1 암호화 유형 확인
from PyPDF2 import PdfReader
import subprocess
def detect_encryption_type(pdf_file):
"""PDF 암호화 유형 감지"""
# PyPDF2로 확인
reader = PdfReader(pdf_file)
result = {
"file": pdf_file,
"is_encrypted": reader.is_encrypted,
"encryption_method": "Unknown"
}
if reader.is_encrypted:
# qpdf로 상세 정보 확인
try:
qpdf_output = subprocess.run(
['qpdf', '--check', pdf_file],
capture_output=True,
text=True
).stdout
if '40-bit' in qpdf_output:
result["encryption_method"] = "40-bit RC4 (Very Weak)"
elif '128-bit' in qpdf_output or 'RC4' in qpdf_output:
result["encryption_method"] = "128-bit RC4 (Weak)"
elif 'AES' in qpdf_output and '256' in qpdf_output:
result["encryption_method"] = "256-bit AES (Strong)"
elif 'AES' in qpdf_output:
result["encryption_method"] = "128-bit AES (Medium)"
# 암호화 유형
if 'User password' in qpdf_output:
result["has_user_password"] = True
if 'Owner password' in qpdf_output:
result["has_owner_password"] = True
except Exception as e:
result["error"] = str(e)
return result
# 사용
info = detect_encryption_type('input.pdf')
print(f"파일: {info['file']}")
print(f"암호화됨: {info['is_encrypted']}")
print(f"암호화 방식: {info['encryption_method']}")
6.2 자동 제거
def auto_unlock_pdf(input_pdf, output_pdf, common_passwords=None):
"""자동으로 PDF 잠금 해제 시도"""
import pikepdf
# 기본 비밀번호 리스트
if common_passwords is None:
common_passwords = [
'', '123456', 'password', 'admin', '12345678',
'qwerty', '123123', '1q2w3e4r', '666666', '000000'
]
# 1. 암호 없이 시도
try:
with pikepdf.open(input_pdf) as pdf:
pdf.save(output_pdf)
print(f"✓ 암호 없이 해제 성공")
return True
except Exception as e:
print(f"암호 필요: {e}")
# 2. 일반 비밀번호로 시도
for password in common_passwords:
try:
with pikepdf.open(input_pdf, password=password) as pdf:
pdf.save(output_pdf)
print(f"✓ 비밀번호 '{password}'로 해제 성공")
return True
except:
pass
print(f"✗ 자동 해제 실패")
return False
auto_unlock_pdf('input.pdf', 'output.pdf')
7. 법적 고려사항
언제 PDF 잠금 해제가 합법인가?
8. 성능 비교
9. 배치 처리 (대량 파일)
#!/usr/bin/env python3
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import subprocess
import time
def unlock_batch(input_dir, output_dir, num_workers=4):
"""대량 PDF 잠금 해제"""
Path(output_dir).mkdir(exist_ok=True)
pdf_files = list(Path(input_dir).glob('*.pdf'))
print(f"🔓 {len(pdf_files)}개 파일 해제 시작 ({num_workers} workers)")
def unlock_single(pdf_file):
try:
output_file = Path(output_dir) / pdf_file.name
# qpdf 사용 (가장 안정적)
subprocess.run([
'qpdf', '--decrypt',
str(pdf_file), str(output_file)
], check=True, capture_output=True)
return f"✓ {pdf_file.name}"
except Exception as e:
return f"✗ {pdf_file.name}: {e}"
start_time = time.time()
with ThreadPoolExecutor(max_workers=num_workers) as executor:
results = list(executor.map(unlock_single, pdf_files))
elapsed = time.time() - start_time
successful = sum(1 for r in results if r.startswith("✓"))
print(f"
✅ 완료")
print(f" 성공: {successful}/{len(pdf_files)}")
print(f" 시간: {elapsed:.1f}초 ({len(pdf_files)/elapsed:.1f} 파일/초)")
return results
# 사용
results = unlock_batch('/home/user/locked_pdfs', '/home/user/unlocked_pdfs')
10. FAQ
- 가장 빠른 방법? qpdf CLI (1초)
- 가장 안전한 온라인 도구? Sejda (256-bit AES 지원)
- Python에서 추천? pikepdf (가장 안정적)
- 배치 처리 최고? qpdf + ThreadPoolExecutor
- 타인의 PDF 해제 가능? 법적으로 문제 될 수 있음
- 256-bit AES 제거? 거의 불가능 (무작정 공격 필요)
- 사용자 암호 제거? 소유자 암호 필요한 경우 어려움
11. 관련 자료
댓글
댓글 쓰기