# ProjectSign - PDF Digital Signature Component

Module ký số PDF chuyên nghiệp, thiết kế để dễ dàng tích hợp vào các dự án Python khác. Hoạt động trên Windows với giao diện Native (không cần thư viện GUI nặng).

## 🌟 Tính Năng Chính
1. **Windows Certificate Store**: Ký số bằng chứng chỉ từ USB Token thông qua kho chứng chỉ của Windows. Cắm USB Token và ký, không cần cấu hình phức tạp.
2. **PKCS#11 Direct Signer (Nâng cao)**: Ký trực tiếp qua thư viện PKCS#11 của Token (Viettel-CA, VNPT-CA...), không qua Windows Store.
3. **LTV Timestamping**: Tự động nhúng Time-Stamp từ các Server TSA (Time Stamping Authority) công cộng, giúp chữ ký có giá trị pháp lý và hợp lệ lâu dài ngay cả khi chứng thư đã hết hạn.
4. **Layout Chữ Ký Tùy Chỉnh (Acrobat Premium Style)**: 
   - Hỗ trợ chèn Logo công ty (hoặc hình dạng Minimalist nếu không chèn logo).
   - Tự động trích xuất Tên, Công ty, MST (Mã Số Thuế) thiết lập tự động co giãn font chữ cho vừa khung ký.
5. **Giao Diện Chọn File/Cert Native**: Gọi trực tiếp hộp thoại chuẩn của Windows (File Picker, Certificate Picker) mà không phụ thuộc vào PyQt/Tkinter v.v.

---

## 📂 Kiến Trúc Files

| File | Chức năng | Phụ thuộc |
|---|---|---|
| `sign_pdf.py` | Lõi xử lý PDF (tìm từ khóa, vẽ layout chữ ký, tạo signature field, ghi file PDF). | `pyhanko`, `Pillow`, `pdfminer.six` |
| `win_signer.py` | Giao tiếp với thiết bị phần cứng (USB Token) thông qua [1] Windows CryptoAPI (`ncrypt.dll`, `crypt32.dll`) hoặc [2] PKCS#11 (`PyKCS11`). Lấy Private Key để ký Hash. | `asn1crypto`, `PyKCS11` (nếu dùng 2) |
| `win_file_picker.py` | Mở hộp thoại Open/Save file chuẩn của Windows. | Thuần `ctypes` |
| `token_manager.py` | Quản lý USB Token chuyên sâu thông qua chuẩn PKCS#11 (Liệt kê slot, mở session,...). Thường Support cho CA Việt Nam. | `PyKCS11`, `asn1crypto` |
| `sign.yaml` | File cấu hình: Cài đặt Hashing, cấu hình TSA Timestamp Url, Lựa chọn PKCS11 vs Windows Store. | `PyYAML` |
| `main.py` | Code ví dụ để chạy Demo độc lập. Hoặc tích hợp toàn bộ luồng. | |
| `requirements.txt`| Chứa các thư viện Python cài đặt bằng pip. | |
| `assets/` | Chứa file `logo.png` dùng để chèn nền và biểu tượng công ty. | |

---

## 🚀 Cách Tích Hợp Vào Dự Án Khác

### 1. Cài đặt thư viện
Copy toàn bộ thư mục `sign` vào dự án của bạn, sau đó cài đặt environment:
```bash
pip install -r requirements.txt
```

### 2. Ví dụ cách gọi từ Code của bạn

Giả sử ứng dụng của bạn muốn ký file PDF `document.pdf` ngay dòng chữ "Bên A":

```python
import os
from win_signer import select_signer
from sign_pdf import PDFSigner

# 1. Để người dùng chọn chứng chỉ từ USB Token qua giao diện Windows:
print("Đang chờ chọn chứng chỉ...")
signer_obj = select_signer()

if not signer_obj:
    print("Người dùng đã hủy chọn chứng chỉ.")
    exit()

# 2. Thực hiện hàm ký
input_pdf = "document.pdf"
output_pdf = "document_signed.pdf"
keyword = "Bên A"
tsa_url = "http://timestamp.digicert.com" # (Tùy chọn) Để lấy LTV Timestamp

signer_engine = PDFSigner(None, None)

try:
    signer_engine.sign_pdf_native(
        in_file=input_pdf,
        out_file=output_pdf,
        keyword=keyword,
        signer=signer_obj,       # Obj mang thông tin khóa ký
        offsets=(-45, -15),      # Điều chỉnh vị trí trục X, Y so với keyword
        ts_url=tsa_url,          # URL timestamp (None nếu tắt)
        use_logo=True            # Có chèn hình ảnh Logo không (False = Khung Tối giản)
    )
    print(f"✅ Ký thành công. Lưu tại: {output_pdf}")
except Exception as e:
    print(f"❌ Lỗi khi ký: {e}")
finally:
    # Giải phóng resource USB Token
    del signer_obj
```

*(Bạn có thể tham khảo `main.py` để xem cách vòng lặp bắt lỗi và retry khi người dùng quên cắm USB token)*.

---

## ⚙️ Cấu hình `sign.yaml`
Mặc định tôi đã cấu hình file `sign.yaml` với Time-stamping:
```yaml
timestamp:
  enabled: true
  url: "http://timestamp.digicert.com"  # Miễn phí
```
Nếu bạn bật cờ này, khi chạy `main.py`, chương trình sẽ tự gắn timestamp vào document.

---

## 🛠 Những Lưu ý Kỹ Thuật
1. **Xử lý Font Tiếng Việt**: Để chữ ký không bị lỗi font rác (như `&D&1...`), class `WindowsStoreSigner` và `PKCS11Signer` đã được tự động _loại bỏ dấu (`D` thay vì `Đ`)_ ở tầng metadata. Đồng thời Layout vẽ hình sử dụng ảnh Rasterizer `Pillow` với font Arial chuẩn Windows để đảm bảo trên PDF bạn thấy đúng 100% tiếng Việt.
2. **Ký Hàng Loạt (Batch Signing)**: Lấy instance của `signer_obj = select_signer()` MỘT LẦN ở ngoài vòng lặp. Truyền object này vào lặp gọi `sign_pdf_native()` nhiều lần. KHÔNG mở select_signer cho mỗi file.
3. **Hardware Recovery**: Khi gặp lỗi `0x80090016` hoặc `0x80090020`, Token bị lỏng hoặc driver bận. Tham khảo code Catch Try Retry trong `main.py` để làm popup Retry thay vì làm crash phần mềm.

---

## 🔍 Tiện Ích Mở Rộng: Kiểm tra PDF đã ký số
Đi kèm trong package `sign` là module `check_sign.py`, cho phép bạn kiểm tra nhanh xem một file PDF đã được ký số hay chưa và trích xuất thông tin người ký trong file.

### Sử dụng từ Code:
```python
from check_sign import is_pdf_signed, get_signature_info

pdf_file = "tai_lieu.pdf"

# Chỉ kiểm tra mộc/chữ ký (Trẻ/False)
if is_pdf_signed(pdf_file):
    print("File đã được ký!")
    
    # Lấy thông tin chi tiết các chữ ký (Tên người ký, Thời gian)
    chi_tiet = get_signature_info(pdf_file)
    print("Danh sách chữ ký:", chi_tiet)
else:
    print("File chưa được ký.")
```

### Sử dụng từ Terminal (Dùng thử nhanh):
Bạn có thể kéo thả file PDF vào terminal để kiểm tra trực tiếp:
```bash
python sign/check_sign.py "duong/dan/toi/file_pdf.pdf"
```
