Exemplo n.º 1
0
def index(file_id):

    # Verify if the provided userfile exists.
    file_path = join(current_app.config['APPDATA_FOLDER'], file_id)
    if not exists(file_path):
        return render_template('error.html', msg='File not found')

    # Get an instance of PadesSignatureExplorer class, used to open/validate PDF
    # signatures.
    sig_explorer = PadesSignatureExplorer(get_rest_pki_client())

    # Specify that we want to validate the signatures in the file, not only
    # inspect them.
    sig_explorer.validate = True

    # Set the PDF file.
    sig_explorer.signature_file_path = file_path

    # Specify the parameters for the signature validation:
    # Accept any PAdES signature as long as the signer has an ICP-Brasil
    # certificate.
    sig_explorer.default_signature_policy_id = \
        StandardSignaturePolicies.PADES_BASIC
    # Specify the security context to be used to determine trust in the
    # certificate chain. We have encapsulated the security context on utils.py.
    sig_explorer.security_context_id = get_security_context_id()

    # Call the open() method, which returns the signature file's information.
    signature = sig_explorer.open()

    # Render the information (see file templates/open_pades_rest/index.html for
    # more information on the information returned)
    return render_template('open_pades_rest/index.html', signature=signature)
Exemplo n.º 2
0
def full():
    """

    This function initiates a XML element signature using REST PKI and renders
    the signature page. The full XML signature is recommended in cases which
    there is a need to sign the whole XML file.

    """

    try:

        # Get an instance of the FullXmlSignatureStarter class, responsible for
        # receiving the signature elements and start the signature process.
        signature_starter = FullXmlSignatureStarter(get_restpki_client())

        # Set the XML to be signed, a sample XML Document.
        signature_starter.set_xml_to_sign(get_sample_xml_document_path())

        # Set the location on which to insert the signature node. If the
        # location is not specified, the signature will appended to the root
        # element (which is most usual with enveloped signatures).
        nsm = NamespaceManager()
        nsm.add_namespace('ls', 'http://www.lacunasoftware.com/sample')
        signature_starter.set_signature_element_location(
            '//ls:signaturePlaceholder', XmlInsertionOptions.APPEND_CHILD, nsm)

        # Set the signature policy.
        signature_starter.signature_policy = \
            StandardSignaturePolicies.XADES_BES

        # Set the security context to be used to determine trust in the
        # certificate chain. We have encapsulated the security context choice on
        # util.py.
        signature_starter.security_context = get_security_context_id()

        # Call the start_with_webpki() method, which initiates the signature.
        # This yields the token, a 43-character case-sensitive URL-safe string,
        # which identifies this signature process. We'll use this value to call
        # the signWithRestPki() method on the Web PKI component (see
        # signature-form.js javascript) and also to complete the signature after
        # the form is submitted (see method pades_signature_action()). This
        # should not be mistaken with the API access token.
        result = signature_starter.start_with_webpki()

        # The token acquired above can only be used for a single signature
        # attempt. In order to retry the signature it is necessary to get a new
        # token. This can be a problem if the user uses the back button of the
        # browser, since the browser might show a cached page that we rendered
        # previously, with a now stale token. To prevent this from happen, we
        # force page expiration through HTTP headers to prevent caching of the
        # page.
        response = make_response(
            render_template('xml_signature/full.html', token=result.token))
        response.headers = get_expired_page_headers()
        return response

    except Exception as e:
        return render_template('error.html', msg=e)
Exemplo n.º 3
0
def element():
    """

    This function initiates a XML element signature using REST PKI and renders
    the signature page. The XML element signature is recommended in cases which
    there is a need to sign a specific element of a XML.

    """

    # Instantiate the XmlElementSignatureStarter class, responsible for
    # receiving the signature elements and start the signature process.
    try:
        signature_starter = XmlElementSignatureStarter(get_restpki_client())

        # Set the XML to be signed, a sample XML Document.
        signature_starter.set_xml_to_sign(get_sample_nfe_path())

        # Set the ID of the element to be signed.
        signature_starter.to_sign_element_id = \
            'NFe35141214314050000662550010001084271182362300'

        # Set the signature policy.
        signature_starter.signature_policy = \
            StandardSignaturePolicies.NFE_PADRAO_NACIONAL

        # Set a security context to be used to determine trust in the
        # certificate chain. We have encapsulated the security context choice on
        # util.py.
        signature_starter.security_context = get_security_context_id()

        # Call the start_with_webpki() method, which initiates the signature.
        # This yields the token, a 43-character case-sensitive URL-safe string,
        # which identifies this signature process. We'll use this value to call
        # the signWithRestPki() method on the Web PKI component (see
        # signature-form.js javascript) and also to complete the signature after
        # the form is submitted (see method action()). This should not be
        # mistaken with the API access token.
        result = signature_starter.start_with_webpki()

        # The token acquired above can only be used for a single signature
        # attempt. In order to retry the signature it is necessary to get a new
        # token. This can be a problem if the user uses the back button of the
        # browser, since the browser might show a cached page that we rendered
        # previously, with a now stale token. We force page expiration through
        # HTTP headers to prevent caching of the page.
        response = make_response(
            render_template('xml_signature/element.html', token=result.token))
        response.headers = get_expired_page_headers()
        return response

    except Exception as e:
        return render_template('error.html', msg=e)
