2
votes

I am digitally signing a PDF with iText7 and GlobalSign DSS. I implemented the GlobalSing DSS API calls into the necessary iText classes. I get the proper server responses and I am able to call the pdfSigner.signDetached() method with all the needed arguments. Signing with the pdfSigner also succeeds and I get a signed PDF that looks good at first sight. But when I open the signed pdf in Adobe Reader it tells me that the trust chain of the signing certificate is broken and that it can not trace it back to the CA root. Which is strange because it is an AATL certificate and the AATL list of the Adobe Reader is up to date.
And I do not understand why this is happening.

This is what I do :

  • call DSS for an identity : returns an id string, the signing certificate and an ocsp response

  • call DSS for the trustchain : returns the chain of certificates used to
    sign the signing certicate, up to the GlobalSign root, together with
    their oscp responses (except for the root)

  • I create an array of X509Certificate objects containing the signing
    certificate, 2 intermediates and the GlobalSign root certificate (in that order)

  • I implement an IOcspClient that uses the ocsp response from the DSS call for the identity

  • I implement an ITsaClient that calls the DSS API /timestamp/{digest}

  • and finally I execute : pdfSigner.signDetached(externalDigest, externalSignature, chain.toArray(new X509Certificate[]{}), null, dssOcspClient, dssTSAClient, 0, PdfSigner.CryptoStandard.CMS);

  • in which the externalSignature (an implementation of IExternalSignature) will call the DSS identity/{id}/sign/{digest} API

While debugging into the signDetached method and deeper into the pdfSigner code, I clearly see that all certificates are in the chain in the right order. I see them being processed in the PdfPKCS7 class (however I don't know/understand exactly what is going on there). I see the signing taking place, no exceptions are thrown and at the end the produced PDF looks like it is correctly signed. Which Adobe says is not.

What am I missing here ?

The trustchain response from de DSS API not only returns the certificates from the chain of trust of the signing certificate, but also the ocsp responses for the two intermediates between the signing certificate and the GlobalSign root. These are never used. And in fact I don't know what to do with them either.
Could these be the missing pieces for AdobeReader to reconstruct the trust chain up to the GlobalSign root ?
And if so : how do I put them into that PDF ?
And if not : then what am I doing wrong that breaks that trustchain ?

An answer to these questions would save my day :-)

Here is the link to a PDF that will show the problem :
test pdf signed with DSS
(after accepting the answer, I removed the example pdf on my client's request)

Below are some pieces of the code.

The center piece that gathers the DSS info and calls the signDetached method

    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());
}


The IExternalSignature implementation

    @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);
    }
}


The IOcspClient implementation

    @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;
}


The ITSAClient implementation

    @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);
}
1
Please share an example pdf signed by your code illustrating the issue.mkl
I added a link to a sample PDF that shows the problemcrolha
FYI : I use iText 7.1.0crolha
I'll look into that later but probably not before Tuesday.mkl

1 Answers

2
votes

In short

Your signer certificate is invalid.

In detail

Your signer certificate and its certificate chain (according to issuer/subject match) are embedded in the signature, in particular your certificate with 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

and its claimed issuer

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

Thus, one can check the signature with which your certificate is signed. And while doing so one sees that the TBSCertificate part of your signer certificate (the to-be-signed part) has this digest value

C8751FDC7F679CB627F61028ACDD0D09613AFA782412ACFC7E189EA5DA625831

but the signature actually signs this digest value

16090737B41E6E0466E7EB7A7EBD79F5494E438C11D0FB408BCA663A5923AD03

Thus, your signer certificate is not correctly signed.

What does this mean

In a comment you ask

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?

You're not doing anything wrong during signing, at least I don't think so. The broken signature is not the signature signing the document but its the signature signing your certificate by your CA.

I see essentially three possible reasons for that:

  • The certificate signature simply is broken and doesn't match your certificate anywhere, anyhow.

    This would surprise me.

  • The certificate signature has been calculated not for the DER encoded form of your to-be-signed certificate part but some other form.

    This is not unheard of, if your certificate originally was not in DER form but the certificate signing process assumed it to be, a non-DER form may have been signed (even though according to specification the DER form has to be signed). If some validator then checked the signature which also does not ensure DER form but takes the TBSCertificate as is, that validator would even have indicated that the signature was valid.

    In the certificate as embedded in the PDF signature the to-be-signed part is DER encoded but this may have been enforced at some step after the initial certificate generation.

  • Some minute change may have happened to your certificate after creation.

    This also is possible.

You could try and receive a copy of your certificate from your CA in a form that is as original as possible and compare to the certificate that is embedded in your signature. If you find differences, then analyzing the differences most likely will further illuminate the cause of the problem.