import os
import sys
from PyKCS11 import PyKCS11, CKF_SERIAL_SESSION, CKF_RW_SESSION
from asn1crypto import x509

class TokenManager:
    def __init__(self, lib_path=None):
        """
        Khởi tạo TokenManager với đường dẫn đến thư viện PKCS#11 (DLL/SO).
        Các đường dẫn phổ biến:
        - SafeNet: C:\Windows\System32\dkck201.dll
        - Viettel-CA: C:\Windows\System32\viettel-ca_p11.dll
        - VNPT-CA: C:\Windows\System32\vnpt-ca_p11.dll
        """
        self.lib_path = lib_path or self._find_default_lib()
        if not self.lib_path or not os.path.exists(self.lib_path):
            raise FileNotFoundError(f"Không tìm thấy thư viện PKCS#11 tại: {self.lib_path}")
        
        self.pkcs11 = PyKCS11()
        self.pkcs11.load(self.lib_path)

    def _find_default_lib(self):
        # Các đường dẫn DLL phổ biến cho USB Token tại Việt Nam trên Windows
        # Thêm các tên tệp DLL phổ biến từ các nhà cung cấp
        common_dlls = [
            "viettel-ca_p11.dll", "vnpt-ca_p11.dll", "fpt-ca_p11.dll",
            "bkavca_p11.dll", "smartsign_p11.dll", "ca2_p11.dll",
            "nacacent_p11.dll", "vnpt-ca_v3_p11.dll", "vsign_p11.dll",
            "dkck201.dll", "eps2003csp11.dll"
        ]
        
        search_dirs = [
            os.environ.get("SystemRoot", r"C:\Windows") + r"\System32",
            os.environ.get("SystemRoot", r"C:\Windows") + r"\SysWOW64",
            r"C:\Program Files",
            r"C:\Program Files (x86)"
        ]

        # Kiểm tra trực tiếp trong System32 và SysWOW64
        for d in search_dirs[:2]:
            for dll in common_dlls:
                path = os.path.join(d, dll)
                if os.path.exists(path):
                    return path

        # Nếu không thấy, có thể quét sâu hơn trong Program Files (nhưng việc này chậm)
        # Tạm thời chỉ liệt kê các đường dẫn tuyệt đối ổn định
        return None

    def list_slots(self):
        slots = self.pkcs11.getSlotList(tokenPresent=True)
        return slots

    def get_token_info(self, slot):
        return self.pkcs11.getTokenInfo(slot)

    def open_session(self, slot, pin):
        session = self.pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
        session.login(pin)
        return session

    def get_certificates(self, session):
        # Liệt kê các chứng chỉ từ session
        from PyKCS11.LowLevel import CKA_CLASS, CKO_CERTIFICATE, CKA_VALUE, CKA_LABEL
        objects = session.findObjects([(CKA_CLASS, CKO_CERTIFICATE)])
        cert_info = []
        for obj in objects:
            try:
                attributes = session.getAttributeValue(obj, [CKA_VALUE, CKA_LABEL])
                cert_der = bytes(attributes[0])
                label = attributes[1]
                
                # Giải mã chứng chỉ để lấy tên
                cert = x509.Certificate.load(cert_der)
                subject = cert.subject.native
                common_name = subject.get('common_name', label)
                
                cert_info.append({
                    'obj': obj,
                    'label': label,
                    'common_name': common_name,
                    'subject': subject,
                    'der': cert_der
                })
            except Exception as e:
                print(f"Bỏ qua chứng chỉ lỗi: {e}")
        return cert_info

if __name__ == "__main__":
    try:
        manager = TokenManager()
        slots = manager.list_slots()
        print(f"Tìm thấy {len(slots)} khe cắm có token.")
        for slot in slots:
            info = manager.get_token_info(slot)
            print(f"Slot {slot}: {info.label.strip()} (Nhà sản xuất: {info.manufacturerID.strip()})")
    except Exception as e:
        print(f"Lỗi: {e}")
