An ASiC-E container (Associated Signature Container Extended, ETSI EN 319 162) is a single ZIP file (.asice) with any number of files of any type and their XAdES signatures. It is the container recommended by the eIDAS Regulation and used by many e-government, e-invoicing, e-procurement and court systems. Every signature of the container covers all its files.

What the sample shows

  • Create a signed container from files, or in memory from byte arrays, with sub-folders: AsicSignature.CreateContainer.
  • The XAdES level of the signatures (B, T, LT, LTA), the hash algorithm, the commitment type and the signing place.
  • Co-signatures: a second signer adds a signature with AddSignature; the existing signatures remain valid.
  • Long-term archival: AddArchiveTimestamp renews the archive time-stamp of a XAdES-LTA container.
  • Reading and verifying: the signers, the file names, ExtractDataFile, VerifyDigitalSignature for all the signatures or for a specific certificate, and the detection of a changed or an added file.

C# code: create an ASiC-E container

/// <summary>
/// Creates a signed ASiC-E container with files (any type). The files are stored with their file names.
/// </summary>
static void CreateContainerFromFiles(string containerFile)
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    asicSignature.DigitalSignatureCertificate = LoadSigningCertificate();

    // The files to sign (any number of files, of any type) and the container to create. One signature covers all the files.
    string[] filesToSign = { SampleFile("test.txt"), SampleFile("test.xml"), SampleFile("source.pdf") };

    asicSignature.CreateContainer(filesToSign, containerFile);

    Console.WriteLine("Container created: " + Path.GetFileName(containerFile));
}

/// <summary>
/// Creates a signed ASiC-E container in memory. The names of the files can contain sub-folders.
/// </summary>
static byte[] CreateContainerInMemory()
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    asicSignature.DigitalSignatureCertificate = LoadSigningCertificate();

    // The name of the file in the container and its content. The content can come from a database, from a web request, etc.
    IDictionary<string, byte[]> files = new Dictionary<string, byte[]>
    {
        { "contract.pdf", File.ReadAllBytes(SampleFile("source.pdf")) },
        { "annexes/annex1.txt", File.ReadAllBytes(SampleFile("test.txt")) },
        { "annexes/data.xml", File.ReadAllBytes(SampleFile("test.xml")) }
    };

    byte[] container = asicSignature.CreateContainer(files);

    Console.WriteLine("Container created in memory: " + container.Length + " bytes");

    return container;
}

C# code: XAdES-T, LT or LTA signatures in the container

/// <summary>
/// Creates a container with a signature with all the options.
/// </summary>
static void CreateContainerWithOptions(string containerFile, XadesSignatureStandard standard)
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    asicSignature.DigitalSignatureCertificate = LoadSigningCertificate();

    // The XAdES baseline level of the signatures: XadesB (the default), XadesT (time-stamp), XadesLT (validation data) or
    // XadesLTA (long term archival, with an archive time-stamp). XadesT, XadesLT and XadesLTA need a time-stamping server.
    asicSignature.SignatureStandard = standard;

    if (standard != XadesSignatureStandard.XadesB)
    {
        asicSignature.TimeStamping.ServerUrl = TimestampServerUrl;

        // The revocation data (CRL, OCSP responses) included in the XAdES-LT and XAdES-LTA signatures. A certificate without
        // CRL or OCSP (as the sample certificate) gives a signature without revocation data.
        asicSignature.LtvLevel = XadesLtvLevel.IncludeCrlAndOcsp;
    }

    // The hash algorithm of the signatures: SHA256 (the default), SHA384 or SHA512. SHA-1 is not supported for new signatures.
    asicSignature.HashAlgorithm = SignLib.HashAlgorithm.SHA384;

    // What the signer declares by signing, and the place where the signature is created (both are optional).
    asicSignature.CommitmentType = XadesCommitmentType.ProofOfApproval;
    asicSignature.SignatureProductionPlace = new XadesProductionPlace { City = "Bucharest", CountryName = "RO" };

    // An explicit signature policy can be set when a regulation requires it:
    // asicSignature.SetSignaturePolicyInformation("2.16.724.1.3.1.1.2.1.9", policyDocumentHash, "2.16.840.1.101.3.4.2.1", "https://example.com/policy.pdf");

    asicSignature.CreateContainer(new[] { SampleFile("test.txt"), SampleFile("source.pdf") }, containerFile);

    Console.WriteLine("Container created: " + Path.GetFileName(containerFile) + " (" + standard + ")");
}

C# code: add a second signature

