Skip to content

PDF-Kryptorichtlinie, FIPS & CBOM

PDF Oxide abstrahiert jede Verschlüsselungs- und Signaturoperation hinter einem austauschbaren Krypto-Provider, legt darüber eine fail-closed arbeitende Laufzeit-Richtlinie und erfasst pro Prozess ein Inventar der tatsächlich genutzten Algorithmen. Dieses Inventar wird als CycloneDX-1.6-Kryptografie-Stückliste (CBOM) exportiert – das maschinenlesbare Artefakt, das regulierte Käufer und Prüfer verlangen.

Diese Seite behandelt die Provider-Auswahl, den FIPS-140-3-Modus, die Grammatik der Laufzeitrichtlinie, das Krypto-Inventar und den CBOM-Export.

Binding-Abdeckung. Die gesamte Krypto-Governance-Oberfläche ist prozessglobal (sie nimmt kein Dokument-Handle entgegen) und wird von Rust, Python, Go, Node.js / TypeScript und C# bereitgestellt. Die Methodennamen unterscheiden sich je nach Binding (idiomatische Schreibweise). Die Provider-Auswahl-Hälfte (active_provider / fips_available / use_fips) ist die FIPS-Oberfläche aus Issue #236; die Richtlinien-Hälfte (set_policy / policy / inventory / cbom) ist die Laufzeit-Governance-Oberfläche aus Issue #230. Der FIPS-Modus selbst bewirkt nur dann etwas, wenn die native Bibliothek mit --features fips kompiliert wurde – andernfalls gibt use_fips einen Fehler zurück bzw. löst ihn aus und fips_available ist false.

Die zwei Provider

Provider Build Algorithmen Verwendung
rust-crypto (Standard) immer vorhanden jeder Algorithmus, auf den die PDF-Spezifikation verweist, einschließlich des veralteten MD5-±RC4-Pfads für ISO-32000-1-R≤4-Dokumente allgemein
aws-lc-rs optional, --features fips FIPS-140-3-validiert; verweigert MD5, RC4 und SHA-1-Signaturen FIPS-Konformität

Der Standard-Provider wird verzögert (lazy) installiert – die erste Kryptooperation (Öffnen eines verschlüsselten Dokuments, Prüfen einer Signatur) legt ihn fest. Deshalb müssen Sie vor jeder Kryptoarbeit im Prozess auf FIPS umschalten.

Wie prüfe ich den aktiven Krypto-Provider?

active_provider ist nicht initialisierend: Wenn noch kein Provider festgelegt wurde, gibt es "rust-crypto (default, lazy)" zurück, ohne den Standard festzuschreiben, sodass ein späterer use_fips-Aufruf weiterhin gelingen kann. Sobald ein Provider festgelegt ist (explizit oder verzögert), gibt es den echten Namen zurück ("rust-crypto" oder "aws-lc-rs").

import pdf_oxide

# Safe to call first — does not commit a provider.
print(pdf_oxide.crypto_active_provider())      # "rust-crypto (default, lazy)"
print(pdf_oxide.crypto_available_providers())  # ["rust-crypto"]  (+ "aws-lc-rs" on a FIPS wheel)
use pdf_oxide::crypto;

// Non-initializing read for display/audit.
let name = if crypto::is_set() {
    crypto::active().name().to_string()
} else {
    format!("{} (default, lazy)", crypto::RustCryptoProvider.name())
};
println!("active crypto provider: {name}");
package main

import (
    "fmt"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    fmt.Println(pdfoxide.ActiveCryptoProvider())   // "rust-crypto"
    fmt.Println(pdfoxide.IsFipsCryptoAvailable())  // false unless built with FIPS
}
const pdf_oxide = require("pdf-oxide");

console.log(pdf_oxide.getActiveCryptoProvider()); // "rust-crypto (default, lazy)"
console.log(pdf_oxide.isFipsCryptoAvailable());   // false unless the -fips package
using PdfOxide;

