Debugging

curl

In order to isolate where an integration problem is located, it can be helpful to eliminate any custom runtimes and environment specific aspects, and use a standard tool, configured with applicable keys and certificates, and try to communicate with the API. The following guide will use curl curl binaries, together with the openssl openssl binaries tool, which are already readily available on many systems.

Prepare your enterprise certificate

To instruct curl to use a client certificate (both the private and public key), it must be available non-encrypted in the PEM format. You likely have your enterprise certificate as an entry in a keystore file, typically a .p12-file (PKCS#12). If you have your enterprise certificate in another format than .p12, you need to figure out how you can extract your certificate to a PEM-file.

We will use openssl to extract the key entry to PEM format. You need to have the following at hand:

  • the keystore file with your enterprise certificate as a filepath, keystore.p12 is assumed below

  • password to read the keystore file (and possibly the particular key entry)

Run the following two commands to extract the public certificate and private key from your enterprise certificate into separate PEM files. You will be prompted for the password:

# Extract public certificate chain
openssl pkcs12 --noenc --in keystore.p12 --out publicCertificate.pem --nokeys

# Extract the private key, ensure this file is kept private at all times
openssl pkcs12 --noenc --in keystore.p12 --out privateKey.pem --nocerts

Issue a request to the API

With your enterprise certificate available in the files publicCertificate.pem and privateKey.pem, you should now be able to establish API communication with the Posten signering API.

Substitute ${orgNumber} with the organization number your certificate is issued to. You can also use an organization number you are authorized to send on behalf of.

curl https://api.difitest.signering.posten.no/api/${orgNumber} \
  --cert publicCertificate.pem \
  --key privateKey.pem \
  --insecure

If you need to view more details about the TLS handshake process, supply the --verbose flag.

Note

Why the --insecure flag? This flag turns off standard TLS validation of the certificate used by the server, specifically disabling the host name validation. The reason for this is our API uses an enterprise certificate as its server certificate, and it is not issued to any specific domain name. curl will not accept the API’s certificate unless this flag is set. An actual integration should do proper validation of the server certificate when establishing the TLS connection.

Responses

If a TLS connection is established (you use an appropriate enterprise certificate), the server will respond accordingly depending on whether your request operates as one of:

You are [the organization number your certificate is issued to]

This means the organization number is authorized to access the API. The request URL contains the same organization number as the certificate is issued to, and you have access to the API.

Error responses

Your certificate may be accepted, and TLS connection is established, but may still be denied access because the organization number of your certificate may be missing relevant authorization to access the API.

Your enterprise certificate has been successfully validated as authentic, but the organization number the certificate is issued to has not been granted access to the API.

<error>
  <error-code>NOT_AUTHORIZED</error-code>
  <error-message>
    API authorization request for [organization number], authenticated using certificate
      OID.2.5.4.97=NTRNO-[organization number], CN=Your Organization, (...)
      valid from 2024-11-01T12:00:45Z to 2027-11-01T22:59:00Z,
      serial-number: xyz123abc456,
    issuer: CN=Buypass Class 3 Test4 CA G2 ST Business, O=Buypass AS,
      OID.2.5.4.97=NTRNO-983163327, C=NO
    is not authorized to access this system
  </error-message>
  <error-type>CLIENT</error-type>
</error>

(Namespaces and other XML-related formalities omitted for brevity)

If you are missing authorization for the API, please contact us.


.NET Core

Enabling logging

The client library has the ability to log useful information that can be used for debug purposes. To enable logging, supply the Direct or Portal client with an implementation of Microsoft.Extensions.Logging.ILoggerFactory. This is Microsoft’s own logging API, and allows the user to chose their own logging framework.

Enabling logging on level DEBUG will output positive results of requests and worse, WARN only failed requests or worse, while ERROR will only occur on failed requests to create a signature job. These loggers will be under the Digipost.Signature.Api.Client namespace.

Implementing using NLog

There are numerous ways to implement a logger, but the following examples will be based on NLog documentation.

  1. Install the Nuget-packages NLog, NLog.Extensions.Logging and Microsoft.Extensions.DependencyInjection.

  2. Create a nlog.config file. The following is an example that logs to file and to console:

<?xml version="1.0" encoding="utf-8"?>

<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogFile="c:\temp\console-example-internal.log"
      internalLogLevel="Info">
    <!-- the targets to write to -->
    <targets>
        <!-- write logs to file -->
        <target xsi:type="File"
                name="fileTarget"
                fileName="/logs/signature-api-client-dotnet/signature-api-client-dotnet.log"
                layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}"
                archiveEvery="Day"
                archiveNumbering="Date"
                archiveDateFormat="yyyy-MM-dd"/>
        <!-- write logs to console -->
        <target xsi:type="Console"
                name="consoleTarget"
                layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}" />
    </targets>

    <!-- rules to map from logger name to target -->
    <rules>
        <logger name="*" minlevel="Trace" writeTo="fileTarget,consoleTarget"/>
    </rules>
</nlog>
  1. In your application, do the following to create a logger and supply it to the client:

private static IServiceProvider CreateServiceProviderAndSetUpLogging()
{
    var services = new ServiceCollection();

    services.AddSingleton<ILoggerFactory, LoggerFactory>();
    services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
    services.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace));

    var serviceProvider = services.BuildServiceProvider();
    SetUpLoggingForTesting(serviceProvider);

    return serviceProvider;
}

private static void SetUpLoggingForTesting(IServiceProvider serviceProvider)
{
    var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

    loggerFactory.AddNLog(new NLogProviderOptions {CaptureMessageTemplates = true, CaptureMessageProperties = true});
    NLog.LogManager.LoadConfiguration("./nlog.config");
}

static void Main(string[] args)
{
    ClientConfiguration clientConfiguration = null;
    var serviceProvider = CreateServiceProviderAndSetUpLogging();
    var client = new PortalClient(clientConfiguration, serviceProvider.GetService<ILoggerFactory>());
}

Request and response logging

For initial integration and debugging purposes, it can be useful to log the actual request and response going over the wire. This can be enabled by doing the following:

Set the property ClientConfiguration.LogRequestAndResponse = true.

Caution

Enabling request logging should never be used in a production system. It will severely impact the performance of the client.

Logging of document bundle

Logging of document bundle can be enabled via the ClientConfiguration:

var clientConfiguration = new ClientConfiguration(Environment.DifiTest, "3k 7f 30 dd 05 d3 b7 fc...");
clientConfiguration.EnableDocumentBundleDiskDump("/directory/path/for/bundle/disk/dump");

Note

Remember to only set the directory to save the disk dump. A new zip file will be placed there for each created signature job.

If you have special needs for the bundle other than just saving it to disk, add your own bundle processor to ClientConfiguration.DocumentBundleProcessors.


Java

Request and response logging

Caution

Enabling request logging should never be used in a production system. It will impact the performance of the client.

You may configure the client library to log HTTP requests and responses by calling .enableRequestAndResponseLogging() when creating the client’s configuration. You may configure the logger no.digipost.signature.client.http.requestresponse in order to customize logging. It must be set to at least INFO to write anything to the log.

Writing document bundle to disk

You may configure the client library to write a ZIP file with the document bundle by calling .enableDocumentBundleDiskDump(Path) when creating the client’s configuration.

The Path parameter is the directory to where the files will be written. This directory must exists as the client library won’t try creating it.

If you have needs for the document bundle other than just saving it to disk, add your own document bundle processor by calling .addDocumentBundleProcessor(…) with your own DocumentBundleProcessor when creating the client’s configuration.