Exemplo n.º 4
0
def start(file_id=None):
    """

    This function is called asynchonously via AJAX by the batch signature page
    for each document being signed. It receives the ID of the document and
    initiates a CAdES signature using REST PKI and returns a JSON with the
    token, which identifies this signature process, to be used in the next
    signature steps (see batch-signature-form.js).

    """

    # Get an instantiate of the CadesSignatureStarter class, responsible for
    # receiving the signature elements and start the signature process.
    signature_starter = CadesSignatureStarter(get_restpki_client())

    # Set the document to be signed based on its ID.
    signature_starter.set_file_to_sign_path(get_sample_batch_doc_path(file_id))

    # Set the signature policy.
    signature_starter.signature_policy = \
        StandardSignaturePolicies.PKI_BRAZIL_CADES_ADR_BASICA

    # Set a security context. We have encapsulated the security context
    # choice on util.py.
    signature_starter.security_context = get_security_context_id()

    # Optionally, set whether the content should be encapsulated in the
    # resulting CMS. If this parameter is ommitted, the following rules
    # apply:
    # - If no CmsToCoSign is given, the resulting CMS will include the
    # content.
    # - If a CmsToCoSign is given, the resulting CMS will include the
    # content if and only if the CmsToCoSign also includes the content.
    #
    signature_starter.encapsulate_content = True

    # Call the start_with_webpki() method, which initiates the signature.
    # This yields the token, a 43-character case-sensitive URL-safe string,
    # which identifies this signature process. We'll use this value to call
    # the signWithRestPki() method on the Web PKI component (see
    # signature-form.js) and also to complete the signature after
    # the form is submitted (see method action()). This should not be
    # mistaken with the API access token.
    result = signature_starter.start_with_webpki()

    # Return a JSON with the token obtained from REST PKI (the page will use
    # jQuery to decode this value).
    return jsonify(result.token)
Exemplo n.º 5
0
def index(code):
    # On printer_version_pades_express, we stored the unformatted version of the
    # verification code (without hyphens), but used the formatted version (with
    # hyphens) on the printer-friendly PDF. Now, we remove the hyphens before
    # looking it up.
    verification_code = parse_verification_code(code)

    # Get document associated with verification code.
    file_id = lookup_verification_code(verification_code)
    if file_id is None:
        # Invalid code given!
        # Small delay to slow down brute-force attacks (if you want to be extra
        # careful you might want to add a CAPTCHA to the process)
        sleep(2)
        # Return "Not Found" page.
        return render_template('error.html', msg='File not found')

    # Locate document from storage.
    file_path = join(current_app.config['APPDATA_FOLDER'], file_id)

    # Get an instance of PadesSignatureExplorer class, used to open/validate PDF
    # signatures.
    sig_explorer = PadesSignatureExplorer(get_rest_pki_client())

    # Specify that we want to validate the signatures in the file, not only
    # inspect them.
    sig_explorer.validate = True

    # Set the PDF file.
    sig_explorer.signature_file_path = file_path

    # Specify the parameters for the signature validation:
    # Accept any PAdES signature as long as the signer has an ICP-Brasil
    # certificate.
    sig_explorer.default_signature_policy_id = \
        StandardSignaturePolicies.PADES_BASIC
    # Specify the security context to be used to determine trust in the
    # certificate chain. We have encapsulated the security context on utils.py.
    sig_explorer.security_context_id = get_security_context_id()

    # Call the open() method, which returns the signature file's information.
    signature = sig_explorer.open()

    # Render the signature opening page.
    return render_template('check_pades_rest/index.html',
                           signature=signature,
                           file_id=file_id)
