Class GlobalSignDSSManager


  • public class GlobalSignDSSManager
    extends Object

    This class creates a SignatureHandlerFactory that makes use of GlobalSign's Digital Signing Service to sign documents remotely. There are two variations on this service; the original "DSS" added in revision 2.22, and the Qualified Signing Service or "QSS" variation added in release 2.28

    To use this class you will need login credentials and client certificate issued by GlobalSign. The client (or "mTLS") certificate must be stored in a KeyStore. You will also need details of the identity you wish to use to sign the PDF.

    DSS

    The "DSS" service is the original version of this API. An identity may be stored as an X.500 Distinguished Name, or extracted from an X.509 Certificate. Which is more appropriate wlil depend on the type of account that you have with GlobalSign, but as of 2020 we believe the most common way will be to simply specify an identity as an X.500 Principal, for example new X500Principal("CN=nnn") or new X500Principal("OU=nnn") - the type of account with GlobalSign will determine the validation policy which determines which fields are allowed. This is shown in this example

     import org.faceless.pdf2.*;
     import java.io.*;
     import java.security.*;
     import javax.security.auth.x500.X500Principal;
    
     String apikey = "apikey";                                   // supplied by GlobalSign
     String apisecret = "apisecret";                             // supplied by GlobalSign
     String keystorePath = "keystore.pkcs12";                    // path to KeyStore with client certificate
     char[] keystorePassword = "password".toCharArray();         // password for KeyStore
     X500Principal identity = new X500Principal("CN=identity");  // the user identity
    
     // Setup
     GlobalSignDSSManager gs = new GlobalSignDSSManager();
     KeyStore keystore = KeyStore.getInstance("PKCS12");
     keystore.load(new FileInputStream(keystorePath), keystorePassword);
     gs.setLogin(keystore, password, apikey, apisecret);
    
     // Sign
     FormSignature sig = new FormSignature();
     pdf.getForm().getElements().put("Sig1", sig);
     sig.sign(null, null, null, gs.createSignatureHandlerFactory(identity);
     

    QSS

    The qualified service was launched by GlobalSign in early 2023, and is a bit more interesting, as the workflow involves using a mobile phone to approve the signature. The signer identity is an email address, which must be registered (along with a mobile phone number) with GlobalSign before signing. Any signatures signed with that identity must be approved by the user with the GlobalSign QSS app (for iOS or Android) in order to complete; the PDF.render(java.io.OutputStream) method will wait until this is done or the request times-out.

    Registering users, retrieving user details and deleting users can all be done with this API, although we're not aiming to be a full-fledged user management system so the interface is intentionally simple. The workflow and signing process is described in this example.

     import org.faceless.pdf2.*;
     import java.io.*;
     import java.security.*;
    
     String apikey = "apikey";                             // supplied by GlobalSign
     String apisecret = "apisecret";                       // supplied by GlobalSign
     String keystorePath = "keystore.pkcs12";              // path to KeyStore with client certificate
     char[] keystorePassword = "password".toCharArray();   // password for KeyStore
     String email = "test@test.com";                       // signer is identitifed by an email address
    
     // Setup
     GlobalSignDSSManager gs = new GlobalSignDSSManager("QSS");   // note "QSS"
     KeyStore keystore = KeyStore.getInstance("PKCS12");
     keystore.load(new FileInputStream(keystorePath), keystorePassword);
     gs.setLogin(keystore, password, apikey, apisecret);
    
     if (gs.getQSSUser(email) == null) {
         // Identity is not registered - it can be registered with this API if required
         String firstName = "Max";
         String lastName = "Mustermann";
         String mobile = "+447000123456";
         String id = gs.adQSSUser(firstName, lastName, email, mobile);
         System.out.println("User ID = " + id);
         // the user now downloads the GlobalSign QSS mobile app
         // and registers with this User ID. They confirm their identity
         // by entering codes sent to both their email and phone number,
         // and once confirmed they will be able to sign.
     } else {
         // Identity is registered, they can sign
         PDF pdf = new PDF(...);
         FormSignature sig = new FormSignature();
         pdf.getForm().getElements().put("Sig1", sig);
         sig.sign(null, null, null, gs.createSignatureHandlerFactory(email));
         pdf.render(outputstream);
         // actual signing occurs during render: the mobile app will receive
         // an alert, and assuming the user uses the app to approve the signing
         // request within the allotted time, the PDF is signed and pdf.render()
         // completes as normal. If not, the request will time-out and
         // pdf.render() will fail with an IOException containing the details.
     }
     

    Signatures created by this class are "Long-Term Validated" by default; they include a TimeStamp and all the necessary OCSP responses (all of which are, in theory, supplied by GlobalSign as part of the protocol). The hash algorithm is SHA-256 and as we understand the setup in 2023, all keys used with this service are RSA.

    Since:
    2.22, with QSS added in 2.28
    • Constructor Detail

      • GlobalSignDSSManager

        public GlobalSignDSSManager()
        Create a new GlobalSign Digital Signature Service Manager using the "DSS" service. The returned object is safe for use across multiple threads, and does not expire.
      • GlobalSignDSSManager

        public GlobalSignDSSManager​(String service)
        Create a new GlobalSignDSSManager using the specified service. The returned object is safe for use across multiple threads, and does not expire.
        Parameters:
        service - the service, either "DSS" or "QSS"
    • Method Detail

      • getService

        public String getService()
        Return the type used in the constructor; DSS or QSS
        Returns:
        the type
        Since:
        2.28.
      • setURL

        public void setURL​(String url)
        Set the URL prefix to use for all requests, for example "https://dss.globalsign.com:8443". This is not typically required, as the correct value (at the time of implementation) is set by default.
        Parameters:
        url - the URL prefix
      • setLogin

        public void setLogin​(KeyStore keystore,
                             char[] password,
                             byte[] encrypted)
                      throws GeneralSecurityException,
                             IOException
        Set the Login credentials to use this account. This must be done before any any communication with the signing service; this includes user-management as well as signing.
        Parameters:
        keystore - the KeyStore containing the GlobalSign client certificate
        password - the password for that KeyStore
        encrypted - the encrypted API credentials, as supplied by GlobalSign
        Throws:
        GeneralSecurityException - if there is an issue accessing the appropriate certificate from the KeyStore
        IOException - if there is an IO exception or if globalsign rejected the transaction
      • setLogin

        public void setLogin​(KeyStore keystore,
                             char[] password,
                             String apikey,
                             String apisecret)
                      throws GeneralSecurityException,
                             IOException
        Set the Login credentials to use this account. This must be done before any any communication with the signing service; this includes user-management as well as signing.
        Parameters:
        keystore - the KeyStore containing the GlobalSign client certificate
        password - the password for that KeyStore
        apikey - the "API key", as supplied by GlobalSign
        apisecret - the "API secret", as supplied by GlobalSign
        Throws:
        GeneralSecurityException - if there is an issue accessing the appropriate certificate from the KeyStore
        IOException - if there is an IO exception or if globalsign rejected the transaction
      • addQSSUser

        public String addQSSUser​(String givenName,
                                 String familyName,
                                 String email,
                                 String mobileNumber)
                          throws IOException

        Request the creation of a new user within the GlobalSign Qualified Signing Service system. All parameters are required; the returned value is short alphanumeric String used as the "User ID", which is supplied to the GlobalSign QSS mobile app to register the user. Once registered, the email address can be used as a key to initiate signing.

        Note: During testing we found that sometimes the user ID was not returned from this method - if that happens, wait a few seconds before calling getQSSUser(java.lang.String), as the user_id is in that response.

        Parameters:
        givenName - the users given name. Required
        familyName - the users family name. Required
        email - the users email, which will be used as a login key and is case-sensitive. Required
        mobileNumber - the users full international mobile number, a "+" followed by digits. Required.
        Returns:
        a "User ID" for use with the GlobalSign QSS app, or null if none was returned.
        Throws:
        IOException - if communications with the QSS service fails
        Since:
        2.28
      • getQSSUser

        public String getQSSUser​(String email)
                          throws IOException

        Request the registered details for a user within the GlobalSign Qualified Signing Service system. If a user with the supplied email address has previously been registered, return a JSON-formatted String containing the known details of that user.

        Parameters:
        email - the users email, which used as a login key and is case-sensitive. Required
        Returns:
        a JSON-formatted string containing the details of the user, or null if the user is not found
        Throws:
        IOException - if communications with the QSS service fails
        Since:
        2.28
      • deleteQSSUser

        public void deleteQSSUser​(String email)
                           throws IOException

        Request the deletion of a user within the GlobalSign Qualified Signing Service system.

        Parameters:
        email - the users email, which used as a login key and is case-sensitive. Required
        Throws:
        IOException - if communications with the QSS service fails
        Since:
        2.28
      • getValidationPolicy

        public String getValidationPolicy()
                                   throws IOException

        Return the Validation Policy for this account. The login method must have been called already. The Validation Policy is a serialized JSON object containing details about which fields can or cannot be set in the "identity" passed to GlobalSign. A full description of this datastructure is not offered here, but a high-level description is available in GlobalSign's guide to this service.

        If attempts to sign return an HTTP 422 Exception, this value should be consulted to determine which fields are disallowed in the request.

        Returns:
        the validation policy as a JSON-formatted String
        Throws:
        IOException
      • createSignatureHandlerFactory

        public SignatureHandlerFactory createSignatureHandlerFactory​(X509Certificate cert)
                                                              throws IOException,
                                                                     CertificateException

        Creata a new SignatureHandlerFactory which will sign PDFs with credentials extracted fom the supplied X.509 Certificate. The certificate itself is used only as a source for the identity data sent to GlobalSign, so can be self-signed or unsigned.

        The following data is extracted from the certificate:

        • Subject distinguished name
        • Subject alternative names (optional)
        • Extended key usage (optional)
        • Subject directory attributes from extension 2.5.29.9 (see RFC3739) (optional)
        • Subject Qualified Statements from extension 1.3.6.1.5.5.7.1.3: specifically those defined in ETSI EN 319 412-5 V2.1.1 (optional)

        The returned object can be used to sign multiple signatures in multiple threads simultaneously.

        Only applies to the "DSS" service

        Parameters:
        cert - an X.509 Certificate to extract the identity details from.
        Returns:
        a SignatureHandlerFactory which can be used to sign PDFs
        Throws:
        IllegalStateException - if this is a QSS instance, as this only applies to DSS
        IOException - if an exception occurs during setup
        CertificateException - if an exception occurs during extraction of the identify from the certificate
      • createSignatureHandlerFactory

        public SignatureHandlerFactory createSignatureHandlerFactory​(X500Principal subject)
                                                              throws IOException

        Creata a new SignatureHandlerFactory which will sign PDFs with credentials extracted fom the supplied X.500 distinguished name. The returned object can be used to sign multiple signatures in multiple threads simultaneously.

        Only applies to the "DSS" service

        Parameters:
        subject - an X.500 distinguished name which will be used as the source of the identity details.
        Returns:
        a SignatureHandlerFactory which can be used to sign PDFs
        Throws:
        IllegalStateException - if this is a QSS instance, as this only applies to DSS
        IOException - if an exception occurs during setup
      • createSignatureHandlerFactory

        public SignatureHandlerFactory createSignatureHandlerFactory​(String subject)
                                                              throws IOException

        Creata a new SignatureHandlerFactory which will sign PDFs with credentials extracted fom the supplied String.

        • For DSS, the String must be JSON-formatted and describe the structure from the GlobalSign specification. We would generally recommend an X.509 Certificate or X.500 Principal be used instead as there is less room for error. However a String can be supplied as described in the GlobalSign specification, eg {"subject_dn":{"organizational_unit":["Administration"]}}.
        • For QSS, the String must be an email address previously registered with the service.

        The returned object can be used to sign multiple signatures in multiple threads simultaneously.

        Parameters:
        identity - the identity to be used with the service.
        Returns:
        a SignatureHandlerFactory which can be used to sign PDFs
        Throws:
        IOException - if an exception occurs during setup
      • setCustomAppearance

        public void setCustomAppearance​(PDFCanvas canvas,
                                        float x1,
                                        float y1,
                                        float x2,
                                        float y2)

        Set a custom appearance for this signature. This method allows you to add a signature, thumbprint, logo or any other form of custom image to your signature annotations (technically, this method sets the "n2" layer of the annotation). The canvas may be of any size, and will be scaled to fit the annotation on the page.

        Care needs to be taken when setting this value. It's not recommended to use a canvas containing a bitmap image with an opaque background, as this may mask any layers behind this one. In particular, a "?" is typically used on a layer below this one to indicate the signature has not been verified. Specifying an opaque image may result in this being obscured.

        As a optional convenience, by specifying non-zero coordinates for the x1,y1,x2 and y2 values, some text describing the signing certificate will be added to the canvas at the specified location. The format for this is fixed, but as it doesn't have to be included, the developer is free to add his or her own text if they don't like the result, simply by setting all four values to zero.

        As an example, the default PKCS7 appearance is set with the following code which loads a pre-defined pattern from a resources file:

           setCustomAppearance(new PDFCanvas("logo.Adobe", 1), 0, 35, 100, 65);
         
        Parameters:
        canvas - the canvas to display as the "n2" layer of the signature appearance.
        x1 - the left-most X co-ordinate to place the (optional) certificate text
        y1 - the bottom-most Y co-ordinate to place the (optional) certificate text
        x2 - the right-most X co-ordinate to place the (optional) certificate text
        y2 - the top-most Y co-ordinate to place the (optional) certificate text
        Since:
        2.28