Skip to content
Sign with GlobalSign DSS

Sign with GlobalSign DSS

GlobalSign’s Digital Signing Service issues a short-lived certificate per signature and signs the digest on request, so a signature handler built on it does three things the KMS handlers do not: it asks for an identity, embeds the OCSP response that came with that identity, and adds a timestamp token. The result is close to a PAdES B-T signature with LTV data in the same revision.

The client comes from github.com/unidoc/globalsign-dss and needs the API key and secret plus the mTLS certificate and key pair for the API endpoint:

c, err := globalsign.NewClient(apiKey, apiSecret, certFilepath, keyFilepath)

Identity per signature

InitSignature requests an identity, which returns the signing certificate, the CA chain and an OCSP response for the signing certificate:

identity, err := globalsign.DSSService.DSSGetIdentity(gsClient.DSSService, es.ctx,
    "GLOBALSIGN TEST ACCOUNT - FOR TESTING PURPOSE ONLY",
    &globalsign.IdentityRequest{SubjectDn: globalsign.SubjectDn{}})

The identity name is account-specific and has to be the same one used later for the signing request. The certificate and CA are PEM, decoded into a chain, put on the handler for later use and also written to the signature’s Cert array.

InitSignature finishes with es.Sign(sig, nil). A nil digest takes the early return in Sign and reserves sigLen zero bytes for the contents without calling the service, so exactly one signing request is made per signature, during appender.Write.

The OCSP response and the timestamp

Because the certificate is minted for this signature, its OCSP response is already at hand and gets embedded as an Adobe revocation attribute rather than fetched again later:

_, err := ocsp.ParseResponseForCert(es.ocsp, certs[0], certs[1])
if err != nil {
    return err
}

siConfig.ExtraSignedAttributes = []pkcs7.Attribute{{
    Type: pkcs7.OIDAttributeAdobeRevocation,
    Value: RevocationInfoArchival{
        Ocsp: []asn1.RawValue{{FullBytes: es.ocsp}},
    },
}}

This only happens when the chain has more than one certificate, since verifying the response needs the issuer. signedData.RequestSignerTimestampToken(0, ...) then adds a timestamp token to the first signer, obtained from the DSS timestamp endpoint rather than a public TSA.

LTV in the same revision

generateSignedFile signs and LTV enables in one write, which means EnableChain: the VRI entry cannot be built in the revision that creates the signature, because its key is derived from the signature contents. The clients are tuned before enabling:

ltv, err := model.NewLTV(appender)
if err != nil {
    return nil, err
}
ltv.CertClient.HTTPClient.Timeout = 30 * time.Second
ltv.OCSPClient.HTTPClient.Timeout = 30 * time.Second
ltv.CRLClient = nil

err = ltv.EnableChain(certChain)

The default timeout on all three clients is 5 seconds per request, which is tight for CRL downloads. Setting CRLClient to nil skips CRLs entirely, and setting OCSPClient to nil would skip OCSP the same way. See LTV in one revision for what EnableChain writes.

Limitations

Signer.Public() returns nil. That works because pkcs7 only needs the signer to sign here, but it means the same crypto.Signer cannot be used with x509.CreateCertificate, unlike the KMS examples.

The signature is padded to sigLen, 8192 in the example. A DSS signature with a timestamp token and an embedded OCSP response is considerably larger than a bare PKCS7 package, and copy truncates rather than failing, so raise sigLen before assuming a short signature is a service problem.

Every value the example passes as an identity name is a GlobalSign test account string. Replace them with your own before anything will validate.

Run the example

NewGlobalSignPdfSignature builds the handler, createSignatureField turns a SignOption into the field and appearance, and generateSignedFile does the signing, LTV and write.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/signatures
go run pdf_sign_external_globalsign.go input.pdf output.pdf API_KEY API_SECRET cert.pem key.pem

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on