libdigidocpp 4.6.0
Libdigidocpp Programmer's Guide

Introduction

Libdigidocpp is a C++ library for creating applications that create and verify digital signatures. Digitally signed files are created as Associated Signature Containers (ASiC), using the .asice file extension and XML Advanced Electronic Signatures (XAdES) standardized by the European Telecommunications Standards Institute (ETSI).

Additionally, the libdigidocpp library can create, save, read, validate, and extend digitally timestamped containers (using the .asics file extension) with a single data file and RFC 3161 time-stamp tokens. ASiC containers with CAdES signatures, PDF, and DDOC files can be validated with the SiVa service.

Development of the library can be monitored in GitHub environment: https://github.com/open-eid/libdigidocpp.

Changes by version are listed on the separate Release Notes page. Supported container formats are described on the separate Supported container formats page. Configuration is described on the separate Configuration guide page. Implementation notes are described on the separate Implementation notes page.

About DigiDoc

Libdigidocpp library forms a part of the wider DigiDoc system framework which offers a full-scale architecture for digital signature and documents, consisting of software libraries (C++ and Java), SiVa service and end-user applications such as DigiDoc4 according to the following figure:

DigiDoc framework

It is easy to integrate DigiDoc components into existing applications in order to allow for creation, handling, forwarding and verification of digital signatures. All applications share common digitally signed file formats.

Overview

Supported functional properties

Libdigidocpp is a library of C++ classes offering the functionality of handling digitally signed files in supported DigiDoc formats. The following functions are implemented:

  • creating containers in supported DigiDoc formats and adding data files;
  • creating digital signatures using smart cards or other supported cryptographic tokens;
  • adding time marks and validity confirmations to digital signatures using OCSP and time-stamping protocols;
  • validating the digital signatures;
  • using trust service status lists (TSL) as trust anchors for certificate validation;
  • extracting data files from a container;
  • removing signatures and data files from a container.

The following table gives overview of functional features that are supported with Libdigidocpp.

FeatureSupported values
DigiDoc document format
  • ASiC-E - the main document format to be used.
  • ASiC-S - a single-data-file container. Opening and validating XAdES signatures are supported; this variant is read-only. For RFC 3161 TimeStampToken containers, creation, saving, validation, and archive-manifest time-stamp extension are supported. A TimeStampToken container that encloses a legacy DDoc file also exposes the DDoc's data files and signatures, validated by the SiVa service (see ASiC-S container format).
  • BDOC 2.1 - legacy read-only compatibility.
