如何在Python中使用带eMudhra DSC的Hypersecure USB令牌生成可验证的可见PDF数字签名?
如何在Python中使用带eMudhra DSC的Hypersecure USB令牌生成可验证的可见PDF数字签名?
我最近尝试用Python结合带eMudhra DSC的Hypersecure USB硬件令牌给PDF做数字签名,目标是生成一个在Adobe Acrobat这类标准PDF查看器里能被验证的可见签名。我用了两个核心库:
PyKCS11:负责和硬件令牌进行交互,包括令牌识别、登录、私钥获取和签名操作pikepdf:负责PDF文件的读取、修改以及签名数据的嵌入
以下是我写的完整实现代码:
from csv import reader, writer from pdfrw import PdfReader, PdfWriter from pdfrw.objects import PdfDict, PdfName, PdfString, PdfArray import hashlib import PyKCS11 class TokenSigner: def __init__(self, token_library_path, pin): self.pkcs11 = PyKCS11.PyKCS11Lib() try: self.pkcs11.load(token_library_path) print("PKCS11 library loaded successfully.") except Exception as e: print(f"Error loading PKCS11 library: {e}") self.pkcs11 = None self.session = None self.token_slot = None if self.pkcs11: self.login(pin) def login(self, pin): try: slots = self.pkcs11.getSlotList(tokenPresent=True) if not slots: print("No tokens found.") return self.token_slot = slots[0] self.session = self.pkcs11.openSession(self.token_slot, PyKCS11.CKF_SERIAL_SESSION) self.session.login(pin) print("Logged in to token.") except Exception as e: print(f"Error during login: {e}") def get_private_key(self): try: private_keys = self.session.findObjects([(PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY)]) if not private_keys: print("No private keys found.") return None for private_key in private_keys: key_type = self.session.getAttributeValue(private_key, [PyKCS11.CKA_KEY_TYPE])[0] if key_type == PyKCS11.CKK_RSA: print("Found valid RSA private key.") return private_key print("No valid RSA private key found.") except Exception as e: print(f"Error retrieving private key: {e}") return None def sign(self, private_key, data): try: print(f"Signing data with private key: {private_key}") if private_key is None: print("Private key is None!") return None hash_data = hashlib.sha256(data).digest() print(f"Hash data: {hash_data.hex()}") mechanism = PyKCS11.Mechanism(PyKCS11.CKM_SHA256_RSA_PKCS, None) signed_data = self.session.sign(private_key, hash_data, mechanism) print("Data signed successfully.") return bytes(signed_data) except PyKCS11.PyKCS11Error as e: print(f"Signing error: {e}") return None def logout(self): try: if self.session: self.session.logout() self.session.closeSession() print("Logged out from token.") except Exception as e: print(f"Error logging out: {e}") from pikepdf import Pdf, Name, Array, String, Dictionary def sign_pdf(input_pdf_path, output_pdf_path, signature_text, signature_position, token_library_path, pin): try: # Initialize the signer signer = TokenSigner(token_library_path, pin) if signer is None: print("Signer initialization failed.") return private_key = signer.get_private_key() if private_key is None: print("Private key not found. Exiting.") return # Read the PDF and data for signing with open(input_pdf_path, "rb") as f: data_to_sign = f.read() # Sign the data signed_data = signer.sign(private_key, data_to_sign) if signed_data is None: print("Error signing data.") return # Open the input PDF with Pdf.open(input_pdf_path) as pdf: first_page = pdf.pages[0] # Define the signature field dictionary signature_dict = Dictionary( Type=Name("/Annot"), Subtype=Name("/Widget"), FT=Name("/Sig"), Rect=Array([ signature_position[0], signature_position[1], signature_position[0] + signature_position[2], signature_position[1] + signature_position[3] ]), T=String(signature_text), DA=String("/Helv 0 Tf 0 g"), V=Dictionary( Contents=String(signed_data.hex()) ) ) print("\n signature data = ", signature_dict) # Add the signature field to the annotations if "/Annots" not in first_page: first_page.Annots = Array() first_page.Annots.append(signature_dict) # Save the updated PDF pdf.save(output_pdf_path) print(f"PDF signed successfully and saved to: {output_pdf_path}") except Exception as e: print(f"An unexpected error occurred: {e}") finally: if signer: signer.logout() token_library_path = "HYP2003-Linux-x86_64/redist/libcastle_v2.so.1.0.0" pin = "12345678" pdf_path = "sample.pdf" output_path = "signed_sample_visible.pdf" signature_text = "JAYESH MAHATO" signature_position = (100, 50, 200, 50) sign_pdf(pdf_path, output_path, signature_text, signature_position, token_library_path, pin)
运行代码后,控制台显示所有步骤都执行成功:
PKCS11 library loaded successfully. Logged in to token. Found valid RSA private key. Signing data with private key: CKA_ALWAYS_AUTHENTICATE: False CKA_ALWAYS_SENSITIVE: True CKA_CLASS: CKO_PRIVATE_KEY CKA_DECRYPT: True CKA_DERIVE: False CKA_END_DATE: () CKA_EXTRACTABLE: False CKA_ID: (122, 171, 175, 175, 25, 237, 96, 58, 207, 130, 173, 148, 193, 195, 74, 94, 121, 110, 10, 31) CKA_KEY_TYPE: CKK_RSA CKA_LABEL: 48055270-5655-4074-92f5-5f043e5b7908 CKA_LOCAL: True CKA_MODIFIABLE: True CKA_MODULUS: (157, 159, 241, 25, 154, 190, 52, 253, 254, 145, 218, 252, 211, 2, 17, 85, 223, 229, 71, 192, 8, 62, 249, 36, 2, 41, 176, 194, 172, 106, 202, 61, 94, 96, 77, 48, 72, 93, 167, 128, 128, 175, 108, 104, 225, 68, 26, 216, 171, 110, 230, 91, 243, 102, 158, 92, 93, 20, 42, 182, 199, 212, 53, 3, 248, 60, 42, 242, 185, 150, 13, 97, 238, 226, 93, 109, 215, 61, 228, 56, 16, 204, 101, 9, 126, 237, 88, 12, 158, 154, 234, 250, 23, 201, 148, 183, 167, 93, 222, 247, 219, 232, 247, 196, 175, 34, 197, 136, 181, 50, 235, 106, 139, 92, 88, 0, 176, 30, 137, 113, 174, 66, 124, 15, 120, 21, 77, 248, 194, 49, 47, 76, 76, 174, 204, 141, 94, 213, 161, 96, 240, 123, 70, 86, 109, 231, 242, 183, 10, 108, 10, 148, 40, 125, 197, 187, 130, 57, 82, 65, 16, 100, 158, 114, 102, 0, 232, 247, 8, 47, 58, 175, 155, 86, 255, 35, 96, 213, 34, 37, 255, 189, 162, 143, 54, 92, 147, 57, 43, 167, 44, 157, 128, 231, 196, 72, 13, 218, 71, 18, 153, 122, 39, 226, 86, 173, 201, 142, 102, 63, 142, 238, 234, 27, 77, 186, 98, 143, 105, 218, 57, 118, 37, 227, 72, 150, 192, 134, 139, 91, 72, 37, 26, 164, 205, 78, 182, 130, 142, 177, 49, 189, 213, 130, 9, 219, 0, 87, 23, 223, 80, 100, 233, 176, 69, 155) CKA_NEVER_EXTRACTABLE: True CKA_PRIVATE: True CKA_PUBLIC_EXPONENT: (1, 0, 1) CKA_SENSITIVE: True CKA_SIGN: True CKA_SIGN_RECOVER: True CKA_START_DATE: () CKA_SUBJECT: () CKA_TOKEN: True CKA_UNWRAP: True CKA_WRAP_WITH_TRUSTED: False Hash data: d5ddf867002b9541fcc6ea8169a85ca2a741b6afa36448d2b389cdfc0e80bd5b Data signed successfully. signature data = pikepdf.Dictionary(Type="/Annot")({ "/DA": "/Helv 0 Tf 0 g", "/FT": "/Sig", "/Rect": [ 100, 50, 300, 100 ], "/Subtype": "/Widget", "/T": "JAYESH MAHATO", "/Type": "/Annot", "/V": { "/Contents": "7d5653390f44bf06377b2ae5264538a3d0d4e9c524d33374ce8b6ad03033ea0354de6fde1d07fbded86679b4f2ebe29a0ffb139eb47ad5c7cd04b7486a671a783936c77fde54a871a00afb818e7acf7140c1570be07597c906d7d138645d3e011fcb9da1c788ba3cc79b31029fc7de1a53cadd50983ddcbb160c7fd78f5e968a53074217f56b85f2f23292f133a9086346c33fe68f13c8c41ab8c2fbbc45bb51648553ba68e9c76bd11e3e7616a67c5bde5f09f55639c8a5f38dcf3b96e5b02eb53c1af4bd3e4ff88db36d204e00cf78867967a7c577a910576e5bf68aec8966a1f18546016fc09c56c388a50ed9ad954608bf07c0c23ff549e10477138e413e" } }) PDF signed successfully and saved to: signed_sample_visible.pdf Logged out from token.
但实际生成的PDF存在两个关键问题:
- 签名完全不可见:我设置的签名文本
JAYESH MAHATO和指定的位置没有在PDF中任何地方显示 - 签名无法被验证:使用Adobe Acrobat等标准PDF查看器打开时,系统提示无法验证该数字签名
我已经确认USB令牌能被正常识别,私钥也能正确获取,签名数据也生成了,但看起来签名数据要么不完整,要么嵌入的方式不符合PDF签名规范。
备注:内容来源于stack exchange,提问作者kishan Kumar
相关产品推荐
相关产品推荐

