Source code for AIS.ais
# -*- 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 json
import re
import uuid
import requests
from .pdf import PDF
from . import exceptions
url = "https://ais.swisscom.com/AIS-Server/rs/v1.0/sign"
[docs]class AIS(object):
"""Client object holding connection information to the AIS service."""
def __init__(self, customer, key_static, cert_file, cert_key):
"""Initialize an AIS client with authentication information."""
self.customer = customer
self.key_static = key_static
self.cert_file = cert_file
self.cert_key = cert_key
self.byte_range = None
self.last_request_id = None
def _request_id(self):
request_id = self.last_request_id = uuid.uuid4().hex
return request_id
[docs] def post(self, payload):
""" Do the post request for this payload and return the signature part
of the json response.
:type payload: str
:rtype: dict
"""
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8',
}
cert = (self.cert_file, self.cert_key)
response = requests.post(url, data=payload, headers=headers,
cert=cert)
sign_resp = response.json()['SignResponse']
result = sign_resp['Result']
if 'Error' in result['ResultMajor']:
raise exceptions.error_for(response)
return sign_resp
[docs] def sign_batch(self, pdfs):
"""Sign a batch of files.
:type pdfs: list(PDF)
"""
# prepare pdfs in one batch
# payload in batch
PDF.prepare_batch(pdfs)
payload_documents = {
"DocumentHash" + str(count): {
"@ID": count,
"dsig.DigestMethod": {
"@Algorithm":
"http://www.w3.org/2001/04/xmlenc#sha256"
},
"dsig.DigestValue": pdf.digest()
}
for count, pdf in enumerate(pdfs)
}
payload = {
"SignRequest": {
"@RequestID": self._request_id(),
"@Profile": "http://ais.swisscom.ch/1.0",
"OptionalInputs": {
"ClaimedIdentity": {
"Name": ':'.join((self.customer, self.key_static)),
},
"SignatureType": "urn:ietf:rfc:3369",
"AdditionalProfile":
"http://ais.swisscom.ch/1.0/profiles/batchprocessing",
"AddTimestamp": {"@Type": "urn:ietf:rfc:3161"},
"sc.AddRevocationInformation": {"@Type": "BOTH"},
},
"InputDocuments": payload_documents
}
}
payload_json = json.dumps(payload, indent=4)
payload_json = re.sub(r'"DocumentHash\d+"', '"DocumentHash"',
payload_json)
sign_resp = self.post(payload_json)
other = sign_resp['SignatureObject']['Other']['sc.SignatureObjects']
for signature_object in other['sc.ExtendedSignatureObject']:
signature = Signature(base64.b64decode(
signature_object['Base64Signature']['$']
))
which_document = int(signature_object['@WhichDocument'])
pdf = pdfs[which_document]
pdf.write_signature(signature)
[docs] def sign_one_pdf(self, pdf):
"""Sign the given pdf file.
:type pdf: PDF
"""
pdf.prepare()
payload = {
"SignRequest": {
"@RequestID": self._request_id(),
"@Profile": "http://ais.swisscom.ch/1.0",
"OptionalInputs": {
"ClaimedIdentity": {
"Name": ':'.join((self.customer, self.key_static)),
},
"SignatureType": "urn:ietf:rfc:3369",
"AddTimestamp": {"@Type": "urn:ietf:rfc:3161"},
"sc.AddRevocationInformation": {"@Type": "BOTH"},
},
"InputDocuments": {
"DocumentHash": {
"dsig.DigestMethod": {
"@Algorithm":
"http://www.w3.org/2001/04/xmlenc#sha256"
},
"dsig.DigestValue": pdf.digest()
},
}
}
}
sign_response = self.post(json.dumps(payload))
signature = Signature(base64.b64decode(
sign_response['SignatureObject']['Base64Signature']['$']
))
pdf.write_signature(signature)
class Signature(object):
"""A cryptographic signature returned from the AIS webservice."""
def __init__(self, contents):
"""Build a Signature."""
self.contents = contents