我正在尝试通过在页面中搜索特定文本“授权签名”,使用 3 类 USB 令牌对 pdf 文档进行数字签名,当我必须对 pdf 进行一次签名时,它工作得非常好。但在一个场景中,我遇到了多页文档,我必须在每一页上签名,在签署 pdf 后,当我尝试在 Adobe pdf 阅读器中验证签名时,它只验证了我的第一个签名,如下面的屏幕截图所示
而且我看不到第二页和第三页的注释。
这是我的代码,
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()
我哪里出错了?
我不是一个固定的用户,我不明白这部分:
# 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)。