使用 DSS(数字签名服务)对哈希进行签名

Meh*_*hdi 4 java digital-signature pdfbox

我正在尝试使用 DSS 签署 PDF 文档,但我的问题是我无法在服务器 A 中计算文档的哈希值,然后在服务器 B 中对其进行签名。

知道服务器 A 包含 PDF 文档,并且在服务器 B 中我们检索用于签名的证书

我的问题是如何在不需要证书的情况下计算服务器 A 中文档的哈希值。然后发送到服务器B请求签名?

更新 :

****** 哈希的准备和计算 ********

    IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
    PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();

    PAdESSignatureParameters parameters = new PAdESSignatureParameters();

    parameters.setDigestAlgorithm(DigestAlgorithm.SHA512);
    parameters.setReason("Preuve de signature");
    parameters.setLocation("MAROC");
    parameters.setGenerateTBSWithoutCertificate(true);

    SignatureImageParameters imageParameters = new SignatureImageParameters();

    imageParameters.setPage(1);
    FileDocument imageFile = new FileDocument("logo.png");
    RemoteDocument fileImage = RemoteDocumentConverter.toRemoteDocument(imageFile);
    DSSDocument image = RemoteDocumentConverter.toDSSDocument(fileImage);
    // set an image
    imageParameters.setImage(image);

    imageParameters.setxAxis(350);
    imageParameters.setyAxis(400);
    imageParameters.setWidth(200);
    imageParameters.setHeight(100);
    parameters.setImageParameters(imageParameters);
    SignatureImageTextParameters textParameters = new SignatureImageTextParameters();
    DSSFont font = new DSSJavaFont(Font.SERIF);
    font.setSize(16); // Specifies the text size value (the default font size is 12pt)
    textParameters.setFont(font);
    textParameters.setTextColor(Color.BLUE);

    textParameters.setSignerTextPosition(SignerTextPosition.RIGHT);
    // Specifies a horizontal alignment of a text with respect to its area
    textParameters.setSignerTextHorizontalAlignment(SignerTextHorizontalAlignment.LEFT);
    // Specifies a vertical alignment of a text block with respect to a signature field area
    textParameters.setSignerTextVerticalAlignment(SignerTextVerticalAlignment.TOP);
    imageParameters.setTextParameters(textParameters);

    FileDocument fileToSign = new FileDocument("file.pdf");
    RemoteDocument fileSign = RemoteDocumentConverter.toRemoteDocument(fileToSign);
    DSSDocument toSignDocument = RemoteDocumentConverter.toDSSDocument(fileSign);

    byte[] hash = pdfSignatureService.digest(toSignDocument, parameters);

    DSSDocument signatureValue = SignHashDocument.signHash(hash);

    DSSDocument signedDocument = pdfSignatureService.sign(toSignDocument, DSSUtils.toByteArray(signatureValue), parameters);

    save(signedDocument);
Run Code Online (Sandbox Code Playgroud)

****** 哈希签名 ********

     // Create common certificate verifier
    CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
        // Create CAdESService for signature
    CAdESService service = new CAdESService(commonCertificateVerifier);

    CAdESSignatureParameters parameters = new CAdESSignatureParameters();
    DSSPrivateKeyEntry privateKey = getKey("certificate.p12","123456");


    // We choose the level of the signature (-B, -T, -LT, -LTA).
    parameters.setSignatureLevel(SignatureLevel.CAdES_BASELINE_B);
    parameters.setSignaturePackaging(SignaturePackaging.ENVELOPING);

    parameters.setDigestAlgorithm(DigestAlgorithm.SHA512);
    // We set the signing certificate
    parameters.setSigningCertificate(privateKey.getCertificate());
    // We set the certificate chain
    parameters.setCertificateChain(privateKey.getCertificateChain());


    SignatureTokenConnection signingToken = new Pkcs12SignatureToken("certificate.p12",
            new KeyStore.PasswordProtection("123456".toCharArray()));


    convertByteArrayToFile(hashToSign,"filetosign.hash");
    FileDocument fileToSign = new FileDocument("filetosign.hash");
    RemoteDocument fileSign = RemoteDocumentConverter.toRemoteDocument(fileToSign);
    DSSDocument toSignDocument = RemoteDocumentConverter.toDSSDocument(fileSign);

    //ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);

    ToBeSigned dataToSign = new ToBeSigned(hashToSign);

    DigestAlgorithm digestAlgorithm = parameters.getDigestAlgorithm();
    SignatureValue signatureValue = signingToken.sign(dataToSign, digestAlgorithm, privateKey);

    DSSDocument signedDocument = service.signDocument(toSignDocument, parameters, signatureValue);

    return  signedDocument;
