最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to sign a PDF using Hypersecure USB with eMudhra DSC in Python? - Stack Overflow

programmeradmin5浏览0评论

I am trying to digitally sign a PDF using a hardware USB token (Hypersecure USB with eMudhra DSC) in Python. The goal is to create a visible digital signature on the PDF that can be verified by standard PDF viewers like Adobe Acrobat. I am using the following libraries:

PyKCS11 for interacting with the hardware token. pikepdf for manipulating the PDF and adding signature data.

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)

GETTING OUTPUT AS :

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.

The output of my script shows that the signing process completes successfully, and the signed PDF is saved. However:

The resulting PDF does not contain any visible signature. The PDF viewers cannot verify the digital signature. The dictionary data for the signature is being added to the PDF annotations, but the signature is not working as expected. It seems like the digital signature data is either incomplete or not being embedded correctly.

Key points:

The USB token is successfully recognized, and I can retrieve the private key using PyKCS11. Data is being hashed and signed using CKM_SHA256_RSA_PKCS. The visible annotation is added to the PDF, but the digital signature is not functional.

Environment:

Python version: (specify version, e.g., Python 3.8) Libraries: PyKCS11, pikepdf, hashlib Hardware: Hypersecure USB token with eMudhra DSC.

Any help, guidance, or working examples would be greatly appreciated.

发布评论

评论列表(0)

  1. 暂无评论