Console.WriteLine(PdfDocument.GetActiveCryptoProvider()); // "rust-crypto"
Console.WriteLine(PdfDocument.IsFipsCryptoAvailable());   // False unless FIPS lib

Signaturen

Operation Rust Python Go Node.js C#
Aktiver Provider crypto::active().name() -> &'static str crypto_active_provider() -> str ActiveCryptoProvider() -> string getActiveCryptoProvider(): string GetActiveCryptoProvider() -> string
FIPS verfügbar? cfg(feature = "fips") crypto_available_providers() -> list[str] IsFipsCryptoAvailable() -> bool isFipsCryptoAvailable(): boolean IsFipsCryptoAvailable() -> bool

Die C-ABI, die sie umhüllen: char *pdf_oxide_crypto_active_provider(void) und int32_t pdf_oxide_crypto_fips_available(void) (gibt 1 zurück, wenn der FIPS-Provider einkompiliert wurde, andernfalls 0).

Wie aktiviere ich den FIPS-Modus für ein PDF?

Rufen Sie use_fips (oder in Rust set_provider) vor jeder Kryptooperation auf. Es ist einmalig setzbar: Ein zweiter Aufruf – oder jeder Aufruf, nachdem der Standard-Provider verzögert installiert wurde – schlägt fehl.

C-ABI-Fehlercodes für int32_t pdf_oxide_crypto_use_fips(void):

Code Bedeutung
0 Erfolg
1 FIPS-Feature nicht einkompiliert
2 ein Provider ist bereits gesetzt
import pdf_oxide

try:
    pdf_oxide.crypto_use_fips()   # raises RuntimeError if not compiled in / already set
    print("FIPS provider active:", pdf_oxide.crypto_active_provider())  # "aws-lc-rs"
except RuntimeError as e:
    print("FIPS unavailable:", e)

# Now every encrypted-PDF open / signature verify routes through aws-lc-rs.
doc = pdf_oxide.PdfDocument("encrypted-r6.pdf")
print(doc.page_count())
use std::sync::Arc;
use pdf_oxide::PdfDocument;
use pdf_oxide::crypto::{set_provider, AwsLcProvider};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Must run before any pdf_oxide operation that uses crypto.
    set_provider(Arc::new(AwsLcProvider::new()))
        .expect("crypto provider already installed");

    // Every PDF open / signature verify now routes through aws-lc-rs.
    let doc = PdfDocument::open("encrypted-r6.pdf")?;
    println!("pages: {}", doc.page_count());
    Ok(())
}
package main

import (
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    // Before any crypto operation.
    if err := pdfoxide.UseFipsCryptoProvider(); err != nil {
        log.Fatalf("FIPS unavailable: %v", err) // ErrFipsNotCompiled / ErrCryptoProviderAlreadySet
    }
    log.Println("active:", pdfoxide.ActiveCryptoProvider()) // "aws-lc-rs"
}
const pdf_oxide = require("pdf-oxide");

try {
  pdf_oxide.useFipsCryptoProvider();
  console.log("active:", pdf_oxide.getActiveCryptoProvider()); // "aws-lc-rs"
} catch (e) {
  console.error("FIPS unavailable:", e.message);
}
using PdfOxide;

try
{
    PdfDocument.UseFipsCryptoProvider();
    Console.WriteLine(PdfDocument.GetActiveCryptoProvider()); // "aws-lc-rs"
}
catch (InvalidOperationException e)
{
    Console.WriteLine($"FIPS unavailable: {e.Message}");
}

Was lehnt FIPS ab?

PDF Standard Security R≤4 (Passwortverschlüsselung von PDF 1.4–1.6, ISO 32000-1 §7.6.3) verwendet MD5 zur Schlüsselableitung und RC4/AES-128 als Chiffre. FIPS 140-3 verbietet MD5 und RC4, daher schlägt das Öffnen eines solchen Dokuments unter aws-lc-rs mit einer Behebungsmeldung fehl:

active CryptoProvider 'aws-lc-rs' rejects PDF Standard Security R=4
(R≤4 requires MD5; FIPS 140-3 forbids MD5).
Re-encrypt the document at R=6 (AES-256) or build pdf_oxide
without the 'fips' feature so the default 'rust-crypto'
provider stays active.