Exemplo n.º 6
0
def start(file_id=None):
    """

    This function is called asynchonously via AJAX by the batch signature page
    for each document being signed. It receives the ID of the document and
    initiates a PAdES signature using REST PKI and returns a JSON with the
    token, which identifies this signature process, to be used in the next
    signature steps (see batch-signature-form.js).

    """

    # Get an instantiate of the PadesSignatureStarter class, responsible for
    # receiving the signature elements and start the signature process.
    signature_starter = PadesSignatureStarter(get_restpki_client())

    # Set the document to be signed based on its ID.
    signature_starter.set_pdf_to_sign(get_sample_batch_doc_path(file_id))

    # Set the signature policy.
    signature_starter.signature_policy = \
        StandardSignaturePolicies.PADES_BASIC

    # Set a security context to determine trust in the certificate chain. We
    # have encapsulated the security context choice on util.py.
    signature_starter.security_context = get_security_context_id()

    # Set the visual representation for the signature. We have encapsulated
    # this code (on util-pades.py) to be used on various PAdES examples.
    signature_starter.visual_representation = \
        PadesVisualElementsRestPki.get_visual_representation()

    # Call the start_with_webpki() method, which initiates the signature.
    # This yields the token, a 43-character case-sensitive URL-safe string,
    # which identifies this signature process. We'll use this value to call
    # the signWithRestPki() method on the Web PKI component (see
    # signature-form.js) and also to complete the signature after
    # the form is submitted (see method action()). This should not be
    # mistaken with the API access token.
    result = signature_starter.start_with_webpki()

    # Return a JSON with the token obtained from REST PKI (the page will use
    # jQuery to decode this value).
    return jsonify(result.token)
Exemplo n.º 7
0
def index():
    """

    This view function initiates an authentication with REST PKI and renders
    the authentication page.

    """

    try:

        # Get an instance of the Authentication class.
        auth = get_restpki_client().get_authentication()

        # Call the start_with_webpki() method, which initiates the
        # authentication. This yields the "token", a 22-character case-sensitive
        # URL-safe string, which represents this authentication process. We'll
        # use this value to call the signWithRestPki() method on the Web PKI
        # component (see signature-form.js) and also to call the
        # complete_with_webpki() method on the route /authentication/action.
        # This should not be mistaken with the API access token. We have
        # encapsulated the security context choice on util.py.
        token = auth.start_with_webpki(get_security_context_id())

        # The token acquired above can only be used for a single
        # authentication. In order to retry authenticating it is necessary to
        # get a new token. This can be a problem if the user uses the back
        # button of the browser, since the browser might show a cached page that
        # we rendered previously, with a now stale token. To prevent this from
        # happening, we force page expiration through HTTP headers to prevent
        # caching of the page.
        response = make_response(
            render_template('authentication/index.html', token=token))
        response.headers = get_expired_page_headers()
        return response

    except Exception as e:
        return render_template('error.html', msg=e)
