Skip to content

Invoice Signing

How to sign UBL invoices with a CSID certificate and private key.

Example

pub fn main() {
    use fatoora_core::invoice::sign::invoice_hash_base64_from_xml_str;
    use fatoora_core::invoice::xml::parse::parse_signed_invoice_xml;

    let signed_xml_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/invoices/sample-simplified-invoice.xml");
    let signed_xml = std::fs::read_to_string(&signed_xml_path).expect("read signed invoice xml");
    let signed = parse_signed_invoice_xml(&signed_xml).expect("parse signed invoice");

    let hash = invoice_hash_base64_from_xml_str(&signed_xml).expect("compute invoice hash");
    let qr = signed.qr_code();
    let signing_time = signed.signed_properties().signing_time();
    assert!(!hash.is_empty());
    assert!(!qr.is_empty());
    let _ = signing_time;
}
from fatoora.invoice import parse_signed_invoice_xml

# signed_xml_path = "path/to/signed_invoice.xml"
signed_xml = signed_xml_path.read_text(encoding="utf-8")

signed = parse_signed_invoice_xml(signed_xml)

hash_b64 = signed.hash_base64()
qr = signed.qr_code()
signing_time = signed.signing_time()
assert hash_b64
assert qr
import { Signer } from 'fatoority';

export function signInvoice(xml, certificatePem, keyPem) {
  return Signer.fromPem(certificatePem, keyPem).signXml(xml);
}
    public static string SignInvoice(string xml, string certificatePem, string keyPem)
    {
        using var signer = Signer.FromPem(certificatePem, keyPem);
        using var invoice = FinalizedInvoice.FromXml(xml);
        using var signed = signer.Sign(invoice); // Consumes invoice's inner state.
        return signed.IntoXml();
    }
#include "fatoora.h"
#include <stdio.h>
#include <stdlib.h>

/* File I/O belongs to the application; the signing API takes byte buffers. */
static unsigned char *read_der(const char *path, size_t *length) {
    FILE *file = fopen(path, "rb");
    if (!file) return NULL;
    unsigned char *data = NULL;
    if (fseek(file, 0, SEEK_END)) goto done;
    long size = ftell(file);
    if (size <= 0 || size > 1024 * 1024) goto done;
    rewind(file);
    data = malloc((size_t)size);
    if (!data) goto done;
    if (fread(data, 1, (size_t)size, file) != (size_t)size) {
        free(data);
        data = NULL;
    } else {
        *length = (size_t)size;
    }
done:
    fclose(file);
    return data;
}

int main(int argc, char **argv) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s invoice.xml certificate.der private-key.der\n", argv[0]);
        return 1;
    }
    size_t cert_len = 0, key_len = 0;
    unsigned char *cert = read_der(argv[2], &cert_len);
    unsigned char *key = read_der(argv[3], &key_len);
    if (!cert || !key) {
        fputs("Unable to read certificate or private key DER.\n", stderr);
        free(cert);
        free(key);
        return 1;
    }
    FatooraSigner *signer = NULL;
    FatooraFinalizedInvoice *invoice = NULL;
    FatooraSignedInvoice *signed_invoice = NULL;
    FatooraError *error = NULL;
    char *xml = NULL;
    FatooraStatus status = FATOORA_OK;
    int exit_code = 1;
#define TRY(call) do { status = (call); if (status != FATOORA_OK) goto cleanup; } while (0)
    TRY(fatoora_signer_from_der(cert, cert_len, key, key_len, &signer, &error));
    TRY(fatoora_finalized_invoice_from_file(argv[1], &invoice, &error));
    /* Signing consumes invoice's inner state; its outer handle still needs free. */
    TRY(fatoora_signer_sign(signer, invoice, &signed_invoice, &error));
    TRY(fatoora_signed_invoice_into_xml(signed_invoice, &xml, &error));
    puts(xml);
    exit_code = 0;
cleanup:
    if (exit_code) fprintf(stderr, "Error %d: %s\n", (int)status, fatoora_error_message(error));
    fatoora_string_free(xml);
    fatoora_signed_invoice_free(signed_invoice);
    fatoora_finalized_invoice_free(invoice);
    fatoora_signer_free(signer);
    fatoora_error_free(error);
    free(cert);
    free(key);
    return exit_code;
}

Notes

  • Signed invoice metadata (signature/public key/signed props hashes) are exposed as getters.
  • The C example reads certificate/key DER in the application and uses the invoice file helper. Signing consumes the finalized invoice; all handles are released during cleanup.

See also: Invoice Signing Reference

C examples include fatoora.h; compile the adapter once with your application. See the C build instructions.

See the JavaScript / TypeScript reference for installation and runtime contracts.

See the C# / .NET reference for installation, imports, and ownership rules.