Die Lösung: das Dokument in einer nicht FIPS-beschränkten Umgebung mit R=6 (PDF 2.0 AES-256, ISO 32000-2 §7.6.4) neu verschlüsseln. R=6-Dokumente lassen sich unter aws-lc-rs problemlos öffnen.

Installation der FIPS-Variante. Die -fips-Distributionen liefern Single-Provider-Binärdateien (Prüfer verlangen dies in der Regel). Verwenden Sie pip install pdf_oxide_fips, npm install pdf-oxide-fips, dotnet add package PdfOxide.Fips, go get github.com/yfedoseev/pdf_oxide/go-fips oder bauen Sie Rust mit cargo build --features fips. Standard- und FIPS-Variante desselben Release-Tags sind auf allen Nicht-Krypto-Pfaden byteweise identisch.

Wie lege ich eine Laufzeit-Kryptorichtlinie fest?

Eine Richtlinie schränkt ein, welche Algorithmen erlaubt sind – unabhängig davon, welcher Provider aktiv ist. Sie ist einmalig setzbar (ein Laufzeit-Downgrade ist ein Angriffsvektor) und fail-closed (eine nicht parsbare Spezifikation wird abgelehnt und es wird keine Richtlinie installiert). Der Standard – nie gesetzt – ist "compat", was byteweise identisch zum Verhalten vor Einführung der Richtlinien ist.

Grammatik: mode[;clause]*

  • modecompat | strict | fips-strict
  • clause = (allow|deny):<alg>@<read|write> – z. B. "compat;deny:rc4@write;deny:md5@write" oder "fips-strict"

C-ABI-Fehlercodes für int32_t pdf_oxide_crypto_set_policy(const char *spec):

Code Bedeutung
0 Erfolg
1 ungültiges Argument (null / Nicht-UTF-8-Spezifikation)
2 Parse-Fehler – Spezifikation abgelehnt, Richtlinie nicht installiert
3 eine Richtlinie ist bereits gesetzt
import pdf_oxide

# Deny the legacy write paths while staying compatible for reads.
pdf_oxide.crypto_set_policy("compat;deny:rc4@write;deny:md5@write")
print(pdf_oxide.crypto_policy())  # canonical grammar string
use pdf_oxide::crypto::{self, SecurityPolicy};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse fail-closed, then install set-once.
    let policy: SecurityPolicy = "fips-strict".parse()?;
    crypto::set_policy(policy).expect("a crypto policy is already set");

    println!("active policy: {}", crypto::active_policy());
    Ok(())
}
package main

import (
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    // Treat any error as fatal — the policy is not installed on failure.
    if err := pdfoxide.SetCryptoPolicy("strict;deny:md5@write"); err != nil {
        log.Fatalf("policy rejected: %v", err)
    }
    log.Println("active policy:", pdfoxide.CryptoPolicy())
}
const pdf_oxide = require("pdf-oxide");

// Throws on an unparseable spec (fail-closed) or if already set.
pdf_oxide.setCryptoPolicy("fips-strict");
console.log(pdf_oxide.cryptoPolicy()); // "fips-strict"
using PdfOxide;

// Throws ArgumentException (rejected) or InvalidOperationException (already set).
PdfDocument.SetCryptoPolicy("compat;deny:rc4@write");
Console.WriteLine(PdfDocument.CryptoPolicy());

Signaturen

Operation Rust Python Go Node.js C#
Richtlinie festlegen crypto::set_policy(SecurityPolicy) -> Result<(), SetPolicyError> crypto_set_policy(spec: str) -> None SetCryptoPolicy(spec string) -> error setCryptoPolicy(spec: string): void SetCryptoPolicy(string spec) -> void
Richtlinie lesen crypto::active_policy() -> &'static SecurityPolicy crypto_policy() -> str CryptoPolicy() -> string cryptoPolicy(): string CryptoPolicy() -> string

Wie lese ich das Krypto-Inventar aus?