Exemplo n.º 8
0
def index(file_to_sign=None, file_to_cosign=None):
    """

    This function initiates a CAdES signature using REST PKI and renders the
    signature page.

    All CAdES signature examples converge to this action, but with different
    URL arguments:

        1. Signature with a server file               : no arguments filled
        2. Signature with a file uploaded by the user : "******" filled
        3. Co-signature of a previously signed CMS    : "cmsfile" filled

    """

    try:

        # Get an instantiate of the CadesSignatureStarter class, responsible for
        # receiving the signature elements and start the signature process.
        signature_starter = CadesSignatureStarter(get_restpki_client())

        if file_to_cosign is not None:
            # If the URL argument "cmsfile" is filled, the user has asked to
            # co-sign a previously signed CMS. We'll set the path to the CMS to
            # be co-signed, which was previously saved in the "app-data" folder
            # by the action() step. Note two important things:
            #
            #   1. The CMS to be co-signed must be set using the method
            #      "set_cms_to_cosign", not the method "set_file_to_sign".
            #
            #   2. Since we're creating CMSs with encapsulated content (see call
            #      to encapsulated_content property below), we don't need to set
            #      the content to be signed, REST PKI will get the content from
            #      the CMS being co-signed.
            #
            signature_starter.set_cms_to_cosign_path(
                '%s/%s' %
                (current_app.config['APPDATA_FOLDER'], file_to_cosign))
        else:
            # If the URL argument "userfile" is filled, it means the user was
            # redirected here by "upload" view (signature with file uploaded by
            # user). We'll set the path of the file to be signed, which was
            # saved in the app_data folder by "upload" view.
            signature_starter.set_file_to_sign_path(
                '%s/%s' % (current_app.config['APPDATA_FOLDER'], file_to_sign))

        # Set the signature policy.
        signature_starter.signature_policy =\
            StandardSignaturePolicies.PKI_BRAZIL_CADES_ADR_BASICA

        # Set a security context to be used to determine trust in the
        # certificate chain. We have encapsulated the security context choice on
        # util.py.
        signature_starter.security_context = get_security_context_id()

        # Optionally, set whether the content should be encapsulated in the
        # resulting CMS. If this parameter is ommitted, the following rules
        # apply:
        #
        # - If no CmsToCoSign is given, the resulting CMS will include the
        # content.
        # - If a CmsToCoSign is given, the resulting CMS will include the
        # content if and only if the CmsToCoSign also includes the content.
        #
        signature_starter.encapsulate_content = True

        # Call the start_with_webpki() method, which initiates the signature.
        # This yields the token, a 43-character case-sensitive URL-safe string,
        # which identifies this signature process. We'll use this value to call
        # the signWithRestPki() method on the Web PKI component (see
        # signature-form.js) and also to complete the signature after
        # the form is submitted (see method action()). This should not be
        # mistaken with the API access token.
        result = signature_starter.start_with_webpki()

        # The token acquired above can only be used for a single signature
        # attempt. In order to retry the signature it is necessary to get a
        # new token. This can be a problem if the user uses the back button of
        # the browser, since the browser might show a cached page that we
        # rendered previously, with a now stale token. To prevent this from
        # happening, we call the method get_expired_page_headers(). To prevent
        # this from happen, we force page expiration through HTTP headers to
        # prevent caching of the page.
        response = make_response(
            render_template('cades_signature_restpki/index.html',
                            token=result.token))

        get_expired_page_headers(response.headers)
        return response

    except Exception as e:
        return render_template('error.html', msg=e)
