PyHanko - 无效签名,并在中断模式下出现错误“定义签名数据范围的意外字节范围值”

问题描述 投票:0回答:1

我尝试在 Flask API 中设置 pades 签名流程。

当我们在客户端计算机上使用 PKCS11 设备时,我们需要使用中断的签名流程:

  • 用户在
    /pades/start
    上发布其证书(PEM 文件)和要签名的 PDF。
  • API 将摘要返回给客户端,客户端使用智能卡对其进行签名,以及唯一的 task_id
  • 用户在
    /pades/complete
    上发布其任务 ID 和计算出的签名。 API 使用此签名来创建数字签名的 PDF

目前,该流程有效。但生成的 PDF 被认为具有无效签名,并显示此消息“定义签名数据范围的意外字节范围值。 详细信息:签名字节范围无效”

# Relevant part in the /pades/start route 
    with open(task_dir / "certificate.pem", "w") as f:
        f.write(body["certificate"])

    cert = load_cert_from_pemder(task_dir / "certificate.pem")

    with open(task_dir / "document.pdf", "rb+") as f:
        writer = IncrementalPdfFileWriter(f)
        fields.append_signature_field(
            writer,
            sig_field_spec=fields.SigFieldSpec("Signature", box=(200, 600, 400, 660)),
        )

        meta = signers.PdfSignatureMetadata(
            field_name="Signature",
            subfilter=fields.SigSeedSubFilter.PADES,
            md_algorithm="sha256",
        )

        ext_signer = signers.ExternalSigner(
            signing_cert=cert,
            cert_registry=registry.CertificateRegistry(),
            signature_value=bytes(8192), # I tried to adjust this with many different values without success
        )

        pdf_signer = signers.PdfSigner(meta, signer=ext_signer)

        prep_digest, tbs_document, _ = pdf_signer.digest_doc_for_signing(writer)

        post_sign_instructions = tbs_document.post_sign_instructions

        def async_to_sync(awaitable):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return loop.run_until_complete(awaitable)

        signed_attrs: asn1crypto.cms.CMSAttributes = async_to_sync(
            ext_signer.signed_attrs(
                prep_digest.document_digest, "sha256", use_pades=True
            )
        )

        task = {
            **(body or {}),
            "id": task_id,
            "prep_digest": prep_digest,
            "signed_attrs": signed_attrs,
            "psi": post_sign_instructions,
        }

        redis.set(
            f"task:{task_id}",
            pickle.dumps(task),
        )

        writer.write_in_place()

    return {"task": task_id, "digest": prep_digest.document_digest.hex()}

# Relevant part in the /pades/complete route 

    task_id = body["task"]
    task_str = redis.get(f"task:{task_id}")
    task = pickle.loads(task_str) if task_str else None
    task_dir = Path(get_task_dir(settings.WORKDIR, task_id))

    if not task:
        return {"error": "Task not found"}, 404

    ext_signer = signers.ExternalSigner(
        signing_cert=load_cert_from_pemder(task_dir / "certificate.pem"),
        signature_value=bytes.fromhex(body["signature"]),
        cert_registry=registry.CertificateRegistry(),
    )

    sig_cms = ext_signer.sign_prescribed_attributes(
        "sha256", signed_attrs=task["signed_attrs"]
    )

    with open(task_dir / "document.pdf", "rb+") as f:
        PdfTBSDocument.finish_signing(
            f,
            prepared_digest=task["prep_digest"],
            signature_cms=sig_cms,
            post_sign_instr=task["psi"],
        )

    redis.delete(f"task:{task_id}")

    return "ok"

我可以尝试什么来修复此错误消息?

python flask pades pyhanko
1个回答
0
投票

经过几个小时的搜索,我找到了解决方案。我把它放在这里给下一个需要它的人

出现问题的原因是:

  • 我们需要返回

    signed_attrs.dump()
    而不是
    prep_digest.document_digest
    (并对它进行哈希处理!)

  • 我们应该在摘要计算期间使用

    pdf_signer.digest_doc_for_signing(pdf_out=writer,in_place=True)
    ,以允许正确更新pdf

© www.soinside.com 2019 - 2024. All rights reserved.