Das Inventar ist die Menge der Algorithmus-Tokens, die in diesem Prozess bisher tatsächlich genutzt wurden – ein sperrfreies atomares Bitset, das an jeder Durchsetzungsgrenze erfasst wird. Es ist der minimale Bericht „welche Kryptografie hat dieser Lauf verwendet?". Tokens sind stabile Kleinbuchstaben-Strings wie md5, aes256, rc4, sha256.

Die C-ABI gibt einen kommaseparierten String zurück (char *pdf_oxide_crypto_inventory(void), "", wenn nichts genutzt wurde); die High-Level-Bindings teilen ihn für Sie in eine Liste/Slice/ein Array auf.

import pdf_oxide

doc = pdf_oxide.PdfDocument("signed.pdf")
doc.verify_signatures()                  # exercises some algorithms

print(pdf_oxide.crypto_inventory())      # e.g. ["sha256", "aes256"]  (list[str])
use pdf_oxide::crypto;

// `inventory()` returns the exercised algorithms in declaration order.
let used: Vec<&'static str> = crypto::inventory()
    .into_iter()
    .map(|a| a.token())
    .collect();
println!("exercised: {used:?}"); // e.g. ["md5", "aes256"]
package main

import (
    "fmt"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    inv := pdfoxide.CryptoInventory() // []string, empty when nothing exercised
    fmt.Println("exercised:", inv)
}
const pdf_oxide = require("pdf-oxide");

console.log(pdf_oxide.cryptoInventory()); // string[], e.g. ["sha256"]
using PdfOxide;

foreach (var token in PdfDocument.CryptoInventory()) // string[]
    Console.WriteLine(token);

Wie exportiere ich eine CBOM (CycloneDX) für ein PDF?

crypto_cbom serialisiert das Inventar in eine CycloneDX-1.6-cryptographic-asset-Stückliste als JSON-String. Das Dokument hat stets bomFormat: "CycloneDX", specVersion: "1.6", einen metadata-Block (RFC-3339-Zeitstempel + die pdf_oxide-Tool-Komponente) und eine cryptographic-asset-Komponente pro genutztem Algorithmus. Ein leeres Inventar ergibt eine gültige BOM ohne Komponenten.

import json
import pdf_oxide

# Run your workload first so the inventory reflects what was used.
doc = pdf_oxide.PdfDocument("signed.pdf")
doc.verify_signatures()

cbom = pdf_oxide.crypto_cbom()           # JSON string
report = json.loads(cbom)
assert report["bomFormat"] == "CycloneDX"
assert report["specVersion"] == "1.6"

with open("pdf-cbom.json", "w") as f:
    f.write(cbom)
use std::fs;
use pdf_oxide::crypto;

fn main() -> std::io::Result<()> {
    // ... run the crypto workload first ...
    let cbom: String = crypto::cbom_json();   // CycloneDX 1.6 JSON
    fs::write("pdf-cbom.json", cbom)?;
    Ok(())
}
package main

import (
    "os"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    cbom := pdfoxide.CryptoCBOM() // CycloneDX 1.6 JSON string
    _ = os.WriteFile("pdf-cbom.json", []byte(cbom), 0o644)
}
const fs = require("fs");
const pdf_oxide = require("pdf-oxide");

const cbom = pdf_oxide.cryptoCbom(); // CycloneDX 1.6 JSON string
fs.writeFileSync("pdf-cbom.json", cbom);
using System.IO;
using PdfOxide;

string cbom = PdfDocument.CryptoCbom(); // CycloneDX 1.6 JSON string
File.WriteAllText("pdf-cbom.json", cbom);

CBOM-Struktur

Jeder genutzte Algorithmus wird zu einer Komponente:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "metadata": {
    "timestamp": "2026-06-22T12:00:00+00:00",
    "tools": { "components": [{ "type": "application", "name": "pdf_oxide", "version": "0.3.69" }] }
  },
  "components": [
    {
      "type": "cryptographic-asset",
      "name": "aes256",
      "bom-ref": "crypto/algorithm/aes256",
      "cryptoProperties": {
        "assetType": "algorithm",
        "algorithmProperties": {
          "primitive": "block-cipher",
          "classicalSecurityLevel": 256,
          "nistQuantumSecurityLevel": 0,
          "certificationLevel": ["fips140-3"]
        }
      }
    }
  ]
}