Run Code Online (Sandbox Code Playgroud)

****** PDF 错误:************

在此输入图像描述

mkl*_*mkl 5

一般来说

这是为了回应您问题的原始修订版而写的。

您的代码提到了 PAdES。因此,当您说尝试使用 DSS 签署 PDF 文档时,我假设您指的是集成 PAdES(不是分离的 CAdES 或 XAdES)签名。

创建集成 PDF 签名(如 PAdES)需要首先准备 PDF 以能够携带嵌入签名,即向现有或新签名字段添加签名字典。这个签名字典包含了多种信息,签名时间、签名原因等,同时也是一个占位符,用于后面嵌入CMS签名容器。然后对这个准备好的 PDF(占位符除外)进行哈希处理。

此外,您的代码提到您选择签名的级别 (-B, -T, -LT, -LTA)

创建 PAdES Baseline LT 和 PAdES Baseline LTA 签名需要准备具有 PAdES Baseline T 签名的 PDF,并根据 T 签名的性质添加其他对象的集合。

如果 eSig DSS 有 PDF 可供准备,它可以为您完成所有这些准备工作。

因此,如果您只想从服务器 A 向 B 发送哈希值,则必须在服务器 A 上使用 eSig DSS 来完成大部分工作,而服务器 B 仅充当返回签名哈希值的哑签名服务或大多数可用于 PAdES 的 CMS 容器。

您是否可以在服务器 A 不知道证书的情况下执行此操作,取决于您是否希望证书详细信息显示在新签名的签名小部件中。创建小部件外观是 PDF 准备步骤的一部分,因此如果您想要这样一个带有证书信息的小部件,服务器 A 需要知道证书,通常甚至在请求签名之前

那么服务器 A 和 B 之间运行什么样的协议,就由你决定了。您只需SignatureTokenConnection相应地实现即可在 eSig DSS 中的服务器 A 上使用来与服务器 B 进行通信。

如果你的方法

现在显示来自两台服务器的代码后,人们可以讨论您的具体方法。

在服务器 A 上,您使用 eSig DSS 准备 PAdES 签名,并在调用返回中嵌入 CMS 签名SignatureValue容器SignHashDocument.signHash

ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);

SignatureValue signatureValue = SignHashDocument.signHash(dataToSign);

DSSDocument signedDocument = service.signDocument(toSignDocument, parameters, signatureValue);
Run Code Online (Sandbox Code Playgroud)

即服务器 A 创建 CMS 签名容器,而服务器 B 仅提供签名的哈希值。

除非您知道用于签名的证书并在调用parameters之前将其设置,否则这无法工作service.getDataToSign

原因是 CMS 容器在容器的未签名字节和(对于 PAdES)签名字节中都包含对该证书的引用。对于未签名字节中的引用,理论上,连同签名字节一起检索证书就足够了,但对于签名字节中的引用,必须事先知道。

或者,您可以尝试在服务器 B 上实现 CMS 容器的完整生成。