Note
BDOC 1.0 file format is not supported (more info can be found from https://www.id.ee/en/article/digidoc-container-format-life-cycle-2/).
Signature profileSignature profiles are based on the profiles defined by XAdES (XAdES).
  • time-stamp (TS) - signature profile in case of which the certificate validity information is added to the signature with an RFC 6960 OCSP confirmation; the signing time information is added with an RFC 3161 time-stamp token retrieved from a time-stamping service. In this case, signature creation time is regarded as the issuance time of the time-stamp token (RFC 3161 TSTInfo.genTime).
  • time-mark (TM) - certificate validity and signing time information is added to the signature with a time-mark - a special OCSP confirmation in case of which the hash value of the binary value of the signature (along with hash algorithm identifier in case of BDOC 2.1 document format) must be present in the "nonce" field of the OCSP confirmation. In this case, signature creation time is regarded as the issuance time of the OCSP confirmation (producedAt value in the confirmation), additional time-stamp token is not required.
  • archive time-stamp (TSA/LTA) - the signature and its accompanying validation data are time-stamped to provide long-term validity. Eligible ASiC-E XAdES signatures receive an ArchiveTimeStamp element. ASiC-S TimeStampToken signatures are extended with an ASiC archive manifest and another RFC 3161 time-stamp token.
Trust anchorsInformation of trusted CA certificates (trust anchors) is used to validate the trustworthiness of certificates used in the signature. The signer certificate's CA, OCSP responder certificate and time-stamping service's certificate (in case of TS signature profile) must be trusted. Trusted certificates' information is obtained from TSL list (Trust Service status list), the trusted certificates' list is retrieved from a signed TSL list that provides information about the status and the status history of the trust services (including certification, OCSP confirmation and time-stamping services). The European Commission's TSL list is used, more information of which can be found from https://ec.europa.eu/information_society/policy/esignature/trusted-list/. For the TSL specification document, see also Trusted Lists. For more information about the TSL implementation and configuration possibilities in Libdigidocpp library, see TSL list usage in Libdigidocpp and Trust anchor/TSL settings.
Signature creation module
  • PKCS#11 – in Linux and macOS environments, the default module for singing with smart card (e.g. Estonian ID card or any other smartcard provided that you have the external native PKCS#11 driver for it).
  • Windows CryptoAPI – the default module for signing with smart card in Windows environment. By default, a dialog window is opened for the user to choose the signing certificate and enter PIN code.
Cryptographic token type
  • Smart card, e.g. Estonian ID card. Supported signature creation modules are PKCS#11 and Windows CryptoAPI.
    Note
    Usage of SafeNet USB tokens with a Digital Stamp certificate (https://www.skidsolutions.eu/en/services/Digital-stamp) has been tested indirectly with Libdigidocpp through the DigiDoc desktop application, which uses Libdigidocpp as a base layer.
Public-key algorithm
  • RSA
  • ECDSA

Component model

The figure below describes the architecture of software and hardware components that are used when creating signatures with Libdigidocpp library.

Components used in Libdigidocpp implementation when signing with smart card
ComponentDescription
PKCS#11Widely adopted platform-independent API to cryptographic tokens (HSMs, smart cards and USB tokens), a standard management module of the cryptographic token and its certificates
CryptoAPIMicrosoft Cryptography API. Programming API for implementing cryptographic functions in Windows environment.
PC/SCStandard communication interface between the computer and the smart card, a cross-platform API for accessing smart card readers
IFDHandlerInterface Device Handler for CCID readers
CCIDUSB driver for Chip/Smart Card Interface Devices
ReaderDevice used for communication with a smart card

Using Libdigidocpp API

Note that Libdigidocpp uses internal memory buffers in case of all the operations, so that intermediary data is not written to temporary files on the disk. Also, the data files to be added to a DigiDoc container can be read from a data stream and later extracted from the container to a stream so that the data can be kept in memory.

String encoding

All std::string values passed to or returned from the API are UTF-8 encoded. This applies to file paths, media types, and all other string parameters and return values throughout the library.

Initialization

Libdigidocpp's initialization method conducts the following operations:

  1. initializes dependent libraries (see also Software libraries)
  2. loads configuration settings from default configuration files
  3. initializes trust anchors: initializes TSL lists. See TSL initialization process for more information about the TSL initialization process.

If you would like to use non-default configuration settings then call out the configuration file's initialization before initializing the library, for example:

// Optionally initialize global configuration file to use non-default settings
digidoc::Conf::init(new digidoc::XmlConf("<file-path-and-name>"));
// Initialize the library
static void init(Conf *conf)
Definition Conf.cpp:63
XML Configuration class.
Definition XmlConf.h:30
DIGIDOCPP_EXPORT void initialize(const std::string &appInfo="libdigidocpp", initCallBack callBack=nullptr)
Definition Container.cpp:91

The digidoc::XmlConf class must be used in order to access all the configuration methods that are available.

Note
If you would like to use different configuration values that are fixed as constants in the digidoc::Conf configuration class then it is possible to extend the class and override the values that need to be reset.

Creating and signing a DigiDoc document (local signing)

Creating a DigiDoc container

Create a new container object and specify the DigiDoc document's type, for example:

auto doc = digidoc::Container::createPtr("<output-file's-path>"); // create new container
static std::unique_ptr< Container > createPtr(const std::string &path)
Definition Container.cpp:317

Container class is used to incorporate the data of a DigiDoc document.

Creating and timestamping an ASiC-S container

Create an ASiC-S container by using an .asics or .scs output file name. An ASiC-S TimeStampToken container supports exactly one data file and one initial RFC 3161 time-stamp token.

auto doc = digidoc::Container::createPtr("timestamped-document.asics");
doc->addDataFile("document.txt", "text/plain");
digidoc::Signer *signer = ...;
signer->setProfile("TimeStampToken"); // "time-stamp-token" is also accepted
digidoc::Signature *timestamp = doc->sign(signer);
timestamp->validate();
doc->save();
Signature interface. Provides interface for handling a signature and the corresponding OCSP response ...
Definition Signature.h:37
virtual void validate() const =0
Signer interface. Provides interface for signing documents.
Definition Signer.h:32

ASiC-S TimeStampToken creation uses the one-step digidoc::Container::sign method. External two-step signing with digidoc::Container::prepareSignature is not supported for this format. Additional archive-manifest time-stamp layers can be added with the workflow described in Extending container validity.

Adding data files

Data files can be added to a DigiDoc container in two alternative ways:

  1. adding the data from an input stream (i.e. the data file contents can be read from internal memory buffer):
    std::unique_ptr<std::istream> is = ...;
    void digidoc::Container::addDataFile(std::move(is), // input stream
    const std::string &fileName, // file name that is written to the container
    const std::string &mediaType); // mime type of the data file
    virtual void addDataFile(const std::string &path, const std::string &mediaType)=0
  2. adding the data by reading the it from file system
    void digidoc::Container::addDataFile(const std::string &path, //data file's name and path in file system
    const std::string &mediaType); // mime type of the data file

Parameter mediaType in the methods above stands for a MIME type of the data file, for example "text/plain" or "application/msword". Value "application/octet-stream" is used by default. Calling out any of the methods listed above shall create a new DataFile object and add it to the DigiDoc container's data file collection. Note that in order to add a data file to a container, the container has to be unsigned and there shouldn't be an existing data file with the same name in the container. If a container is signed then it is possible to add data files to it only after the signatures are removed.

The stream overload's fileName is the name stored inside the container. It must be a single non-empty path component: ".", "..", forward slashes, and backslashes are rejected. The file-system overload accepts a source path and stores only its base name. When an existing ASiC-E container is opened, safe relative subdirectories separated by forward slashes are accepted, but absolute paths, backslashes, trailing slashes, and empty, ".", or ".." path segments are rejected. ASiC-S data files must be at the container root.

Warning
- It is important to pay attention that the data file's mime type value in manifest.xml file and in signatures*.xml file's <DataObjectFormat><MimeType> element are the same, otherwise the signature is invalid! In case of the ordinary signature creation process, the library sets the correct value automatically. However, if you create a ASiC-E container with Libdigidocpp library and want to add signatures*.xml file that you have received from another source then make sure that the data files' mime-type values in manifest-xml file and in signatures*.xml file are the same.
- Data file’s mime-type value must be formatted as specified in RFC 2045, section 5.1, i.e. the "type" and "subtype" values must be separated with a forward slash character.
- It is recommended not to use special characters in the data file’s name, i.e. it is suggested to only use the characters that are categorized as "unreserved" according to RFC 3986.
Note
By default, it is recommended to use data file mime-type value "application/octet-stream" for all file types.
For resource-exhaustion protection, structural and other non-data ZIP entries, such as manifests and signature XML, are limited to 10 MiB when read into memory. HTTP service responses are also limited to 10 MiB after decompression. Data files use a separate streaming and temporary-file strategy and are not subject to the 10 MiB metadata limit.

Adding signatures

It is possible to add a signature to a container only if it contains at least one data file, multiple signatures can be added to a single container. The signer's certificate and PIN code to access the private signature key are required during signing.

Note
Libdigidocpp library uses TSL lists (Trust Service Status list) to obtain information about trusted certificates during signature creation process. See also TSL overview, TSL standard and Configuring TSL settings.

Signing can be done by using PKCS#11 module for accessing the signature token. PKCS#11 module is the default module for singing with smart card (e.g. Estonian ID card or any other smart card provided that you have the external native language PKCS#11 driver for it). See also PKCS#11 settings.

digidoc::PKCS11Signer *signer = new digidoc::PKCS11Signer("<PKCS11-driver-path>");
// optionally specify PKCS#11 driver's location. If left unspecified then location is
// looked up from configuration file's parameter "pkcs11.driver.path".
std::string pin = "<pin-code>"; //PIN2 in case of Estonian ID cards
signer->setPin(pin);
Implements Signer interface for ID-Cards, which support PKCS#11 protocol.
Definition PKCS11Signer.h:27
void setPin(const std::string &pin)
Definition PKCS11Signer.cpp:338

If you would like to add PIN insertion dialog window for the signer to enter the PIN code then you can write a new class which extends the PKCS11Signer class, overwrite the std::string pin(const X509Cert &cert) method and write your own PIN dialog implementation code there.

Optionally specify the signature profile

The supported signature profiles are (see also Supported functional properties, under "Signature profiles"):

  • "time-stamp" (TS) - signature profile in case of which the certificate validity information is added to the signature with an OCSP confirmation (retrieved from OCSP server); the signing time information is added with a time-stamp token (retrieved from a time-stamping service). Signature creation time is the issuance time recorded in the token's RFC 3161 TSTInfo.genTime field.

If the signature profile value is not specified then then a "time-stamp" profile is used by default.

Set the profile value as follows:

std::string profile = "time-stamp"; // or "time-stamp-archive"
signer->setProfile(profile);
void setProfile(const std::string &profile)
Definition Signer.cpp:204

Optionally specify additional signer's data

Signature production place and signer role are optional signed meta-data about the signature. If left unspecified then the respective elements in the signatures*.xml file are created with no contents.

std::string city, state, postalCode, country;
std::vector<std::string> roles;
signer->setSignatureProductionPlace(city, state, postalCode, country); // location where the signature is created
signer->setSignerRoles(roles); // role(s) of the signer
void setSignatureProductionPlace(const std::string &city, const std::string &stateOrProvince, const std::string &postalCode, const std::string &countryName)
Definition Signer.cpp:90
void setSignerRoles(const std::vector< std::string > &signerRoles)
Definition Signer.cpp:229

Optionally specify signature digest method

By default, the hash that is being signed is calculated with SHA-256 algorithm. You can also use a different digest algorithm for calculating the hash that is signed (this does not affect calculating other hash values). For that, use the digidoc::Signer::setMethod(const std::string &method) method.

Create the signature

The signing method also adds validation data from external services (OCSP and time-stamping servers). Note that the OCSP responder and time-stamping server settings (in case of TS profile) should be configured before calling out the following method (see also Initialization and Configuration parameters). By default, the RIA's time-stamping service https://eid-dd.ria.ee/ts is used. Container holds the Signature object reference and there is no need cleanup memory.

Signature *signature = doc->sign(signer);

Validating the created signatures

After the signature has been added to the container, it should be validated before writing the signed container to an output file. For validating the signature, do as follows:

signature->validate();

The validation method above validates the signed data files', signer certificate's and OCSP confirmation's correspondence to the signature value. Note that the validation method above does not validate other signatures which may belong to the same container.

Note
See also Validating signature containers and signatures about more information for validating existing signatures and signature containers.

Save container changes

After the signature has been added and validated, container changes should be saved (Reading and writing DigiDoc documents):

doc->save();

Creating and signing a DigiDoc document (external signing, e.g. in browser)

External signing (two-step signing) can be used in case of signing in web applications, where the signature value is calculated externally, via a browser plug-in or extension.

In order to conduct web signing with Libdigidocpp library, do as follows: Container holds the Signature object reference and there is no need cleanup memory.

  1. In Container class, prepare signature XML structure:
    Signature *signature = doc->prepareSignature(signer);
  2. In Signature class, get the hash to be signed in browser:
    std::vector<unsigned char> dataToSign = signature->dataToSign();
    std::vector<unsigned char> signatureMethod = signature->signatureMethod();
    std::string id = signature->id();
    ID can be used when container is saved with digidoc::Container::save and later reloaded to locate signature object.
  3. Sign the hash in browser (e.g. use the hwcrypto.js library)
  4. Add RSA or ECDSA signature value to the signature XML structure: When container was stored on disk before reload container with digidoc::Container::open and use digidoc::Container::signatures() to enumerate signatures to locate correct object with previosly selected ID.
    std::vector<unsigned char> signatureValue = ...;
    signature->setSignatureValue(signatureValue);
  5. Add time-stamp and OCSP data to Signature object, according to the signature's profile (see also section Optionally specify the signature profile for more information):
    signature->extendSignatureProfile(signer);
  6. Write the document to output, as specified in section Reading and writing DigiDoc documents

Reading and writing DigiDoc documents

In order to read an existing DigiDoc file from the file system, do as follows:

auto doc = digidoc::Container::openPtr("<input-file's-path>");
static std::unique_ptr< Container > openPtr(const std::string &path)
Definition Container.cpp:519

The method above reads in the DigiDoc file from the specified location in file system and creates the respective Container object representing the document's data. The file's structure is also validated during its parsing according to the corresponding standards.

Note
Formats that are not validated locally are sent to the SiVa service while opening. Pass a digidoc::ContainerOpenCB to digidoc::Container::openPtr to control online access; the callback is also required for opening a DDoc enclosed in an ASiC-S container. See Online validation through SiVa.

Write a DigiDoc file (represented with a Container object) to file system with the following method:

digidoc::Container::save("<output-file's-path>");
virtual void save(const std::string &path="")=0
Note
ASiC-S containers with TimeStampToken signatures can be saved. Saving a read-only format or an unsupported ASiC-S signature variant throws a digidoc::Exception.

Validating signature containers and signatures

Validation of a signed DigiDoc document consists of three main steps:

  1. Call out the main validation method of the library. If there are multiple validation errors then get the errors list.
  2. Check for additional errors/warnings (separate implementation);
  3. Determine the validation status of the document (according to the returned error codes and validation status priorities).

Using the main validation method

You can validate a signature and its validation data - OCSP confirmation and time-stamp (in case of TS profile) - with method:

If an exception is thrown from the validation method then the signature can be either INVALID or VALID WITH WARNINGS; otherwise the signature is VALID. Before determining the final validation status, additional errors must be checked, as described in the following chapters.

If an exception is thrown then its causes can be retrieved with the following method:

std::vector<digidoc::Exception> digidoc::Exception::causes();
Causes causes() const
Definition Exception.cpp:194

Checking for additional errors/warnings

There is a validation case that is not checked in the default validation method of the library, instead, separate method for checking this specific situation has to be implemented by the library’s user. In Libdigidocpp library, checking for an old file format must be done separately.

The following subchapter describes how this check can be implemented. After checking for old signature format errors/warnings, collect all of the error codes and continue with determining the validation status as described in the next chapter. It is possible to check the source code of digidoc-tool or DigiDoc desktop application, accessible from https://github.com/open-eid/DigiDoc4-Client.

Determining the validation status

After validating the signed DigiDoc document, the validation result must be determined by the library's user. Final validation result must be one of the possible validation statuses that are described in the table below, the status must be chosen according to its priority. The validation status priorities have to be applied in two cases:

  1. Returning a validation result of a single signature:
    If there are more than one validation errors that occur when validating a single signature in DigiDoc container then the overall status of the signature should be chosen according to the status priorities.
  2. Returning a validation result of the whole DigiDoc container:
    If there are more than one signatures in a DigiDoc container and the signatures have different validation statuses or validation of the container structure returns a different status then the overall status of the DigiDoc file should be chosen according to the status priorities.
Note
The user of the library has to determine the validation status according to the error code returned by the library's validation method.
PriorityStatusError codeDescription
1INDETERMINATE/UNKNOWN10 CertificateIssuerMissing (signer's certificate is unknown)
6 CertificateUnknown (OCSP responder certificate is unknown)

Validation process determines that one or more of the certificates included in the document are unknown or not trusted, i.e. the certificates have been issued by an unknown Certificate Authority (the CA has not been added to trusted list). Notes:

  • The file and signature(s) are not legally valid.
  • If the CA will later be added to the trusted list/trust store then the validation status can change to any of the other statuses described in the current table.

Suggested warning message (also displayed in DigiDoc desktop application): "Signature status is displayed as unknown if you don't have all validity confirmation service certificates and/or certificate authority certificates available in TSL"

  • Sample file: unknown_CA_TS.asice (BDOC 2.1, time-stamp profile)
  • Sample file: unknown_CA_TM.asice (BDOC 2.1, time-mark profile)
2INVALIDAll errors except of the ones that are regarded as warnings by the library's user.

Validation process returns error(s), the errors have not been explicitly determined as minor error(s) by the library's user.

  • The file and signature(s) are not legally valid.
  • No further alterations should be made to the file, i.e. no signatures should be added or removed.

3

VALID WITH WARNINGS

See the next section.

Validation process returns error(s) that have been previously explicitly categorized (by the library's user) as minor technical errors. Note that this status is used only in exceptional cases, more details of which are given in the next chapter.

Note
  • The file and signature(s) are handled as legally valid.
  • The error(s) are regarded as validation warnings.
  • Validation warnings should be displayed to the user.
  • No further alterations should be made to the file, i.e. no signatures should be added or removed.
  • Creator of the file should be informed about the error situation.

4

VALID

N/A

Validation process returns no errors. The signature is legally valid.

The error codes described in the table above are defined in Exception.h source file.

Sample code of DigiDoc file validation can be found from digidoc-tool.cpp utility program, from the following method:

open(int argc, char* argv[]); //utility program's command "open"

Validation status VALID WITH WARNINGS

In special cases, validation errors can be regarded as minor technical errors and the file's validation status can be regarded as VALID WITH WARNINGS instead.

Warning
User of the DigiDoc library has to decide on his/her own when to use VALID WITH WARNINGS status instead of INVALID: there may be different interpretations of the severity of validation errors in different information systems then the final decision when to use this status has to be made by the library's user according to the requirements of the specific information system.

It is recommended to use the validation status VALID WITH WARNINGS in case of the error situations that are included in the table below - these error situations are regarded as VALID WITH WARNINGS in DigiDoc applications and software libraries, including:

  • DigiDoc desktop application,
  • Libdigidocpp and DigiDoc4j software libraries' utility programs.

Table 1. Validation error codes recommended to be handled as VALID WITH WARNINGS

Error codeRelated DigiDoc file formatDescription
12 RefereneceDigest Weak
13 SignatureDigestWeak
BDOC 2.1 / ASiC

Weaker digest method (SHA-1, SHA-224 or SHA3-224) has been used than recommended when calculating either <Reference> or <Signature> element's digest value.

Suggested warning message (also displayed in DigiDoc desktop application): "The current DigiDoc container uses weaker encryption method than officially accepted in Estonia."

  • Sample file: weak-sha1-warning-TS.asice (time-stamp profile)
  • Sample file: weak-sha1-warning-TM.bdoc (time-mark profile)
16 ProducedATLateWarningBDOC 2.1 TS / ASiC

The difference between the time-stamp issuance time (RFC 3161 TSTInfo.genTime) and the OCSP response's issuance time (producedAt value) exceeds 15 minutes but is less than 24 hours.

Suggested warning message: "Time-stamp and OCSP issuance time difference is over 15 minutes."

  • Sample file: TS_OCPS_difference_exceeds_15min.asice
14 DataFileNameSpaceWarningDDOC 1.0
DDOC 1.1
DDOC 1.2
DDOC 1.3

<DataFile> element's xmlns attribute is missing.

Suggested warning message (also displayed in DigiDoc desktop application): "This DigiDoc documents has not been created according to specification, but the digital signatures is legally valid. You are not allowed to add or remove signatures to this container."

More info: https://www.id.ee/en/article/digital-signing-and-electronic-signatures/

  • Sample file: datafile_xmlns_missing.ddoc
15 IssuerNameSpace WarningDDOC 1.1
DDOC 1.2
DDOC 1.3

<IssuerSerial><X509IssuerName> and/or <IssuerSerial><X509SerialNumber> element's xmlns attribute is missing.

Suggested warning message (also displayed in DigiDoc desktop application): "This DigiDoc documents has not been created according to specification, but the digital signatures is legally valid. You are not allowed to add or remove signatures to this container."

More info: https://www.id.ee/en/article/digital-signing-and-electronic-signatures/

  • Sample file: issuerserial_xmlns_missing.ddoc
N/A (Separate error code has not been determined.DDOC 1.0
DDOC 1.1
DDOC 1.2

DigiDoc file's version is older than currently supported. Note that the error situation affects only the container and not the signatures, therefore, in DigiDoc libraries, it is returned and displayed only at container level.

Suggested warning message (also displayed in DigiDoc desktop application): "The current file is a DigiDoc container that is not supported officially any longer. You are not allowed to add or remove signatures to this container"

More info: https://www.id.ee/en/article/digidoc-container-format-life-cycle-2/

  • Sample file: old_digidoc_format_1.0.ddoc

Additional information about validation

Overview of validation activities

Overview of validation activities is as follows:

  1. checking that all the data files and signature’s meta-data (signer’s role, etc.) are included in the signature by calculating the data objects’ digest values and comparing them with the <Reference> element values in the signature;
  2. checking that the claimed signer’s certificate is the actual certificate that was used for signing; checking that the "Non-repudiaton" value is set in the "Key Usage" extension of the signer’s certificate;
  3. checking that the signature value is correct by decrypting the value with the signer’s public key and comparing the result with digest calculated from <SignedInfo> element block;
  4. case of time-stamp corresponds to the signature value (by comparing the digest value of <SignatureValue> element’s value and TS response’s digest value);
  5. checking that the OCSP response confirms the signer certificate’s validity and case of time-mark corresponds to the signature value (by comparing the digest value of <SignatureValue> element’s value and OCSP response’s nonce value);
  6. checking that the signer’s, TSA and OCSP responder’s certificates are trusted (i.e. the certificates’ issuers are registered in trust store).
Note
Libdigidocpp library uses TSL lists (Trust Service Status list) to obtain information about trusted certificates during signature validation process. See also TSL overview, TSL standard and Configuring TSL settings.

For formats validated through SiVa, archive time-stamp information returned by the service is available through digidoc::Signature::ArchiveTimeStamps in the same way as locally validated archive time-stamps.

Extending container validity

Since version 4.5.0, digidoc::Container::extendContainerValidity provides the high-level workflow for extending the validity evidence of a container. The method sets the required signer profile automatically and uses the following strategy:

  • eligible ASiC-E LT-level signatures are extended in place with archive time-stamps;
  • an ASiC-S TimeStampToken is extended in place with an ASiC archive manifest and a new time-stamp token;
  • when in-place extension is unsuitable, the original container is embedded as the data file of a new timestamped ASiC-S container.

Only signatures with an Estonian signing certificate are considered for in-place extension, and e-seal signatures are skipped. A container with no applicable Estonian signatures, or with only T-level signatures lacking OCSP validation data, cannot be extended by this workflow.

auto doc = digidoc::Container::openPtr("signed-document.asice");
digidoc::Signer *signer = ...;
std::size_t extendedCount = 0;
if(auto wrapped = digidoc::Container::extendContainerValidity(*doc, signer, extendedCount))
doc = std::move(wrapped); // original container was wrapped in a new ASiC-S container
// Otherwise doc was extended in place and extendedCount contains the number
// of signatures that received new validity evidence.
doc->save();
static std::unique_ptr< Container > extendContainerValidity(Container &doc, Signer *signer, size_t &extendedCount)
Definition Container.cpp:350

The returned unique pointer is empty when extension was performed in place. It contains the new ASiC-S container when wrapping was necessary; in that case extendedCount is zero. The caller must save the modified or newly returned container.

The generated language bindings expose the same workflow with language-specific return values:

  • C#: Container.extendContainerValidity(doc, signer, out ulong extendedCount) returns the wrapping container, or null for an in-place extension.
  • Python: Container.extendContainerValidity(doc, signer) returns a tuple (wrapping_container_or_None, extended_count).
  • Java: Container.extendContainerValidity(doc, signer) returns the wrapping container, or null for an in-place extension; the count is not exposed.

In every binding, replace the original container with the returned container when the return value is not null/None, then save the resulting container. A null/None result means the original object was modified in place.

Extracting data files

A data file can be extracted from container and written to the specified location in the file system or to an output stream.

  1. You can write the data file to a stream and keep it in memory:
    void digidoc::DataFile::saveAs(std::ostream &os) const;
    virtual void saveAs(std::ostream &os) const =0
  2. The file can be written to file system with the following method:
    void digidoc::DataFile::saveAs(const std::string &path) const;

List of all the document’s data files can be retrieved with the following method:

std::vector<digidoc::DataFile *> digidoc::Container::dataFiles() const;
virtual std::vector< DataFile * > dataFiles() const =0

For example, read in a DigiDoc document and write its data files to file system as follows:

auto doc = digidoc::Container::openPtr("<input-file's-path>");
for(const digidoc::DataFile *file: doc->dataFiles()) {
try {
std::string dst = file->fileName();
file->saveAs(dst);
} catch(const digidoc::Exception &e) {
printf(" Document %s extraction: FAILED\n", file->fileName().c_str());
}
}
Data file wrapper providing methods for handling signed files or files to be signed in Container.
Definition DataFile.h:30
Base exception class of the digidoc implementation.
Definition Exception.h:30

Removing signatures and data files

In order to remove a signature from DigiDoc document, use the following method:

void digidoc::Container::removeSignature(unsigned int index);
virtual void removeSignature(unsigned int index)=0

Data files can be removed from a container only after all of its signatures have been removed. Use the following method to remove a data file from DigiDoc container:

void digidoc::Container::removeDataFile(unsigned int index);
virtual void removeDataFile(unsigned int index)=0

The index parameters of the methods above are the signature and data file sequence numbers in the container, starting from zero.

Note
Directly modifying the data file or initial signature of an existing ASiC-S container is not supported. Adding archive-manifest time-stamp layers through digidoc::Container::extendContainerValidity is supported. Signatures cannot be removed from an ASiC-S container while an enclosed DDoc opened through SiVa is exposed.

Shutting down the library

After finishing work with Libdigidocpp, then the last task is to shut down the library:

DIGIDOCPP_EXPORT void terminate()
Definition Container.cpp:163

The termination method closes libraries used in Libdigidocpp implementation and deletes temporary files that may have been written to disk when working with the library.

Exception handling

The Libdigidocpp library may throw exceptions that are instances of Exception class (defined in Exception.h source file). The code which uses Libdigidocpp’s API should be wrapped in a try/catch block as follows:

try {
// code implementation
} catch(const digidoc::Exception &e) {
printf("Exception:%s\n", parseException(e).c_str()); // Sample exception handling method
}

An Exception instance thrown by the library may contain a stack trace of the hierarchy of exceptions. For example, to parse the whole stack trace, do as follows:

std::string parseException(const digidoc::Exception &e)
{
std::string result = e.msg() + "\n"; // the error message is retrieved
// Iteration through the list of causes:
for(const digidoc::Exception &ex: e.causes())
result += parseException(ex); // Parsing the exceptions recursively
return result; // The error message is returned
}
std::string msg() const
Definition Exception.cpp:184

Identity tokens in Libdigidocpp

Libdigidocpp library supports the following identity tokens:

  • Estonian national eID card and Digi-ID

The library is also tested with the following tokens (indirectly via the DigiDoc desktop application that uses Libdigidocpp as a base component):

  • Finnish national eID card
  • Latvian national eID card
  • Lithuanian national eID card
  • SafeNet eToken 5110 with SK issued certificates for organizations