Das primitive wird aus der Algorithmusart abgeleitet (hash, block-cipher, stream-cipher für RC4, signature, kdf, drbg); certificationLevel ist ["fips140-3"] für FIPS-zugelassene Algorithmen und andernfalls ["none"].

Signaturen

Operation Rust Python Go Node.js C#
Inventar crypto::inventory() -> Vec<AlgorithmId> crypto_inventory() -> list[str] CryptoInventory() -> []string cryptoInventory(): string[] CryptoInventory() -> string[]
CBOM crypto::cbom_json() -> String crypto_cbom() -> str CryptoCBOM() -> string cryptoCbom(): string CryptoCbom() -> string

Empfohlene Startsequenz

Da Provider und Richtlinien beide einmalig setzbar sind und der Standard-Provider verzögert installiert wird, konfigurieren Sie die Krypto-Governance als allererstes, was Ihr Prozess tut:

import pdf_oxide

# 1. (optional) switch to FIPS — must precede any crypto operation.
if "aws-lc-rs" in pdf_oxide.crypto_available_providers():
    pdf_oxide.crypto_use_fips()

# 2. install the governance policy (fail-closed, set-once).
pdf_oxide.crypto_set_policy("fips-strict")

# 3. ... run your PDF workload ...
doc = pdf_oxide.PdfDocument("encrypted-r6.pdf")
doc.verify_signatures()

# 4. emit governance artifacts.
print("policy:   ", pdf_oxide.crypto_policy())
print("inventory:", pdf_oxide.crypto_inventory())
with open("pdf-cbom.json", "w") as f:
    f.write(pdf_oxide.crypto_cbom())

FAQ

Kann ich den Krypto-Provider nach dem Festlegen wieder zurückschalten? Nein. Sowohl use_fips (Provider) als auch set_policy (Richtlinie) sind per Design einmalig setzbar – ein Austausch mitten im Prozess, während laufender Kryptozustand existiert (FIPS-Selbsttest, HSM-Sitzung), ist ein Korrektheitsrisiko, und ein Laufzeit-Richtlinien-Downgrade ist ein Angriffsvektor. Tests, die einen frischen Provider benötigen, müssen in separaten Prozess-Namespaces laufen.

Warum sagt crypto_active_provider() "rust-crypto (default, lazy)", bevor ich irgendetwas tue? Diese Auslesung ist bewusst nicht initialisierend: Sie meldet den Standard, ohne ihn festzuschreiben, sodass ein späteres crypto_use_fips() weiterhin gelingen kann. Der echte Name erscheint, sobald ein Provider festgelegt ist.

Ist FIPS in jedem Paket verfügbar? Nur, wenn die native Bibliothek mit --features fips gebaut wurde. Verwenden Sie die parallelen -fips-Distributionen (pdf_oxide_fips, pdf-oxide-fips, PdfOxide.Fips, go-fips), die Single-Provider-FIPS-Binärdateien liefern. In einem Nicht-FIPS-Build ist fips_available false und use_fips liefert einen Fehler.

Ist die CBOM standardkonform? Ja – es handelt sich um ein gültiges CycloneDX-1.6-Dokument (bomFormat: "CycloneDX", specVersion: "1.6"), sodass es sich direkt in SBOM-/CBOM-Werkzeuge und Lieferketten-Audits einfügt.

Verlangsamt das Festlegen einer Richtlinie die PDF-Verarbeitung? Nein. PDF Oxide bleibt bei seinem Extraktionsprofil von 0,8 ms im Mittel / 100 % Bestehensquote; das Inventar ist ein sperrfreies atomares Bitset und Richtlinienprüfungen finden nur an Krypto-Durchsetzungsgrenzen statt.

Nächste Schritte