但这需要相当大的改变,而且你必须更深入地研究源头PAdESService。您必须执行以下操作,而不是在服务器 A 上引用上面引用的三行:

  • 首先检索 PDF 对象库和 PDF 签名服务

    IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
    PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();
    
    Run Code Online (Sandbox Code Playgroud)
  • 第一次准备 PDF 进行签名并计算文档摘要

    byte[] hash = pdfSignatureService.digest(toSignDocument, parameters);
    
    Run Code Online (Sandbox Code Playgroud)
  • 将此文档摘要发送到后端(服务器 B),后端必须创建并返回一个特殊的 CAdES 签名容器,而不仅仅是裸露的签名字节,

  • 并再次准备 PDF 进行签名并注入此签名容器:

    DSSDocument signature = pdfSignatureService.sign(toSignDocument, encodedData, parameters);
    
    Run Code Online (Sandbox Code Playgroud)

概念证明

以下是使用 eSig DSS 5.8 的概念证明:

在您的服务器 A 上,我们基本上可以使用您现有的代码:

IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();
Run Code Online (Sandbox Code Playgroud)

SplitPAdESSing测试testSplitPAdESGenerationForMehdi

该方法signHash现在应为给定的文档哈希独立创建 CMS 签名容器,并且该容器应符合 PAdES 要求。eSig DSS 包含提供此功能的方法和类,但它们protected甚至不太可见。因此,对于我们的 POC,我们只需将它们复制到我们的代码中即可。

为简单起见,我使用硬编码 SHA512withRSA 作为签名算法。

因此:

byte[] hash = pdfSignatureService.digest(toSignDocument, parameters);
Run Code Online (Sandbox Code Playgroud)

SplitPAdESSigning方法)

辅助方法getDataToSigngenerateCMSSignedData基本上是从 ; 复制的PAdESService。他们使用padesCMSSignedDataBuilder提供的signHash(而不是成员变量,您也可以将其作为这两个方法的另一个参数):

DSSDocument signature = pdfSignatureService.sign(toSignDocument, encodedData, parameters);
Run Code Online (Sandbox Code Playgroud)

SplitPAdESsigning方法)

由于可见性有限,类PadesCMSSignedDataBuilder和内容将被复制:PAdESLevelBaselineB

DSSDocument toSignDocument = PDF_DOCUMENT_TO_SIGN;
DSSDocument image = IMAGE_DOCUMENT;


PAdESSignatureParameters parameters = new PAdESSignatureParameters();
parameters.setDigestAlgorithm(DigestAlgorithm.SHA512);
parameters.setReason("Preuve de signature");
parameters.setLocation("MAROC");
parameters.setGenerateTBSWithoutCertificate(true);

SignatureImageParameters imageParameters = new SignatureImageParameters();
imageParameters.setPage(1);
imageParameters.setImage(image);
imageParameters.setxAxis(350);
imageParameters.setyAxis(400);
imageParameters.setWidth(200);
imageParameters.setHeight(100);
parameters.setImageParameters(imageParameters);

SignatureImageTextParameters textParameters = new SignatureImageTextParameters();
DSSFont font = new DSSJavaFont(Font.SERIF);
font.setSize(16);
textParameters.setFont(font);
textParameters.setTextColor(Color.BLUE);
textParameters.setSignerTextPosition(SignerTextPosition.RIGHT);
textParameters.setSignerTextHorizontalAlignment(SignerTextHorizontalAlignment.LEFT);
textParameters.setSignerTextVerticalAlignment(SignerTextVerticalAlignment.TOP);
textParameters.setText("TESTING");
imageParameters.setTextParameters(textParameters);


IPdfObjFactory pdfObjFactory = new ServiceLoaderPdfObjFactory();
PDFSignatureService pdfSignatureService = pdfObjFactory.newPAdESSignatureService();

byte[] hash = pdfSignatureService.digest(toSignDocument, parameters);

byte[] signatureValue = signHash(hash);

DSSDocument signedDocument = pdfSignatureService.sign(toSignDocument, signatureValue, parameters);


signedDocument.save(PATH_TO_SAVE_THE_SIGNED_DOCUMENT_TO);
Run Code Online (Sandbox Code Playgroud)

SplitPAdESSigning帮助程序类)

signHash及其帮助程序不依赖于服务器 A 代码,因此也可以位于服务器 B 上。