CEF eSignature building block is very powerful tool. However it is daunting task to get started with it. Because of the complexity most of the smaller development agencies reject all project that has qualified electronic signature component. There are also big players with big budgets who can help you. For example Nowina (Developer of DSS, the EU eSignature reference) from Luxembourg and Nortal (Having a lot of practical experience) from Estonia.
This is the first post in coming series which will give you practical and usable samples to get you get started with digital signatures development in Europe according to eIDAS regulation. Main audience is experienced Java developers who have new project in electronic signature field in sights and want to get going without weeks of understanding the topic.
First lets list down digital signature building block resources.
- Source code for the samples can be found here https://github.com/eideasy/cef-esignature-building-block-samples
- Source code for the building block components https://github.com/esig/dss
- Documentation for the building block https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html
- Demonstration webapp. Signature validation part is especially useful in there https://ec.europa.eu/cefdigital/DSS/webapp-demo
We will be using Spring boot with Maven to build the working app. In this sample we will get as simple as possible DSS implementation working. We will upload PDF file and sign it with key in .p12 file.
First step is to include relevant components from DSS. There is special repository set up for that.
<properties>
<java.version>14</java.version>
<dss.groupId>eu.europa.ec.joinup.sd-dss</dss.groupId>
<dss.version>5.7</dss.version>
</properties>
<repositories>
<repository>
<id>cefdigital</id>
<name>cefdigital</name>
<url>https://ec.europa.eu/cefdigital/artifact/content/repositories/esignaturedss/</url>
</repository>
</repositories>
From the DSS repository we need to handpick modules that we need as different functionalities are in different modules. For our PDF signing PAdES signature we need the following: dss-enumerations, dss-service, dss-pades, dss-pades-pdfbox and dss-token. In POM it will looks like this:
<dependency>
<groupId>${dss.groupId}</groupId>
<artifactId>dss-enumerations</artifactId>
<version>${dss.version}</version>
</dependency>
<dependency>
<groupId>${dss.groupId}</groupId>
<artifactId>dss-service</artifactId>
<version>${dss.version}</version>
</dependency>
<dependency>
<groupId>${dss.groupId}</groupId>
<artifactId>dss-pades</artifactId>
<version>${dss.version}</version>
</dependency>
<dependency>
<groupId>${dss.groupId}</groupId>
<artifactId>dss-pades-pdfbox</artifactId>
<version>${dss.version}</version>
</dependency>
<dependency>
<groupId>${dss.groupId}</groupId>
<artifactId>dss-token</artifactId>
<version>${dss.version}</version>
</dependency>
Now we will create controller that will get the PDF file and sign it. This consists of 3 main operations
- Create data/digest that will be signed
- Create signature
- Attach the signature to the file
Part 1. Load unsigned file, private/public key pair and prepare data that will be signed.
// Load unsigned file and signing certificates from local file.
DSSDocument toSignDocument = new FileDocument("src/main/resources/test.pdf");
Pkcs12SignatureToken signingToken = new Pkcs12SignatureToken("src/main/resources/teststore.p12", new KeyStore.PasswordProtection("123456".toCharArray()));
// Exctract first private key from the keystore for signing.
List<DSSPrivateKeyEntry> keys = signingToken.getKeys();
DSSPrivateKeyEntry privateKey = null;
for (DSSPrivateKeyEntry entry : keys) {
privateKey = entry;
break;
}
CertificateToken signerCert = privateKey.getCertificate();
// Construct data to sign.
PAdESSignatureParameters parameters = new PAdESSignatureParameters();
parameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B);
parameters.setSigningCertificate(signerCert);
CommonCertificateVerifier commonCertificateVerifier = new CommonCertificateVerifier();
PAdESService service = new PAdESService(commonCertificateVerifier);
ToBeSigned dataToSign = service.getDataToSign(toSignDocument, parameters);
Part 2 and 3 – after this preparation comes signing and embedding the signature to the PDF
// Create signature and attach it to the PDF file.
SignatureValue signatureValue = signingToken.sign(dataToSign, DigestAlgorithm.SHA256, privateKey);
DSSDocument signedFile = service.signDocument(toSignDocument, parameters, signatureValue);
InputStreamResource resource = new InputStreamResource(signedFile.openStream());
Once the signature is created then I recommend using DSS demonstration web app to check the signature details. Since we are signing with self signed certificate then validation then we can see that signature itself is OK but it is not trusted.
Format Checking : PASSED
Does the signature format correspond to an expected format?
Is the signature identification not ambiguous?
Is only one SignerInfo present?
Identification of the Signing Certificate : PASSED
Is there an identified candidate for the signing certificate?
Is the signed attribute: 'signing-certificate' present?
Is the signed attribute: 'signing-certificate' present only once?
Is the signed attribute: 'cert-digest' of the certificate present?
Does the certificate digest value match a digest value found in the certificate reference(s)?
Are the issuer distinguished name and the serial number equal?
Validation Context Initialization : PASSED
Is the signature policy known?
Cryptographic Verification : PASSED
Has the reference data object been found?
Is the reference data object intact?
Is the signature intact?
Signature Acceptance Validation : PASSED
Is the structure of the signature valid?
Is the signed qualifying property: 'signing-time' present?
Is the signed qualifying property: 'message-digest' or 'SignedProperties' present?
Are signature cryptographic constraints met?
X509 Certificate Validation : NO_CERTIFICATE_CHAIN_FOUND
Can the certificate chain be built till a trust anchor?
Validation report will look like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:ValidationReport xmlns="http://www.w3.org/2000/09/xmldsig#" xmlns:ns2="http://uri.etsi.org/19102/v1.2.1#" xmlns:ns4="http://uri.etsi.org/02231/v2#" xmlns:ns3="http://uri.etsi.org/01903/v1.3.2#">
<ns2:SignatureValidationReport>
<ns2:SignatureIdentifier id="S-5C4751811F7DAA10165E6F5F20C38150C7FD6CE43DBC04DAA81BD092B58D0FE8">
<ns2:DigestAlgAndValue>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>/RCy+/sMPxZ4/rcYXUmBNNwMbgho74XVwNoD4cuZGc4=</DigestValue>
</ns2:DigestAlgAndValue>
<SignatureValue>MGUCMFFewSIgTgkQQ+8+iXupmYdwltMO767ryceBWwP0+jMD6jna/0CYIBYZlBv80xp6qgIxAK4SjgOhDNE1rYx0/E/XDzZEqN4nd5xk1kq81PBqRYgi262vFf2GE9d8fv41d3oRaA==</SignatureValue>
<ns2:HashOnly>false</ns2:HashOnly>
<ns2:DocHashOnly>false</ns2:DocHashOnly>
</ns2:SignatureIdentifier>
<ns2:ValidationConstraintsEvaluationReport>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:formatChecking</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:passed</ns2:MainIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:identificationOfTheSigningCertificate</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:passed</ns2:MainIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:validationContextInitialization</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:passed</ns2:MainIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:cryptographicVerification</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:passed</ns2:MainIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:signatureAcceptanceValidation</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:passed</ns2:MainIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:x509CertificateValidation</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:applied</ns2:Status>
</ns2:ConstraintStatus>
<ns2:ValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:indeterminate</ns2:MainIndication>
<ns2:SubIndication>urn:etsi:019102:subindication:NO_CERTIFICATE_CHAIN_FOUND</ns2:SubIndication>
</ns2:ValidationStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:pastSignatureValidation</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:disabled</ns2:Status>
</ns2:ConstraintStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:pastCertificateValidation</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:disabled</ns2:Status>
</ns2:ConstraintStatus>
</ns2:ValidationConstraint>
<ns2:ValidationConstraint>
<ns2:ValidationConstraintIdentifier>urn:cef:dss:bbb:validationTimeSliding</ns2:ValidationConstraintIdentifier>
<ns2:ConstraintStatus>
<ns2:Status>urn:etsi:019102:constraintStatus:disabled</ns2:Status>
</ns2:ConstraintStatus>
</ns2:ValidationConstraint>
</ns2:ValidationConstraintsEvaluationReport>
<ns2:ValidationTimeInfo>
<ns2:ValidationTime>2020-10-03T11:38:51Z</ns2:ValidationTime>
<ns2:BestSignatureTime>
<ns2:POETime>2020-10-03T11:38:51Z</ns2:POETime>
<ns2:TypeOfProof>urn:etsi:019102:poetype:validation</ns2:TypeOfProof>
</ns2:BestSignatureTime>
</ns2:ValidationTimeInfo>
<ns2:SignersDocument>
<ns2:DigestAlgAndValue>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>cpjLbxAN2PM7pKndJgVUTOL2f73RTboV8FoS1oe3/Fk=</DigestValue>
</ns2:DigestAlgAndValue>
<ns2:SignersDocumentRef VOReference="D-A193BC992189197AADC688720CAB67752CDD4F16B9FE7FBFD245CC85AEE62C06"/>
</ns2:SignersDocument>
<ns2:SignatureAttributes>
<ns2:SigningTime Signed="true">
<ns2:Time>2020-10-03T11:37:59Z</ns2:Time>
</ns2:SigningTime>
<ns2:SigningCertificate Signed="true">
<ns2:AttributeObject VOReference="C-C16F624C905A24FB047895B961630DF859A9A0BB0A7EE029E140EFF2162FC545"/>
</ns2:SigningCertificate>
<ns2:DataObjectFormat Signed="true">
<ns2:ContentType>1.2.840.113549.1.7.1</ns2:ContentType>
</ns2:DataObjectFormat>
<ns2:CompleteCertificateRefs/>
<ns2:CompleteRevocationRefs/>
<ns2:AttributeCertificateRefs/>
<ns2:AttributeRevocationRefs/>
<ns2:MessageDigest Signed="true">
<ns2:Digest>/RCy+/sMPxZ4/rcYXUmBNNwMbgho74XVwNoD4cuZGc4=</ns2:Digest>
</ns2:MessageDigest>
<ns2:SubFilter>
<ns2:SubFilterElement>ETSI.CAdES.detached</ns2:SubFilterElement>
</ns2:SubFilter>
<ns2:ByteRange>0 8094 27040 494</ns2:ByteRange>
<ns2:Filter>
<ns2:Filter>Adobe.PPKLite</ns2:Filter>
</ns2:Filter>
</ns2:SignatureAttributes>
<ns2:SignerInformation>
<ns2:SignerCertificate VOReference="C-C16F624C905A24FB047895B961630DF859A9A0BB0A7EE029E140EFF2162FC545"/>
<ns2:Signer>eID Easy</ns2:Signer>
</ns2:SignerInformation>
<ns2:SignatureQuality>
<ns2:SignatureQualityInformation>urn:cef:dss:signatureQualification:notApplicable</ns2:SignatureQualityInformation>
</ns2:SignatureQuality>
<ns2:SignatureValidationProcess>
<ns2:SignatureValidationProcessID>urn:etsi:019102:validationprocess:LTA</ns2:SignatureValidationProcessID>
</ns2:SignatureValidationProcess>
<ns2:SignatureValidationStatus>
<ns2:MainIndication>urn:etsi:019102:mainindication:indeterminate</ns2:MainIndication>
<ns2:SubIndication>urn:etsi:019102:subindication:NO_CERTIFICATE_CHAIN_FOUND</ns2:SubIndication>
<ns2:AssociatedValidationReportData>
<ns2:CertificateChain>
<ns2:SigningCertificate VOReference="C-C16F624C905A24FB047895B961630DF859A9A0BB0A7EE029E140EFF2162FC545"/>
</ns2:CertificateChain>
<ns2:CryptoInformation>
<ns2:ValidationObjectId VOReference="S-5C4751811F7DAA10165E6F5F20C38150C7FD6CE43DBC04DAA81BD092B58D0FE8"/>
<ns2:Algorithm>http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256</ns2:Algorithm>
<ns2:SecureAlgorithm>true</ns2:SecureAlgorithm>
<ns2:NotAfter>2025-12-31T23:00:00Z</ns2:NotAfter>
</ns2:CryptoInformation>
</ns2:AssociatedValidationReportData>
</ns2:SignatureValidationStatus>
</ns2:SignatureValidationReport>
<ns2:SignatureValidationObjects>
<ns2:ValidationObject id="C-C16F624C905A24FB047895B961630DF859A9A0BB0A7EE029E140EFF2162FC545">
<ns2:ObjectType>urn:etsi:019102:validationObject:certificate</ns2:ObjectType>
<ns2:ValidationObjectRepresentation>
<ns2:DigestAlgAndValue>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>wW9iTJBaJPsEeJW5YWMN+FmpoLsKfuAp4UDv8hYvxUU=</DigestValue>
</ns2:DigestAlgAndValue>
</ns2:ValidationObjectRepresentation>
<ns2:POE>
<ns2:POETime>2020-10-03T11:38:51Z</ns2:POETime>
<ns2:TypeOfProof>urn:etsi:019102:poetype:validation</ns2:TypeOfProof>
</ns2:POE>
</ns2:ValidationObject>
<ns2:ValidationObject id="D-A193BC992189197AADC688720CAB67752CDD4F16B9FE7FBFD245CC85AEE62C06">
<ns2:ObjectType>urn:etsi:019102:validationObject:signedData</ns2:ObjectType>
<ns2:ValidationObjectRepresentation>
<ns2:DigestAlgAndValue>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>cpjLbxAN2PM7pKndJgVUTOL2f73RTboV8FoS1oe3/Fk=</DigestValue>
</ns2:DigestAlgAndValue>
</ns2:ValidationObjectRepresentation>
<ns2:POE>
<ns2:POETime>2020-10-03T11:38:51Z</ns2:POETime>
<ns2:TypeOfProof>urn:etsi:019102:poetype:validation</ns2:TypeOfProof>
</ns2:POE>
</ns2:ValidationObject>
</ns2:SignatureValidationObjects>
</ns2:ValidationReport>