Source code for AIS.pdf
# -*- coding: utf-8 -*-
"""
AIS.py - A Python interface for the Swisscom All-in Signing Service.
:copyright: (c) 2016 by Camptocamp
:license: AGPLv3, see README and LICENSE for more details
"""
import base64
import codecs
import hashlib
import shutil
import subprocess
import tempfile
import PyPDF2
from pkg_resources import resource_filename
from . import exceptions
from . import helpers
[docs]class PDF(object):
"""A container for a PDF file to be signed and the signed version."""
def __init__(self, in_filename, prepared=False):
self.in_filename = in_filename
"""Filename of the PDF to be treated."""
_out_fp, _out_filename = tempfile.mkstemp(suffix=".pdf")
self.out_filename = _out_filename
"""Filename of the output, signed PDF."""
shutil.copy(self.in_filename, self.out_filename)
self.prepared = prepared
"""Is the PDF prepared with an empty signature?"""
@staticmethod
def _java_command():
java_dir = resource_filename(__name__, 'empty_signer')
return [
'java',
'-cp', '.:vendor/itextpdf-5.5.9.jar',
'-Duser.dir={}'.format(java_dir),
'EmptySigner',
]
[docs] @classmethod
def prepare_batch(cls, pdfs):
"""Add an empty signature to each of pdfs with only one java call."""
pdfs_to_prepare = filter(lambda p: not p.prepared, pdfs)
subprocess.check_call(
cls._java_command() +
[pdf.out_filename for pdf in pdfs_to_prepare]
)
for pdf in pdfs_to_prepare:
pdf.prepared = True
[docs] def prepare(self):
"""Add an empty signature to self.out_filename."""
if not self.prepared:
subprocess.check_call(
self._java_command() + [self.out_filename],
)
self.prepared = True
def digest(self):
reader = PyPDF2.PdfFileReader(self.out_filename)
sig_obj = None
for generation, idnums in reader.xref.items():
for idnum in idnums:
if idnum == 0:
break
pdf_obj = PyPDF2.generic.IndirectObject(idnum, generation,
reader).getObject()
if (
isinstance(pdf_obj, PyPDF2.generic.DictionaryObject) and
pdf_obj.get('/Type') == '/Sig'
):
sig_obj = pdf_obj
break
if sig_obj is None:
raise exceptions.MissingPreparedSignature
self.byte_range = sig_obj['/ByteRange']
h = hashlib.sha256()
with open(self.out_filename, 'rb') as fp:
for start, length in (self.byte_range[:2], self.byte_range[2:]):
fp.seek(start)
h.update(fp.read(length))
result = base64.b64encode(h.digest())
if helpers.PY3:
result = result.decode('ascii')
return result
[docs] def write_signature(self, signature):
""" Write the signature in the pdf file
:type signature: Signature
"""
with open(self.out_filename, "rb+") as fp:
fp.seek(self.byte_range[1] + 1)
fp.write(codecs.encode(signature.contents, 'hex'))