使用 GlobalSign DSS AATL 证书的 iText7 pdf 签名显示 Adob​​e 中的信任链已损坏 Reader

iText7 pdf signing with GlobalSign DSS AATL certificate shows broken trustchain in Adobe Reader

我正在使用 iText7 和 GlobalSign DSS 对 PDF 进行数字签名。我将 GlobalSing DSS API 调用实现到必要的 iText classes 中。我得到了正确的服务器响应,并且能够使用所有需要的参数调用 pdfSigner.signDetached() 方法。使用 pdfSigner 签名也成功了,我得到了一个第一眼看起来不错的签名 PDF。但是,当我在 Adob​​e Reader 中打开已签名的 pdf 时,它告诉我签名证书的信任链已损坏,无法追溯到 CA 根。这很奇怪,因为它是一个 AATL 证书并且 Adob​​e Reader 的 AATL 列表是最新的。
我不明白为什么会这样。

我就是这样做的:

在调试 signDetached 方法和深入 pdfSigner 代码时,我清楚地看到所有证书都以正确的顺序位于链中。我看到它们在 PdfPKCS7 class 中被处理(但是我不 know/understand 到底发生了什么)。我看到正在签名,没有抛出异常,最后生成的 PDF 看起来像是正确签名的。 Adobe 说不是。

我在这里错过了什么?

来自 de DSS 的信任链响应 API 不仅 returns 来自签名证书信任链的证书,还有签名证书和 GlobalSign 根之间的两个中间体的 ocsp 响应。这些从未被使用过。事实上,我也不知道如何处理它们。
这些是否是 Adob​​eReader 重建信任链到 GlobalSign 根的缺失部分?
并且如果是这样:我如何将它们放入那个 PDF 中?
如果不是:那么我做错了什么破坏了信任链?

这些问题的答案将节省我的时间:-)

这是将显示问题的 PDF link:
test pdf signed with DSS
(接受答案后,我应客户要求删除了示例 pdf)

下面是部分代码。

收集DSS信息并调用signDetached方法的中心部分

    private InputStream sign(byte[] unsignedDocument) throws IOException, DssServiceException, GeneralSecurityException {

    SigningIdentity signingIdentity = signingIdentityService.getValidSigningIdentity();
    DssOcspClient dssOcspClient = new DssOcspClient(signingIdentity);

    TrustChainResponse trustChainResponse = digitalSigningService.getTrustChain();
    List<X509Certificate> chain = new ArrayList<>();
    chain.add(signingIdentity.getCertificate());
    chain.addAll(trustChainResponse.getTrustChain());

    IExternalDigest externalDigest = new ProviderDigest(BC_SECURITY_PROVIDER);
    IExternalSignature externalSignature = new DssExternalSignature(signingIdentity.getIdentity(), digitalSigningService);

    ByteArrayOutputStream signedPdfOut = new ByteArrayOutputStream();
    PdfSigner pdfSigner = createPdfSigner(new ByteArrayInputStream(unsignedDocument), signedPdfOut);
    pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CADES);

    return new ByteArrayInputStream(signedPdfOut.toByteArray());
}


IExternalSignature 实现

    @Override
public byte[] sign(byte[] message) throws GeneralSecurityException {
    MessageDigest messageDigest = new BouncyCastleDigest().getMessageDigest(DEFAULT_DIGEST_ALGORITHM);
    byte[] documentHash = messageDigest.digest(message);
    try {
        return digitalSigningService.getSignature(signingIdentity, documentHash);
    }
    catch (DssServiceException e) {
        LOGGER.error("error getting signature", e);
        throw  new GeneralSecurityException(e);
    }
}


IOcspClient实现

    @Override
public byte[] getEncoded(X509Certificate checkCert, X509Certificate issuerCert, String url) {
    try {
        if(Objects.equals(signingIdentity.getCertificate(), checkCert)) {
            OCSPResp response = new OCSPResp(signingIdentity.getOcsp());
            BasicOCSPResp basicResponse = (BasicOCSPResp)response.getResponseObject();
            return basicResponse.getEncoded();
        }
    }
    catch (CertificateException | IOException | OCSPException e) {
        LOGGER.warn("OCSP validatie gefaald!", e.getMessage());
    }
    return null;
}


ITSAClient 实现

    @Override
public byte[] getTimeStampToken(byte[] imprint) throws Exception {

    String digestAlgorithmOID = DigestAlgorithms.getAllowedDigest(DEFAULT_DIGEST_ALGORITHM);
    ASN1ObjectIdentifier digestAlgOID = new ASN1ObjectIdentifier(digestAlgorithmOID);
    AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DERNull.INSTANCE);
    MessageImprint messageImprint = new MessageImprint(algID, imprint);

    byte[] hash = messageImprint.getHashedMessage();
    return digitalSigningService.getTimeStamp(hash);
}

简而言之

您的签署者证书无效。

详细

您的签名者证书及其证书链(根据 issuer/subject 匹配)嵌入在签名中,特别是您的带有主题

的证书

cn=Homologatie Voertuigen, ou=Departement Mobiliteit en Openbare Werken, ou=Vlaams Huis voor de Verkeersveiligheid, o=Ministeries van de Vlaamse Gemeenschap, l=Brussel, st=Brussel, c=BE

及其声称的发行人

cn=GlobalSign CA 5 for AATL, o=GlobalSign nv-sa, c=BE

因此,可以检查您的证书所用的签名。在这样做的同时,您会看到签名者证书的 TBSCertificate 部分(待签名部分)具有此摘要值

C8751FDC7F679CB627F61028ACDD0D09613AFA782412ACFC7E189EA5DA625831

但签名实际上签署了这个摘要值

16090737B41E6E0466E7EB7A7EBD79F5494E438C11D0FB408BCA663A5923AD03

因此,您的签名证书未正确签名。

这是什么意思

在您提问的评论中

But I am a little confused about what it means exactly. Are we actually doing something wrong during signing, sending the wrong document hash to the signing server ? Or do you mean there is something wrong with the server side signing certificate issued by GlobalSign that they use to sign that document hash?

签名的时候你没有做错什么,至少我不这么认为。损坏的签名不是签署文档的签名,而是您的 CA 签署证书的签名。

我基本上看到了三个可能的原因:

  • 证书签名已损坏,无论如何都与您的证书不匹配。

    这会让我大吃一惊。

  • 计算的证书签名不是针对您要签名的证书部分的 DER 编码形式,而是针对其他形式。

    这并非闻所未闻,如果您的证书最初不是 DER 格式但证书签名过程假定它是,则可能已签署非 DER 格式(即使根据规范,DER 表格必须签名)。如果某个验证器随后检查了同样不确保 DER 形式但按原样采用 TBSCertificate 的签名,该验证器甚至会指示签名有效。

    在 PDF 签名中嵌入的证书中,待签名部分采用 DER 编码,但这可能在初始证书生成后的某个步骤强制执行。

  • 您的证书在创建后可能发生了一些细微的变化。

    这个也是可以的

您可以尝试以尽可能原始的形式从您的 CA 接收您的证书副本,并与您的签名中嵌入的证书进行比较。如果您发现差异,那么分析这些差异很可能会进一步阐明问题的原因。