def generate_printer_friendly_version(pdf_path, verification_code):
    client = get_rest_pki_client()

    # The verification code is generated without hyphens to save storage space
    # and avoid copy-and-paste problems. On the PDF generation, we use the
    # "formatted" version, with hyphens (which will later be discarded on the
    # verification page)
    formatted_verification_code = format_verification_code(verification_code)

    # Build the verification link from the constant "VERIFICATION_LINK_FORMAT"
    # (see above) and the formatted verification code.
    verification_link = VERIFICATION_LINK_FORMAT % formatted_verification_code

    # 1. Upload the PDF.

    blob_token = client.upload_file_from_path(pdf_path)

    # 2. Inspect signatures on the uploaded PDF

    # Get and instance of the PadesSignatureExplorer class, used to
    # open/validate PDF signatures.
    sig_explorer = PadesSignatureExplorer(client)
    # Specify that we want to validate the signatures in the file, not only
    # inspect them.
    sig_explorer.validate = True
    # Set the PDF file to be inspected.
    sig_explorer.signature_file_blob_token = blob_token
    # Specify the parameters for the signature validation:
    # Accept any PAdES signature as long as the signer has an ICP-Brasil
    # certificate.
    sig_explorer.default_signature_policy_id = \
        StandardSignaturePolicies.PADES_BASIC
    # Specify the security context to be used to determine trust in the
    # certificate chain. We have encapsulated the security context on utils.py.
    sig_explorer.security_context_id = get_security_context_id()
    # Call the open() method, which returns the signature file's information.
    signature = sig_explorer.open()

    # 3. Create PDF with the verification information from uploaded PDF.

    # Get an instance of the PdfMarker class, used to apply marks on the PDF.
    pdf_marker = PdfMarker(client)
    # Specify the file to be marked.
    pdf_marker.file_blob_token = blob_token

    # Build string with joined names of signers (see method get_display_name
    # below)
    signer_names_list = []
    for signer in signature.signers:
        signer_names_list.append(get_display_name(signer.certificate))
    signer_names = join_strings_pt(signer_names_list)
    all_pages_message = "This document was digitally signed by %s.\n" \
                        "To check the signatures, visit %s at %s and inform this code %s." % (signer_names, VERIFICATION_SITE_NAME_WITH_ARTICLE, VERIFICATION_SITE, formatted_verification_code)

    # PdfHelper is a class from the PKI Express's "fluent API" that helps
    # creating elements and parameters for the PdfMarker.
    pdf = PdfHelper()

    # ICP-Brasil logo on bottom-right corner of every page (except on the page
    # which will be created at the end of the document)
    pdf_marker.marks.append(pdf.mark().on_all_pages().on_container(
        pdf.container().width(1.0).anchor_right(1.0).height(1.0).anchor_bottom(
            1.0)).add_element(
                pdf.image_element().with_opacity(75).with_image_content(
                    get_icp_brasil_logo_content(), "image/png")))

    # Summary on bottom margin of every page (except on the page which will be
    # created at the end of the document)
    pdf_marker.marks.append(pdf.mark().on_all_pages().on_container(
        pdf.container().height(2.0).anchor_bottom().var_width().margins(
            1.5, 3.5)).add_element(pdf.text_element().with_opacity(
                75).add_section_from_text(all_pages_message)))

    # Summary on right margin of every page (except on the page which will be
    # created at the end of the document), rotated 90 degrees counterclockwise
    # (text goes up).
    pdf_marker.marks.append(pdf.mark().on_all_pages().on_container(
        pdf.container().width(2.0).anchor_right().var_height().margins(
            1.5, 3.5)).add_element(
                pdf.text_element().rotate_90_counter_clockwise().with_opacity(
                    75).add_section_from_text(all_pages_message)))

    # Create a "manifest" mark on a new page added on the end of the document.
    # We'll add several elements to this mark.
    manifest_mark = pdf.mark()\
        .on_new_page()\
        .on_container(
            pdf.container()
            .var_width_and_height()
            .margins(2.54, 2.54))

    # We'll keep track of our "vertical offset" as we add elements to the mark.
    vertical_offset = 0

    element_height = 3
    # ICP-Brasil logo on the upper-left corner. Using elementHeight as width
    # because the image is a square.
    manifest_mark.add_element(pdf.image_element().on_container(
        pdf.container().height(element_height).anchor_top(vertical_offset).
        width(element_height).anchor_left()).with_image_content(
            get_icp_brasil_logo_content(), "image/png"))

    # QR Code with the verification link on the upper-right corner. Using
    # elementHeight as width because the image is a square.
    manifest_mark.add_element(pdf.qr_code_element().on_container(
        pdf.container().height(element_height).anchor_top(
            vertical_offset).width(element_height).anchor_right(
            )).with_qr_code_data(verification_link).draw_quiet_zones())

    manifest_mark.add_element(pdf.text_element().on_container(
        pdf.container().height(element_height).anchor_top(
            vertical_offset *
            0.2).full_width()).align_text_center().add_section(
                pdf.text_section().with_font_size(
                    NORMAL_FONT_SIZE * 1.6).with_text('SIGNATURE\nCHECK')))
    vertical_offset += element_height

    # Vertical padding.
    vertical_offset += 1.7

    # Header with verification code.
    element_height = 2
    manifest_mark.add_element(pdf.text_element().on_container(
        pdf.container().height(element_height).anchor_top(
            vertical_offset).full_width()).align_text_center().add_section(
                pdf.text_section().with_font_size(
                    NORMAL_FONT_SIZE * 1.2).with_text(
                        "Verification code: %s" %
                        formatted_verification_code)))
    vertical_offset += element_height

    # Paragraph saving "this document was signed by the following signers etc"
    # and mentioning the timezone of the date/times below.
    element_height = 2.5
    manifest_mark.add_element(pdf.text_element().on_container(
        pdf.container().height(element_height).anchor_top(
            vertical_offset).full_width()
    ).add_section(pdf.text_section(
    ).with_font_size(NORMAL_FONT_SIZE).with_text(
        "This document was digitally signed by the following signers on the indicated dates (%s):"
        % TIME_ZONE_DISPLAY_NAME)))
    vertical_offset += element_height

    # Iterate signers.
    for signer in signature.signers:

        element_height = 1.5
        # Green "check" or red "X" icon depending on result of validation for
        # this signer.
        manifest_mark.add_element(pdf.image_element().on_container(
            pdf.container().height(0.5).anchor_top(vertical_offset + 0.2).
            width(0.5).anchor_left()).with_image_content(
                get_validation_result_icon(signer.validation_results.is_valid),
                'image/png'))

        # Description of signer (see method  get_signer_description() below).
        manifest_mark.add_element(pdf.text_element().on_container(
            pdf.container().height(element_height).anchor_top(vertical_offset).
            var_width().margins(0.8, 0.0)).add_section(
                pdf.text_section().with_font_size(NORMAL_FONT_SIZE).with_text(
                    get_signer_description(signer))))

        vertical_offset += element_height

    # Some vertical padding for last signer.
    vertical_offset += 1.0

    # Paragraph with link to verification site and citing both the verification
    # code above and the verification link below.
    element_height = 2.5
    manifest_mark.add_element(pdf.text_element().on_container(
        pdf.container().height(element_height).anchor_top(
            vertical_offset).full_width()).add_section(
                pdf.text_section().with_font_size(NORMAL_FONT_SIZE).with_text(
                    "In order to check the signatures, visit %s at " %
                    VERIFICATION_SITE_NAME_WITH_ARTICLE)
            ).add_section(
                pdf.text_section().with_font_size(NORMAL_FONT_SIZE).with_color(
                    Color.BLUE).with_text(VERIFICATION_SITE)
            ).add_section(
                pdf.text_section().with_font_size(NORMAL_FONT_SIZE).with_text(
                    ' and inform the code above or access the link below:')))
    vertical_offset += element_height

    # Verification link.
    element_height = 1.5
    manifest_mark.add_element(pdf.text_element().on_container(
        pdf.container().height(element_height).anchor_top(vertical_offset).
        full_width()).add_section(
            pdf.text_section().with_font_size(NORMAL_FONT_SIZE).with_color(
                Color.BLUE).with_text(verification_link)).align_text_center())

    # Add marks.
    pdf_marker.marks.append(manifest_mark)

    # Apply marks.
    result = pdf_marker.apply()

    # Return content of the printer-friendly version.
    return result.open()
