使用 Python endesive 对 PDF 中的多个页面进行数字签名时出错

问题描述 投票:0回答:1

我正在尝试通过在页面中搜索特定文本“授权签名”,使用 3 类 USB 令牌对 pdf 文档进行数字签名,当我必须对 pdf 进行一次签名时,它工作得非常好。但在一个场景中,我遇到了多页文档,我必须在每一页上签名,在签署 pdf 后,当我尝试在 Adobe pdf 阅读器中验证签名时,它只验证了我的第一个签名,如下面的屏幕截图所示

enter image description here

而且我看不到第二页和第三页的注释。

这是我的代码,

def post(self, request):
        serializer = PDFSignSerializer(data=request.data)
        if serializer.is_valid():
            data = serializer.validated_data.get('pdf_base64')

            try:
                pdf_data = base64.b64decode(data)
            except Exception as e:
                return Response({'error': f'Failed to decode base64 PDF data: {e}'}, status=status.HTTP_400_BAD_REQUEST)

            # Search for text in the PDF and retrieve coordinates
            text_to_find = 'Authorised Signatory'  # Text to search for (modify as needed)
            text_positions = self.find_text_in_pdf(pdf_data, text_to_find)

            if not text_positions:
                return Response({'error': 'No position found for signature'}, status=status.HTTP_400_BAD_REQUEST)

            # Get the current UTC time using timezone-aware objects
            current_utc_time = datetime.datetime.now(datetime.timezone.utc)

            # Calculate the Indian time by adding the UTC offset of +5:30
            indian_time = current_utc_time + datetime.timedelta(hours=5, minutes=30)

            # Format the Indian time string in 24-hour format
            indian_time_str = indian_time.strftime('%Y-%m-%d %H:%M:%S') + ' UTC+5:30'

            # Initialize the signer
            #In above code the settings.DLLPATH is the path of the file - eps2003csp11v2.dll in windows OS
            clshsm = Signer(settings.DLLPATH)  # Adjust this according to your settings

            # Prepare signing data structure
            date = indian_time - datetime.timedelta(hours=11)
            date = date.strftime('%Y%m%d%H%M%S+00\'00\'')
            dct = {
                "sigflags": 3,
                "sigbutton": True,
                "contact": f'Digitally signed\nDate: {indian_time_str}',
                "location": 'India',
                "signingdate": date.encode(),
                "reason": 'Approved',
                "text": {
                    'wraptext': True,
                    'fontsize': 6,
                    'textalign': 'left',
                    'linespacing': 1,
                },
                "signature_appearance": {
                    'background': r'C:\Users\Guest\Downloads\check_mark.png',
                    'labels': False,
                    'display': 'contact'.split(','),
                },
            }

            for position in text_positions:
                try:
                    # Reload the PDF document to avoid xref table issues
                    signed_pdf_data = pdf_data
                    
                    dct["sigpage"] = position['page_number']
                    dct["signaturebox"] = (
                        position['x0'] - 10,
                        position['page_height'] - position['y0'],
                        position['x1'] + 10,
                        position['page_height'] - position['y0'] + 60
                    )

                    signed_pdf_data = pdf.cms.sign(signed_pdf_data, dct, None, None, [], 'sha256', clshsm)
                    pdf_data = pdf_data + signed_pdf_data
                    # Return the signed PDF data after each signing operation
                    
                    print(base64.b64encode(pdf_data).decode())
                    
                except Exception as e:
                    return Response({'error': f'Failed to sign at position {position}: {e}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            # Prepare response
            response_data = {
                        'message': 'PDF signed successfully.',
                        'signed_pdf_base64': base64.b64encode(pdf_data).decode()
                    }
            return Response(response_data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

另外签名者类代码如下:

class Signer(hsm.HSM):
    def certificate(self):
        self.login("Token", "password")
        keyid = [0x5e, 0x9a, 0x33, 0x44, 0x8b, 0xc3, 0xa1, 0x35, 0x33, 0xc7, 0xc2, 0x02, 0xf6, 0x9b, 0xde, 0x55, 0xfe, 0x83, 0x7b, 0xde]
        keyid = bytes(keyid)
        try:
            pk11objects = self.session.findObjects([(PK11.CKA_CLASS, PK11.CKO_CERTIFICATE)])
            all_attributes = [
                PK11.CKA_VALUE,
                PK11.CKA_ID,
            ]

            for pk11object in pk11objects:
                try:
                    attributes = self.session.getAttributeValue(pk11object, all_attributes)
                except PK11.PyKCS11Error as e:
                    continue

                attrDict = dict(list(zip(all_attributes, attributes)))
                cert = bytes(attrDict[PK11.CKA_VALUE])
                return bytes(attrDict[PK11.CKA_ID]), cert
        finally:
            self.logout()
        return None, None

    def sign(self, keyid, data, mech):
        self.login("Token", "password")
        try:
            privKey = self.session.findObjects([(PK11.CKA_CLASS, PK11.CKO_PRIVATE_KEY)])[0]
            mech = getattr(PK11, 'CKM_%s_RSA_PKCS' % mech.upper())
            sig = self.session.sign(privKey, data, PK11.Mechanism(mech, None))
            return bytes(sig)
        finally:
            self.logout()

我哪里出错了?

python django pdf digital-signature
1个回答
0
投票

我不是一个固定的用户,我不明白这部分:

                    # Reload the PDF document to avoid xref table issues
                    signed_pdf_data = pdf_data

每个签名都会向 PDF 文件添加一个新的 PDF 版本。

我认为

pdf_data
没有重读,所以第二个签名,应该签署第二个PDF版本,只签署第一个PDF版本的数据。

阅读完整的源代码,它似乎使用以下行添加了“pdf注释”:

    if box is not None:
        self.addAnnotation(cert, udct, box, page0ref, obj13, obj13ref, new_13)

注释是附加到 PDF 文件并由 PDF 阅读器显示、覆盖原始页面的可视化内容。

如果 endesive 可以为每个签名创建多个注释(几个框),那就太好了,那么您只需签名一次,但似乎不能。您可以尝试手动添加多个注释。

如果您采用每页一个签名的方法,则每次都必须启动签名过程(重新阅读新的 PDF)。

© www.soinside.com 2019 - 2024. All rights reserved.