/// <summary>
/// A second signer adds a signature to an existing container. The new signature covers all the files of the container.
/// </summary>
static void AddSecondSignature(string containerFile, string cosignedContainerFile)
{
    AsicSignature secondSigner = new AsicSignature(LibrarySerialNumber);

    // In a real application the second signer has his own certificate.
    secondSigner.DigitalSignatureCertificate = CreateTestCertificate("CN=Second signer, O=Organization");

    // The output container can be the same as the input container.
    secondSigner.AddSignature(containerFile, cosignedContainerFile);

    Console.WriteLine("Signature added: " + Path.GetFileName(cosignedContainerFile));
}

C# code: renew the archive time-stamp of an LTA container

/// <summary>
/// Adds a new archive time-stamp to the XAdES-LTA signatures of a container. It is done periodically, before the certificate
/// of the time-stamping authority expires or its algorithms become weak, to keep the signatures verifiable in the long term.
/// No signing certificate is needed.
/// </summary>
static void RenewArchiveTimestamp(string ltaContainerFile, string renewedContainerFile)
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    asicSignature.TimeStamping.ServerUrl = TimestampServerUrl;
    asicSignature.LtvLevel = XadesLtvLevel.IncludeCrlAndOcsp;

    asicSignature.AddArchiveTimestamp(ltaContainerFile, renewedContainerFile);

    Console.WriteLine("Archive time-stamp added: " + Path.GetFileName(renewedContainerFile));
}

A complete long-term archival workflow, with the renewal policy, is described in ASiC-E LTA: long-term archival containers.

C# code: read, extract and verify

/// <summary>
/// Displays the content of a container: the signatures, the signers and the files. It also verifies the container.
/// </summary>
static void PrintContainer(string containerFile)
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    Console.WriteLine("Container: " + Path.GetFileName(containerFile));

    // The signatures of the container and their signers. The index of the signatures starts at 0 (the signature files in name order).
    int numberOfSignatures = asicSignature.GetNumberOfSignatures(containerFile);
    Console.WriteLine("  Number of signatures: " + numberOfSignatures);

    for (int signatureIndex = 0; signatureIndex < numberOfSignatures; signatureIndex++)
        Console.WriteLine("  Signer " + signatureIndex + ": " + asicSignature.GetDigitalSignatureCertificate(containerFile, signatureIndex).Subject);

    // The names of the signed files, as they are stored in the container.
    foreach (string fileName in asicSignature.GetDataFileNames(containerFile))
        Console.WriteLine("  File: " + fileName);

    // True if the container has at least one signature, every signature is valid and every signature covers all the files of the container.
    // The trust of the signing certificates is not verified.
    Console.WriteLine("  The container is valid: " + asicSignature.VerifyDigitalSignature(containerFile));
    Console.WriteLine();
}

/// <summary>
/// Extracts a signed file from a container and verifies the signature of a specific certificate.
/// </summary>
static void ExtractAndVerifyContainer(byte[] container)
{
    AsicSignature asicSignature = new AsicSignature(LibrarySerialNumber);

    // The container is a byte array: all the methods have an overload for byte arrays.
    foreach (string fileName in asicSignature.GetDataFileNames(container))
        Console.WriteLine("File: " + fileName + ", " + asicSignature.ExtractDataFile(container, fileName).Length + " bytes");

    File.WriteAllBytes(OutputFile("extracted contract.pdf"), asicSignature.ExtractDataFile(container, "contract.pdf"));

    // The container has a valid signature, of all its files, created with the expected certificate (the public part is enough).
    X509Certificate2 expectedSigner = new X509Certificate2(LoadSigningCertificate().RawData);
    Console.WriteLine("The container is signed by the expected certificate: " + asicSignature.VerifyDigitalSignature(container, expectedSigner));

    X509Certificate2 otherCertificate = new X509Certificate2(CreateTestCertificate("CN=Other certificate").RawData);
    Console.WriteLine("The container is signed by another certificate: " + asicSignature.VerifyDigitalSignature(container, otherCertificate));
    Console.WriteLine();
}

The methods verify the integrity of the signatures, not the trust of the signing certificates. A smart card, a USB token, an HSM or a remote service can sign the container through an external signature provider, as in External and remote signature. Without programming, use ASiC Signer.

Run the full sample. The complete Visual Studio project ASiC-E Container Digital Signature is included in the library download, in the folder NET Sample Projects (.NET 8, .NET 9 and .NET 10) and in NET Framework Sample Projects (.NET Framework 4.6.2), with the test files and a test certificate. Without a serial number the library runs in demo mode. Download the library and the samples · User manual

Frequently asked questions

What is the difference between ASiC-E and ASiC-S?

ASiC-S contains a single signed file; ASiC-E (extended) contains any number of files and signatures, and every signature covers all the files.

Can I add a file to a signed container?

No. A signature covers all the files of the container: a file added after signing makes the container invalid, and the sample shows that the verification detects it.

How do I verify an .asice file from PowerShell?

With verifySignature.ps1 from the folder PowerShell Scripts of the download: it verifies PDF, P7M, P7S, XML, Office and ASiC-E files.