EBV-BXA¶
The EBV-BXA processes XML documents conforming to the EBV schema definitions.
The foundation for the EBV-BXA is the German official document: Verordnung zur Einführung einer Ersatzbaustoffverordnung.
Note
Prior to using the integration package you need to obtain a valid license from ZEDAL. This license, as well as the binary package (current version: 1.0.54), is available on request. Please send an e-mail with your inquiry to erp-tools@zedal.de.
Contents¶
The package contains:
- A binary shared object for incorporating the functionality into third-party applications.
- A header file to simplify inclusion into other C++ projects (
lib.h
). - A Directory
schema
containing the schema definitions. - The
mapping
directory contains PDFs illustrating which fields in the printouts correspond to which field id used by the EBV-BXA.
Technical info¶
The shared object (DLL for Windows, x86 and x64 platforms) is statically linked and provides a C interface for easy integration into most languages. On Windows it is built with Microsoft Visual Studio 2022, with the VC runtime also being statically linked in.
The file lib.h
in the includes
directory defines all function declarations to get the functionality by LoadLibrary / GetProcAddress on Windows, or dlopen / dlsym on Linux systems.
If targetting for C++11 or higher, the file xsd_names.h
contains the constexpr
variable linking namespaces and names corresponding to the element names in the XML. This will prevent mistyped names.
All library functions expect UTF-8 encoded strings and return UTF-8 encoded strings. The same applies to the XML files itself. Any other encoding may lead to errors or to incorrectly encoded content.
The header bxa_interface.h
contains constants for common return values of the EBV functions and identifier strings for document and layer types.
The EBV-BXA can manage multiple XML documents in separate workspaces. Operations on multiple workspaces is thread safe. In the reference section the C type of every parameter is listed. For non C/C++ programmers:
const char*
refers to a string which is zero-terminatedconst char**
expects a memory block large enough for an address (pointer). The library stores the address where the result can be fetched into this block. The memory area where the result resides is controlled by the library itself.std::size
is used for memory blocks of specified lengths, in case they are not zero-terminated.std::size*
means a pointer tostd::size
. The EBV-BXA will provide the length of the returned block.
Prerequisites¶
License¶
Using the EBV-BXA requires a license key which can be obtained from ZEDAL. The license consists of the vendor id and the license key. The vendor id will be written into the XML to identify which vendor / software has created /modified the layer. This potentially accelerates the troubleshooting process if interoperability problems should occur.
Document types¶
The XML specification defines three document types: Lieferschein, Anlage8 and Trägerdokument. The XML definition is designed with respect to the official forms. Because the electronic processing needs more information than the paper documents, the XML contains additional data.
ReferenzNr¶
The documents and the participants have optional fields to store a user defined ID for their purposes.
Zusatzdaten¶
Zusatzdaten
contain additional fields for every participant mentioned in the documents. They take up to 60 characters each and could be used for storing IDs for linking to datasets in other IT systems.
Layers¶
The documents are organized into individual layers. This allows for changes even if a document was electronically signed. By adding a new layer, the old (signed) layer will be encapsulated without changing it. By design the EBV-BXA can only modify values in the top layer to avoid breaking electronic signatures unintentionally.
In theory, layers can stack unlimited, but with electronic signatures on each layer, the verification of those will take more and more time, as each signature must be checked, including an OCSP check, which involves a remote call. The number of layers should be kept as small as possible.
If a layer is not electronically signed it may be deliberately edited as many times as necessary.
The Lieferschein document only has a single layer type, LieferscheinLayer
.
The Anlage8 document on the other hand provides two distinct workflows. One consists of layers exclusively of type DeckblattLayer
. The second workflow makes use of the VoranzeigeLayer
type, ultimatively followed by the AbschlussanzeigeLayer
.
Please consult the official EBV legal documents for when to use the different types of workflows.
Transparency¶
The layer structure is used by the EBV-BXA to compute the difference between two layers and only save values that actually have been changed. The exceptions to this rule are tables (e.g., Gemisch / MEB) which are completely rewritten even if only a single cell changes. When a value is written to a layer, the outcome will be influenced by the value in the previous layer(s). The following rules apply:
- If an empty (“”) string is written and there exists no value in an older layer, nothing will be written, the value remains non existing.
- If an empty (“”) string is written and a value from an older layer exists, the empty string will be written, which practically deletes the value from the new layer on.
- If the same value already existing on the previous layer is written, nothing will be written, because there is no change.
- All other cases: The newly set values will be written.
Dissemination¶
The Lieferschein document contains the XML block BeteiligtenListe, which defines how data transport should be carried out by the ZEDAL server.
Currently, two options exist:
- Email – the document will be converted to a PDF, electronic signatures included as printed info, and the results being sent as an email to the specified receiver.
- ZEDAL (only available if the receiver has a ZEDAL account, too) – direct transfer of the XML document to the receiver’s ZEDAL account.
Note: The BeteiligtenListe is not secured if an electronic signature is applied to the layer, as it defines the means of data transport only.
Mode of Operation¶
The EBV-BXA manages multiple workspaces. Each workspace can process a single XML document.
Before a document can be processed, a workspace must be created with a call to ebv_initialize
.
To initialize the EBV-BXA, a vendor id and license key as already mentioned under prerequisites must be provided. The vendor id will be part of the created layer element.
Here is an example of creating a Lieferschein document from scratch:
#include <bxa_interface.h>
#include <lib.h>
#include <stdio.h>
// example C++11 program creating a 'Lieferschein' from scratch, only writing the field
// ReferenzNr and outputting the generated XML to the file 'example.xml'
int main(int argc, char** argv)
{
if (!LoadDll("ebvxba_shared.dll"))
return 5; // dll load failed
const auto workspace_id = ebv_initialize("your_vendor_id", "your_license_key");
if (workspace_id > 0)
{
if (ebv_create(workspace_id, DT_LIEFERSCHEIN) == EBVBXA_OK)
{
const std::string field_list = R"(ReferenzNr=REF-001
)"; // this syntax includes a line-feed which is required
if (ebv_set_field_list(workspace_id, field_list.c_str(), field_list.length()) == EBVBXA_OK)
{
const char* buffer = nullptr;
if (ebv_get_xml(workspace_id, &buffer) == EBVBXA_OK)
{
FILE* f = fopen("example.xml", "wb");
fwrite(buffer, 1, strlen(buffer), f);
fclose(f);
}
else
{
return 3; // generating resulting xml failed
}
}
else
{
return 2; // setting the field list failed
}
}
}
else
{
return 1; // error: initialize failed
}
return 0; // success
}
The next code sample demonstrates the loading and extraction of the complete field list of an XML file.
#include <bxa_interface.h>
#include <lib.h>
// example C++11 program reading the contents of an EBV XML file and printing it to the console
int main(int argc, char** argv)
{
if (!LoadDll("ebvxba_shared.dll"))
return 2; // dll load failed
const auto workspace_id = ebv_initialize("your_vendor_id", "your_license_key");
if (workspace_id > 0)
{
if (ebv_load(workspace_id, "lieferschein.xml") == EBVBXA_OK)
{
const char* buf = nullptr;
size_t size;
if (ebv_get_field_list(workspace_id, 0, true, &buf, &size, true) == EBVBXA_OK)
{
printf("%s\n", buf);
}
else
{
return 3; // getting the field list failed
}
}
else
{
return 2; // loading the XML failed
}
else
{
return 1; // error: initialize failed
}
return 0; // success
}
Attachments¶
Every layer can contain binary attachments like PDF or DOCX files. These can be obtained through special functions in the EBV-BXA.
Content¶
The function ebv_get_field_list
gets the content of the XML file in text format.
A sample output:
+Anlage8Nummer=
+Inverkehrbringer.Adresse.Name=Name
+Inverkehrbringer.Adresse.Strasse=Strasse
+Inverkehrbringer.Adresse.Hausnummer=42
+Inverkehrbringer.Adresse.Plz=12345
+Inverkehrbringer.Adresse.Ort=Ort
+Inverkehrbringer.Adresse.Staat=DE
+Inverkehrbringer.Kommunikation.Telefon=01234/56789
Gemische.size=2
+Gemische[1].Bezeichnung=1. Bezeichnung des mineralischen Ersatzbaustoffs
+Gemische[1].Bemerkung=1. Bemerkung zum mineralischen Ersatzbaustoff
+Gemische[2].Bezeichnung=2. Bezeichnung des mineralischen Ersatzbaustoffs
+Gemische[2].Bemerkung=2. Bemerkung zum mineralischen Ersatzbaustoff
Abfallschluessel.size=0
Every line consists of a single character indicating if the value is in the current layer (+) or in a historical layer (-), a field id, followed by an “equals” sign and the UTF-8 encoded value, and ends with a line feed character (Linux style line endings). If a field is not in the list, the corresponding element in the XML is also not present.
Line breaks inside the contents of a field are encoded as '\n'
. In C++, a corresponding definition might be const char* line_break = "\\n";
.
If there is no value after the “equals” sign the value is intentionally empty.
The field list contains meta fields like Gemische.size (preceded by a space by design) to simplify the handling of tables, by providing the size beforehand, even if the table is empty (see above example Abfallschluessel).
The package contains PDFs which illustrate the mapping from the paper printout to the path names expected by the EBV-BXA.
The ebv_get_field_list
function has two different modes: view and layer. Both need a reference layer as an additional parameter.
In view mode the content is accumulated from the reference layer to the oldest (most inner) layer. Contents from a newer layer overrides contents from older layers.
In layer mode only the distinct reference layer is extracted.
In view mode the text format is slightly extended by a character before the actual path and value. A “plus” sign indicates that the value is changed in the reference layer, and a “minus” sign indicates content from an older layer.
Metainformation¶
Layers and signatures can be extracted to get insight to existing layers and check if they are electronically signed. ebv_get_field_list
provides this information if the last parameter (includeMeta
) of the call is equal to true
.
The following table maps the XML paths to the field ids without the obligatory Meta
prefix (e.g. Meta.Storniert
):
field id | xml path / value |
---|---|
.Storniert | ebv:Lieferschein/@Storniert or ebv:Anlage8/@Storniert indcate the cancellation of the document |
.Layer[] | contains the list of layers from current to oldest in chronological order |
.Layer[].Type | Lieferschein:{LieferscheinLayer}, Anlage8: {DeckblattLayer} or {VoranzeigeLayer, AbschlussanzeigeLayer} |
.Layer[].Role | Lieferschein: INV, Anlage8: VER |
.Layer[].Signatures[] | list of signatures in order of appearance |
.Layer[].Signatures[].Role | role of signature encoded into the first three characters of id, always SNT. This stems from the re-using of the BMU signature schemes |
.Layer[].Signatures[].Id | envelope |
.Layer[].Signatures[].Index | consecutive number of signature in layer |
.Layer[].Signatures[].XmlId | ds:Signature/@Id |
.Layer[].Signatures[].XmlPath | xml path to node with signature, e.g. ebv:Lieferschein/ebv:Layer/ebv:LieferscheinLayer/ds:Signature[1] |
.Layer[].Signatures[].DistinguishedName | not available, always empty |
.Layer[].Signatures[].SubjectName | ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509SubjectName |
.Layer[].Signatures[].Certificate | ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate |
.Layer[].Signatures[].SigningTime | ds:Signature/ds:Object/dsx:QualifyingProperties/dsx:SignedProperties/dsx:SignedSignatureProperties/dsx:SigningTime |
.Layer[].Attachments[] | list of binary attachments in the corresponding layer |
.Layer[].Attachments[].File | File elements; only in field list if the attachment is a file (e.g. PDF) |
.Layer[].Attachments[].Xml | Xml indicates an arbitrary XML node as attachment |
.Layer[].Attachments[].IsSigned | indicate if attachment is secured by an electronic signature |
.Layer[].Attachments[].File.Id | always signiert |
.Layer[].Attachments[].File.Index | consecutive number of attachment in corresponding layer |
.Layer[].Attachments[].Filename | ebv:Dateianhang/@dateiname |
.Layer[].Attachments[].Mime | ebv:Dateianhang/@inhaltsTyp denotes the type of attachment Lieferschein: {Sonstiges}, Anlage8: {Lageskizze, Nachweis, Sonstiges} |
.Layer[].Attachments[].Size | ebv:Dateianhang/@dateigroesse size of the attachment in bytes |
.Layer[].Attachments[].File.XmlId | ebv:Dateianhang/@id |
.Layer[].Attachments[].Remark | ebv:Dateianhang/ebv:Bemerkung optional description of the contents |
.Layer[].Attachments[].Xml.id | ebv:FreieXMLStruktur/@id |
.Layer[].Attachments[].Xml.Uri | ebv:FreieXMLStruktur/@NamespaceURI identifies an XML structure not defined by the specification |
.FollowupLayer | Lieferschein: {LieferscheinLayer}, Anlage8: {DeckblattLayer}, oder {VoranzeigeLayer, AbschlussanzeigeLayer}/{AbschlussanzeigeLayer} this indicates possible next layers used in _ ebv_add_layer` |
.SignaturePositions[] | List of signature positions, contains exactly one element |
.SignaturePositions[0].id | envelope |
.SignaturePositions[0].inner_positions | empty list |
.AttachmentPositions[] | List of possible attachment locations: 1. binary attachments: {id:”signiert”, attachmentType:File, is_signed:true}, 2: arbitrary XML: {id:”signiert”, attachmentType:Xml, is_signed:true} |
.CurrentSignatures[] | List of top-level signatures, which may also be on older layers |
.CurrentSignatures[].InLayer | indicates if a signature is in the current layer, or on an older one |
.CurrentSignatures[].Id | INV if the document is of type Lieferschein, else VER |
.CurrentSignatures[].Signatures[] | single signature |
.CurrentSignatures[].Signatures[].Felder | see structure of Layer[].Signatures[] |
Cancellation¶
The attribute Storniert
set to true
on the document root element indicates a cancelled document.
Signatures¶
The specification allows for optional electronic signatures adhering to the eIDAS regulation of the EU, and thus conforming to XAdES Baseline Signatures B-B. The specification supports the use of XMLDsig and XAdES compliant electronic signatures, used for qualified electronic signatures as defined in the EU eIDAS regulation. Regardless of whether a party uses electronic signatures, received documents may contain them. Recommendations regarding the treatment of signed documents also apply to parties which do not sign documents themselves. In the processing of signed documents, the following points must be respected in order to avoid breaking signatures accidently:
- To allow electronic signatures to be verified later, the stored representation must yield the same content after canonicalization as the canonicalization did when the document was signed. The simplest way of achieving this is by storage of the unmodified message.
- Validating XML parsers modify XML documents implicitly (Normalization). Therefore they must not be used to process the messages. It may be possible to instruct validation parsers to skip the whitespace normalization but this is non-standard behaviour and there is no guarantee that every parser provides this feature.
In the process of creating electronic signatures, several parameters define how the signature is applied and which algorithm may be used. The definition of XMLDsig is very flexible, but to achieve good interoperability the following parameters from the specification must be used:
* CanonicalizationMethod’s Algorithm attribute must be set to http://www.w3.org/2001/10/xmlexc-c14n (exclusive c14n ignoring comments)
* The ds:Signature Element must contain the Id
Attribute, its value beginning with SNT-
followed by an UUID. This ID must be globally unique.
* SignatureMethod’s Algorithm attribute must be set to: http://www.w3.org/2001/04/xmldsigmore#rsa-sha256"/
* The first Reference-Element must be set as URI=””
- the empty URI. This takes the document as context for the signature while ignoring XML comments.
* The Transforms element defines in more detail which part of the XML instance is taken into account. The complete Transforms Block must be:
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
<xpf:XPath Filter="intersect" xmlns:xpf="http://www.w3.org/2002/06/xmldsig-filter2">here()/ancestor::*[6]
</xpf:XPath>
</ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
<xpf:XPath Filter="subtract" xmlns:xpf="http://www.w3.org/2002/06/xmldsig-filter2">here()/ancestor::*[6]/*[local-name()='Signature']
</xpf:XPath>
</ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">
<xpf:XPath Filter="subtract" xmlns:xpf="http://www.w3.org/2002/06/xmldsig-filter2">here()/ancestor::*[6]/*[local-name()='BeteiligtenListe' and namespace-uri()='http://www.infotech.de/zedal/ebv.xsd']
</xpf:XPath>
</ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
- Performance considerations:
- The algorithm xpath-filtering (http://www.w3.org/2002/06/xmldsig-filter2) should be used as it is an efficient way to define the scope. xpath is also possible, but computationally way more expansive.
- DigestValue’s Algorithm must be set to http://www.w3.org/2001/04/xmlenc#sha256
- KeyInfo/X509Data/X509Certificate must contain the certificate used in signing the document.
Attachments¶
The EBV document is capable of transporting binary BASE64 encoded content to the participants in the electronic process.
Information about the attachments can be obtained with ebv_get_field_list
and the parameter includeMeta
set to true
.
Managing attachments is possible through the ebv_***_attachment
functions.
Attachments can be classified into the following categories:
- Sonstige (others)
- Nachweis (notification, proof)
- Lageskizze (location sketch)
Mapping reference¶
This section contains PDF files which illustrate the mappings from the official documents (with additions for the electronic processing in ZEDAL) to the field ids utilized by the EBV-BXA.
-
Lieferschein
-
Anlage8
-
Traegerdokument
XML examples¶
Version history¶
1.0.44¶
First public release
1.0.45¶
- Added new field WiegescheinNr to the Anlage 7 (Lieferschein) document
1.0.54¶
- Added new document type Trägerdokument