Class GlobalSignDSSManager
- java.lang.Object
-
- org.faceless.pdf2.GlobalSignDSSManager
-
public class GlobalSignDSSManager extends java.lang.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.28To 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")
ornew X500Principal("OU=nnn")
- the type of account with GlobalSign will determine thevalidation policy
which determines which fields are allowed. This is shown in this exampleimport 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 Summary
Constructors Constructor Description GlobalSignDSSManager()
Create a new GlobalSign Digital Signature Service Manager using the "DSS" service.GlobalSignDSSManager(java.lang.String service)
Create a new GlobalSignDSSManager using the specified service.
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description java.lang.String
addQSSUser(java.lang.String givenName, java.lang.String familyName, java.lang.String email, java.lang.String mobileNumber)
Request the creation of a new user within the GlobalSign Qualified Signing Service system.SignatureHandlerFactory
createSignatureHandlerFactory(java.lang.String subject)
Creata a newSignatureHandlerFactory
which will sign PDFs with credentials extracted fom the supplied String.SignatureHandlerFactory
createSignatureHandlerFactory(java.security.cert.X509Certificate cert)
Creata a newSignatureHandlerFactory
which will sign PDFs with credentials extracted fom the supplied X.509 Certificate.SignatureHandlerFactory
createSignatureHandlerFactory(javax.security.auth.x500.X500Principal subject)
Creata a newSignatureHandlerFactory
which will sign PDFs with credentials extracted fom the supplied X.500 distinguished name.void
deleteQSSUser(java.lang.String email)
Request the deletion of a user within the GlobalSign Qualified Signing Service system.java.lang.String
getAPIKey()
Return the API key as set insetLogin(java.security.KeyStore, char[], byte[])
java.lang.String
getQSSUser(java.lang.String email)
Request the registered details for a user within the GlobalSign Qualified Signing Service system.java.lang.String
getService()
Return the type used in the constructor; DSS or QSSjava.lang.String
getURL()
Return the URL as set bysetURL(java.lang.String)
java.lang.String
getValidationPolicy()
Return the Validation Policy for this account.void
setCustomAppearance(PDFCanvas canvas, float x1, float y1, float x2, float y2)
Set a custom appearance for this signature.void
setLogin(java.security.KeyStore keystore, char[] password, byte[] encrypted)
Set the Login credentials to use this account.void
setLogin(java.security.KeyStore keystore, char[] password, java.lang.String apikey, java.lang.String apisecret)
Set the Login credentials to use this account.void
setURL(java.lang.String url)
Set the URL prefix to use for all requests, for example "https://dss.globalsign.com:8443".
-
-
-
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(java.lang.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 java.lang.String getService()
Return the type used in the constructor; DSS or QSS- Returns:
- the type
- Since:
- 2.28.
-
setURL
public void setURL(java.lang.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
-
getURL
public java.lang.String getURL()
Return the URL as set bysetURL(java.lang.String)
- Returns:
- the URL
- Since:
- 2.28
-
setLogin
public void setLogin(java.security.KeyStore keystore, char[] password, byte[] encrypted) throws java.security.GeneralSecurityException, java.io.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 certificatepassword
- the password for that KeyStoreencrypted
- the encrypted API credentials, as supplied by GlobalSign- Throws:
java.security.GeneralSecurityException
- if there is an issue accessing the appropriate certificate from the KeyStorejava.io.IOException
- if there is an IO exception or if globalsign rejected the transaction
-
setLogin
public void setLogin(java.security.KeyStore keystore, char[] password, java.lang.String apikey, java.lang.String apisecret) throws java.security.GeneralSecurityException, java.io.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 certificatepassword
- the password for that KeyStoreapikey
- the "API key", as supplied by GlobalSignapisecret
- the "API secret", as supplied by GlobalSign- Throws:
java.security.GeneralSecurityException
- if there is an issue accessing the appropriate certificate from the KeyStorejava.io.IOException
- if there is an IO exception or if globalsign rejected the transaction
-
getAPIKey
public java.lang.String getAPIKey()
Return the API key as set insetLogin(java.security.KeyStore, char[], byte[])
- Returns:
- the key
- Since:
- 2.28
-
addQSSUser
public java.lang.String addQSSUser(java.lang.String givenName, java.lang.String familyName, java.lang.String email, java.lang.String mobileNumber) throws java.io.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 theuser_id
is in that response.- Parameters:
givenName
- the users given name. RequiredfamilyName
- the users family name. Requiredemail
- the users email, which will be used as a login key and is case-sensitive. RequiredmobileNumber
- 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:
java.io.IOException
- if communications with the QSS service fails- Since:
- 2.28
-
getQSSUser
public java.lang.String getQSSUser(java.lang.String email) throws java.io.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:
java.io.IOException
- if communications with the QSS service fails- Since:
- 2.28
-
deleteQSSUser
public void deleteQSSUser(java.lang.String email) throws java.io.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:
java.io.IOException
- if communications with the QSS service fails- Since:
- 2.28
-
getValidationPolicy
public java.lang.String getValidationPolicy() throws java.io.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:
java.io.IOException
-
createSignatureHandlerFactory
public SignatureHandlerFactory createSignatureHandlerFactory(java.security.cert.X509Certificate cert) throws java.io.IOException, java.security.cert.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:
java.lang.IllegalStateException
- if this is a QSS instance, as this only applies to DSSjava.io.IOException
- if an exception occurs during setupjava.security.cert.CertificateException
- if an exception occurs during extraction of the identify from the certificate
-
createSignatureHandlerFactory
public SignatureHandlerFactory createSignatureHandlerFactory(javax.security.auth.x500.X500Principal subject) throws java.io.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:
java.lang.IllegalStateException
- if this is a QSS instance, as this only applies to DSSjava.io.IOException
- if an exception occurs during setup
-
createSignatureHandlerFactory
public SignatureHandlerFactory createSignatureHandlerFactory(java.lang.String subject) throws java.io.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
orX.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:
java.io.IOException
- if an exception occurs during setup
-
For DSS, the String must be JSON-formatted and describe the structure from the
GlobalSign specification. We would generally recommend an
-
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 texty1
- the bottom-most Y co-ordinate to place the (optional) certificate textx2
- the right-most X co-ordinate to place the (optional) certificate texty2
- the top-most Y co-ordinate to place the (optional) certificate text- Since:
- 2.28
-
-