Exemplo n.º 10
0
def index(userfile=None):
    """

    This function initiates a PAdES signature using REST PKI and renders the
    signature page.

    Both PAdES signature examples, with a server file and with a file uploaded
    by the user, converge to this function. The difference is that, when the
    file is uploaded by the user, the function is called with a URL argument
    named "userfile".

    """

    try:

        # Get an instantiate of the PadesSignatureStarter class, responsible for
        # receiving the signature elements and start the signature process.
        signature_starter = PadesSignatureStarter(get_restpki_client())

        # If the URL argument "userfile" is filled, it means the user was
        # redirected here by "upload" view (signature with file uploaded by
        # user). We'll set the path of the file to be signed, which was saved
        # in the app_data folder by "upload" view.
        if userfile is not None:
            signature_starter.set_pdf_to_sign(
                '%s/%s' % (current_app.config['APPDATA_FOLDER'], userfile))
        else:
            signature_starter.set_pdf_to_sign(get_sample_doc_path())

        # Set the signature policy.
        signature_starter.signature_policy =\
            StandardSignaturePolicies.PADES_BASIC

        # Set a security context to be used to determine trust in the
        # certificate chain. We have encapsulated the security context choice on
        # util.py.
        signature_starter.security_context = get_security_context_id()

        # Set the visual representation for the signature. We have encapsulated
        # this code (on util-pades.py) to be used on various PAdES examples.
        signature_starter.visual_representation = get_visual_representation()

        # Call the start_with_webpki() method, which initiates the signature.
        # This yields the token, a 43-character case-sensitive URL-safe string,
        # which identifies this signature process. We'll use this value to call
        # the signWithRestPki() method on the Web PKI component (see
        # signature-form.js javascript) and also to complete the signature after
        # the form is submitted (see method pades_signature_action()). This
        # should not be mistaken with the API access token.
        result = signature_starter.start_with_webpki()

        # The token acquired above can only be used for a single signature
        # attempt. In order to retry the signature it is necessary to get a new
        # token. This can be a problem if the user uses the back button of the
        # browser, since the browser might show a cached page that we rendered
        # previously, with a now stale token. To prevent this from happen, we
        # force page expiration through HTTP headers to prevent caching of the
        # page.
        response = make_response(render_template('pades_signature/index.html',
                                                 token=result.token,
                                                 userfile=userfile))
        response.headers = get_expired_page_headers()
        return response

    except Exception as e:
        return render_template('error.html', msg=e)