Embed Signed hash and certificate to PDF
shyjuk opened this issue · 12 comments
Is there any way to sign a PDF using endesive if I have pdf file, signed hash of pdf file and certificate with me.
Unfortunately no.
@m32 Thank you for your reply. I will explain the question in another way. I'm using a signing service provider's web service which accepts pdf file hash as input and it releases signed hash and public certificate as output. I'm looking for a library to embed the signed hash and certificate to the pdf. Does endesive support this? or do you have a recommend any other library?
I do not know of a library that would allow to suspend the signing process and return to it with new parameters.
AWS, Google and certainly other big players have their HSM services in the cloud and they are probably not expensive services, so I would be interested in them.
@m32
In pdf-sign-cms-hash.py we have used the below configuration.
pdfname = 'pdf.pdf' if len (sys.argv) > 1: pdfname = sys.argv[1] try: config = open(pdfname + ".json", "rt").read() config = json.loads(config) except FileNotFoundError: config = { 'when': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'certificate': open("pubkey.pem", "rt").read(), 'signed_bytes': open("response.bin", 'rb').read(), 'tosign': open(pdfname, "rb").read(), }
File "pdf-sign-cms-hash.py", line 97, in main
signed_bytes = base64.decodebytes(signed_bytes.encode('ascii'))
AttributeError: 'bytes' object has no attribute 'encode'
Please find the certificate,signed_bytes(response.bin) and tosign(pdf.pdf) from the below links
Check the compatibility of variable types in the json file.
In my signed_bytes it's a binary base64 encoded, in yours it's just a binary value.
Also show the entire program, not just a piece that is not in my code.
pdf.pdf.json looks like:
{
"when": "2022-01-16 00:47:00",
"certificate": "-----BEGIN CERTIFICATE-----\nMIICljCCAYACA2ZmkDALBgkqhkiG9w0BAQswETEPMA0GA1UEAwwGaHNtIENBMB4X\nDTIxMDEyNTA2NDEyMFoXDTIyMDEyNjA2NDEyMFowFTETMBEGA1UEAwwKaHNtIFVT\nRVIgMTCCA
SAwCwYJKoZIhvcNAQEBA4IBDwAwggEKAoIBAQCX6aFBlzQuX77NBDEa\nTRXhlfoTpytEMbf3wi63QReQvX4lYxnlfK5AVBZGdI9FL2q05aTIwbwTCVKzB+4t\nCvpR6mr+3r2Dlmf8+fKPqtpMGBEGkalLZ8z7zM3orbCR+hNZTqD8G2KM0RMK/o/K\n4
eAel/qxAr9HZKMUL9fsPMuxb3MIP7JjgBwuwn4uwWy8FN4fHWStNrVI+08CTZdh\n/c9xl+kxJwem5xShj9xPfrenvj4BX7yClCck3CtuM3lgMRKP1VKB+/NyrjEwJR8x\ntxXjKJcVJd8Iq5WRorMhLsTyKRAM6jsTiz030iVvO35RW6wTEtYtInwLdyR
clI6K\nNYrxAgMBAAEwCwYJKoZIhvcNAQELA4IBAQAjw2ePtK0mwTI53CaZ7wkdIjpV8dvc\n1c4i/IKQjD/jvhGVlGCm5hmP0BMdmVQgaR7w1o1o8m4raaMVEvpTTo/vQCTlXhPs\ntngIeAJTqwfrh4hqdF+EDgqFFHc7yyjJij8aventUN7yTEt1x15
Uq/UdwSdvuE5s\nQgJ0NMzUDZKf3W7UFx7TT3hVQ4Wibq92AconMAkR5hP8rf3eWSp5WLwLrisnpWwR\nAwlAyTbb2e3k/ufAlIyIiqTQjw0Q8aA24aiNfpwPNiw68Y8Kmgl4EWvzKL2DhF/M\nGvnOzkNLU6lIN4FmaEeFOtJnCZdX/EuLKoJn4h8P7Vs
66uwtzyOzc631\n-----END CERTIFICATE-----\n",
"signed_bytes": "fVbU2JsyvgrJBs5+YyvmbWiH9TbuLxDbr41N/rRrVn5sPqKBJu2pvBRrD2WOYkePH5h/Z/bNGz3ZtiX6boE6isBl/qsYUxtm+IvwvOUiaRaCYcF21y+TJHeMYVzb7mkB8JUeu/FYqZAaWlz7ybRM6MgKS7da1TMS46jJPjdhb
9gEfqswLoCbdVWuMAAujGLq8ab6J+RhekuEjrj3j8NCAsXNO9xNij24QV6vRuBLtdwt/7CUFU/p4q7J9B3Vuw7nLe+29G9C/Y3E3/FN3ncK3bCOHLcbIt/Q8sIGFE/s5+JMJ2FclNRvlz4CbQ6bIrk3PRJ+vS9fLJ7Bgm5Vff17Uw==",
"tosign": "MWkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwMTE2MDA0NzAwWjAvBgkqhkiG9w0BCQQxIgQgk+Lqwj9OWvjBbgXQLZvj1GnoFPvmDAZXNt29LwzHt00="
}
I have updated pdf-sign-cms-hash.py as shown below, but I have received the below error.
-----Error---------
datas = cls.sign(datau, dct, None, None, [], "sha256", clshsm, mode="sign")
TypeError: sign() missing 1 required positional argument: 'timestampurl'
-----------pdf-sign-cms-hash.py-------------
#!/usr/bin/env vpython3
import sys
import datetime
import base64
import json
from asn1crypto import cms, core, util
from endesive import pdf
class Signer:
def init(self, cert, sig, tosign):
self.cert = cert
self.sig = sig
self.tosign = tosign
self.mech = None
def certificate(self):
return 1, self.cert
def sign(self, keyid, data, mech):
if self.tosign:
assert self.tosign == data
self.tosign = data
self.mech = mech
if self.sig is None:
sig = None
if mech == "sha256":
sig = b"\0" * 256
return sig
return self.sig
def main():
def attrs(signed_value):
result = [
cms.CMSAttribute(
{"type": cms.CMSAttributeType("content_type"), "values": ("data",)}
),
cms.CMSAttribute(
{
"type": cms.CMSAttributeType("message_digest"),
"values": (signed_value,),
}
),
cms.CMSAttribute(
{
"type": cms.CMSAttributeType("signing_time"),
"values": (cms.Time({"utc_time": core.UTCTime(signed_time)}),),
}
),
]
return result
dct = {
"sigflags": 3,
"sigpage": 0,
"sigbutton": True,
"contact": "mak@trisoft.com.pl",
"location": "Szczecin",
"reason": "Dokument podpisany cyfrowo",
"signature": "Dokument podpisany cyfrowo",
"signaturebox": (0, 0, 100, 100),
"aligned": 16384,
"attrs": attrs,
"newid": "1",
}
pdfname = 'pdf.pdf'
if len (sys.argv) > 1:
pdfname = sys.argv[1]
try:
config = open(pdfname + ".json", "rt").read()
config = json.loads(config)
except FileNotFoundError:
config = {
'when': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
'certificate': '-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----\n',
'signed_bytes': 'kKEWZUUVMAS45mziBJHFWAhB5cin2n7qVcq+zOpTY5QBoAoD4RIbh1Eqth/p82RQZY+v0O9DRQ6gfGjHMLh7hMZ/4WdmMIQw2QJZBpbRcVYHolvTr88AcNTWCCsPclF6DdbQssuVBQCRM81tz+0gGojPEnB1Y50kaGIs0JSRrWT/5UvMpOrNqdsuRvkfP34N/hsqDciSh4MXn5D/CltgYQ73wR2HlHAjTIbiGLDCATIXdgiKFbz2Q8PfVzpQ7slWiwR/+SPXBOP4DxsPuxDe0he99VfuWLMoegweiiHyKVVKQnwnkLprOkrhK9LibiatlFTpP5xzTwDbryUDTdS7sQ==',
'tosign': 'JVBERi0xLjMNCiXi48/TDQoNCjEgMCBvYmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL091dGxpbmVzIDIgMCBSDQovUGFnZXMgMyAwIFINCj4+DQplbmRvYmoNCg0KMiAwIG9iag0KPDwNCi9UeXBlIC9PdXRsaW5lcw0KL0NvdW50IDANCj4+DQplbmRvYmoNCg0KMyAwIG9iag0KPDwNCi9UeXBlIC9QYWdlcw0KL0NvdW50IDINCi9LaWRzIFsgNCAwIFIgNiAwIFIgXSANCj4+DQplbmRvYmoNCg0KNCAwIG9iag0KPDwNCi9UeXBlIC9QYWdlDQovUGFyZW50IDMgMCBSDQovUmVzb3VyY2VzIDw8DQovRm9udCA8PA0KL0YxIDkgMCBSIA0KPj4NCi9Qcm9jU2V0IDggMCBSDQo+Pg0KL01lZGlhQm94IFswIDAgNjEyLjAwMDAgNzkyLjAwMDBdDQovQ29udGVudHMgNSAwIFINCj4+DQplbmRvYmoNCg0KNSAwIG9iag0KPDwgL0xlbmd0aCAxMDc0ID4+DQpzdHJlYW0NCjIgSg0KQlQNCjAgMCAwIHJnDQovRjEgMDAyNyBUZg0KNTcuMzc1MCA3MjIuMjgwMCBUZA0KKCBBIFNpbXBsZSBQREYgRmlsZSApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY4OC42MDgwIFRkDQooIFRoaXMgaXMgYSBzbWFsbCBkZW1vbnN0cmF0aW9uIC5wZGYgZmlsZSAtICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjY0LjcwNDAgVGQNCigganVzdCBmb3IgdXNlIGluIHRoZSBWaXJ0dWFsIE1lY2hhbmljcyB0dXRvcmlhbHMuIE1vcmUgdGV4dC4gQW5kIG1vcmUgKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NTIuNzUyMCBUZA0KKCB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDYyOC44NDgwIFRkDQooIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjE2Ljg5NjAgVGQNCiggdGV4dC4gQW5kIG1vcmUgdGV4dC4gQm9yaW5nLCB6enp6ei4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNjA0Ljk0NDAgVGQNCiggbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDU5Mi45OTIwIFRkDQooIEFuZCBtb3JlIHRleHQuIEFuZCBtb3JlIHRleHQuICkgVGoNCkVUDQpCVA0KL0YxIDAwMTAgVGYNCjY5LjI1MDAgNTY5LjA4ODAgVGQNCiggQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA1NTcuMTM2MCBUZA0KKCB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBFdmVuIG1vcmUuIENvbnRpbnVlZCBvbiBwYWdlIDIgLi4uKSBUag0KRVQNCmVuZHN0cmVhbQ0KZW5kb2JqDQoNCjYgMCBvYmoNCjw8DQovVHlwZSAvUGFnZQ0KL1BhcmVudCAzIDAgUg0KL1Jlc291cmNlcyA8PA0KL0ZvbnQgPDwNCi9GMSA5IDAgUiANCj4+DQovUHJvY1NldCA4IDAgUg0KPj4NCi9NZWRpYUJveCBbMCAwIDYxMi4wMDAwIDc5Mi4wMDAwXQ0KL0NvbnRlbnRzIDcgMCBSDQo+Pg0KZW5kb2JqDQoNCjcgMCBvYmoNCjw8IC9MZW5ndGggNjc2ID4+DQpzdHJlYW0NCjIgSg0KQlQNCjAgMCAwIHJnDQovRjEgMDAyNyBUZg0KNTcuMzc1MCA3MjIuMjgwMCBUZA0KKCBTaW1wbGUgUERGIEZpbGUgMiApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY4OC42MDgwIFRkDQooIC4uLmNvbnRpbnVlZCBmcm9tIHBhZ2UgMS4gWWV0IG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NzYuNjU2MCBUZA0KKCBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSB0ZXh0LiBBbmQgbW9yZSApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY2NC43MDQwIFRkDQooIHRleHQuIE9oLCBob3cgYm9yaW5nIHR5cGluZyB0aGlzIHN0dWZmLiBCdXQgbm90IGFzIGJvcmluZyBhcyB3YXRjaGluZyApIFRqDQpFVA0KQlQNCi9GMSAwMDEwIFRmDQo2OS4yNTAwIDY1Mi43NTIwIFRkDQooIHBhaW50IGRyeS4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gQW5kIG1vcmUgdGV4dC4gKSBUag0KRVQNCkJUDQovRjEgMDAxMCBUZg0KNjkuMjUwMCA2NDAuODAwMCBUZA0KKCBCb3JpbmcuICBNb3JlLCBhIGxpdHRsZSBtb3JlIHRleHQuIFRoZSBlbmQsIGFuZCBqdXN0IGFzIHdlbGwuICkgVGoNCkVUDQplbmRzdHJlYW0NCmVuZG9iag0KDQo4IDAgb2JqDQpbL1BERiAvVGV4dF0NCmVuZG9iag0KDQo5IDAgb2JqDQo8PA0KL1R5cGUgL0ZvbnQNCi9TdWJ0eXBlIC9UeXBlMQ0KL05hbWUgL0YxDQovQmFzZUZvbnQgL0hlbHZldGljYQ0KL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcNCj4+DQplbmRvYmoNCg0KMTAgMCBvYmoNCjw8DQovQ3JlYXRvciAoUmF2ZSBcKGh0dHA6Ly93d3cubmV2cm9uYS5jb20vcmF2ZVwpKQ0KL1Byb2R1Y2VyIChOZXZyb25hIERlc2lnbnMpDQovQ3JlYXRpb25EYXRlIChEOjIwMDYwMzAxMDcyODI2KQ0KPj4NCmVuZG9iag0KDQp4cmVmDQowIDExDQowMDAwMDAwMDAwIDY1NTM1IGYNCjAwMDAwMDAwMTkgMDAwMDAgbg0KMDAwMDAwMDA5MyAwMDAwMCBuDQowMDAwMDAwMTQ3IDAwMDAwIG4NCjAwMDAwMDAyMjIgMDAwMDAgbg0KMDAwMDAwMDM5MCAwMDAwMCBuDQowMDAwMDAxNTIyIDAwMDAwIG4NCjAwMDAwMDE2OTAgMDAwMDAgbg0KMDAwMDAwMjQyMyAwMDAwMCBuDQowMDAwMDAyNDU2IDAwMDAwIG4NCjAwMDAwMDI1NzQgMDAwMDAgbg0KDQp0cmFpbGVyDQo8PA0KL1NpemUgMTENCi9Sb290IDEgMCBSDQovSW5mbyAxMCAwIFINCj4+DQoNCnN0YXJ0eHJlZg0KMjcxNA0KJSVFT0YNCg==',
}
when = datetime.datetime.strptime(config['when'], "%Y-%m-%d %H:%M:%S")
dct["signingdate"] = when.strftime("%Y%m%d%H%M%S+00'00'").encode()
signed_time = datetime.datetime(
when.year, when.month, when.day, when.hour, when.minute, when.second, 0, util.timezone.utc
)
cert = config['certificate'].encode('ascii')
signed_bytes = config['signed_bytes']
if signed_bytes is not None:
signed_bytes = base64.decodebytes(signed_bytes.encode('ascii'))
tosign = config['tosign']
if tosign is not None:
tosign = base64.decodebytes(tosign.encode('ascii'))
clshsm = Signer(cert, signed_bytes, tosign)
datau = open(pdfname, "rb").read()
cls = pdf.cms.SignedData()
datas = cls.sign(datau, dct, None, None, [], "sha256", clshsm, mode="sign")
if signed_bytes is None:
config['tosign'] = b"".join(base64.encodebytes(clshsm.tosign).split()).decode('ascii')
json.dump(config, open(pdfname + ".json", "wt"), indent=4)
else:
fname = pdfname.replace(".pdf", "-signed-cms-hash.pdf")
with open(fname, "wb") as fp:
fp.write(datau)
fp.write(datas)
main()
Parameter timestampurl has default value in head version, did You pull it ?
Did You run example ?
dear @m32 , I have Signed hash and Prehashed. what mode can I use? Thank you
Try: examples/pdf-sign-cms-hash.py
The first time you run it, it will create a .json file. The "tosign" is a document hashed value which need to be signed by an external program. After signing, fill "signed_bytes" (base64, signed hash) and "certificate" and run the example again.
@m32 thank you for your information. I'll try it