using DevExpress.Pdf;
using DevExpress.Office.DigitalSignatures;
namespace DXApp.TemplateKitProject.Services;
///
/// Service zum digitalen Signieren von PDF-Dokumenten mit DevExpress PdfDocumentSigner
///
public class PdfSigningService(
IConfiguration configuration,
ILogger logger)
{
///
/// Signiert eine PDF-Datei mit einem PFX-Zertifikat
///
public async Task SignPdfAsync(byte[] pdfBytes, PdfSigningOptions? options = null)
{
options ??= new PdfSigningOptions();
return await Task.Run(() =>
{
try
{
// Zertifikat-Konfiguration laden
var certPath = options.CertificatePath ?? configuration["PdfSigning:CertificatePath"]
?? throw new InvalidOperationException("PdfSigning:CertificatePath nicht konfiguriert.");
var certPassword = options.CertificatePassword ?? configuration["PdfSigning:CertificatePassword"]
?? throw new InvalidOperationException("PdfSigning:CertificatePassword nicht konfiguriert.");
logger.LogInformation("PDF wird signiert mit Zertifikat: {Path}", certPath);
// PDF in MemoryStream laden
using var inputStream = new MemoryStream(pdfBytes);
using var signer = new PdfDocumentSigner(inputStream);
// PKCS#7 Signatur erstellen
var pkcs7Signature = new Pkcs7Signer(certPath, certPassword, HashAlgorithmType.SHA256);
// Signatur-Feld definieren
var fieldInfo = new PdfSignatureFieldInfo(signer.PageCount - 1); // Letzte Seite
fieldInfo.Name = $"Signature_{DateTime.Now:yyyyMMdd_HHmmss}";
fieldInfo.SignatureBounds = new PdfRectangle(
options.SignatureX,
options.SignatureY,
options.SignatureX + options.SignatureWidth,
options.SignatureY + options.SignatureHeight
);
// Signatur-Builder erstellen
var signatureBuilder = new PdfSignatureBuilder(pkcs7Signature, fieldInfo);
signatureBuilder.Name = options.SignerName;
signatureBuilder.Location = options.Location;
signatureBuilder.Reason = options.Reason;
// Optional: Signatur-Bild hinzufügen
if (!string.IsNullOrEmpty(options.SignatureImagePath) && File.Exists(options.SignatureImagePath))
{
signatureBuilder.SetImageData(File.ReadAllBytes(options.SignatureImagePath));
logger.LogDebug("Signatur-Bild hinzugefügt: {Path}", options.SignatureImagePath);
}
// Signierte PDF in MemoryStream speichern
using var outputStream = new MemoryStream();
signer.SaveDocument(outputStream, signatureBuilder);
logger.LogInformation(
"PDF erfolgreich signiert. Größe: {Original} KB ? {Signed} KB",
pdfBytes.Length / 1024,
outputStream.Length / 1024
);
return outputStream.ToArray();
}
catch (Exception ex)
{
logger.LogError(ex, "Fehler beim Signieren der PDF-Datei.");
throw;
}
});
}
///
/// Liest Signatur-Informationen aus einer signierten PDF
///
public async Task> GetSignatureInfoAsync(byte[] pdfBytes)
{
return await Task.Run(() =>
{
try
{
using var stream = new MemoryStream(pdfBytes);
using var signer = new PdfDocumentSigner(stream);
var signatures = signer.GetSignatureInfo();
var result = new List();
foreach (var sig in signatures)
{
var info = new SignatureInformation
{
FieldName = sig.FieldName,
SignerName = sig.SignerName,
Location = sig.Location,
Reason = sig.Reason,
Date = DateTime.Now, // Wird später aus PKCS#7 gelesen
CertificationLevel = sig.CertificationLevel.ToString()
};
// PKCS#7 Details auslesen
try
{
var pkcs7 = signer.GetPdfPkcs7Signature(sig.FieldName);
var cert = pkcs7.GetSignatureCertificate();
// Signatur-Datum: GetTimeStampDate oder aktuelles Datum
var timestampDate = pkcs7.GetTimeStampDate();
info.Date = timestampDate ?? DateTime.Now;
info.IsSignatureValid = pkcs7.VerifySignature();
info.IsCertificateValid = cert.Verify();
info.CertificateIssuer = cert.IssuerName.Name ?? string.Empty;
info.CertificateSubject = cert.SubjectName.Name ?? string.Empty;
info.CertificateValidUntil = cert.NotAfter;
}
catch (Exception ex)
{
logger.LogWarning(ex, "Fehler beim Auslesen der PKCS#7-Details für Signatur '{Name}'", sig.FieldName);
}
result.Add(info);
}
logger.LogInformation("PDF enthält {Count} Signatur(en)", result.Count);
return result;
}
catch (Exception ex)
{
logger.LogError(ex, "Fehler beim Auslesen der Signatur-Informationen.");
throw;
}
});
}
}
///
/// Optionen für die PDF-Signatur
///
public class PdfSigningOptions
{
public string? CertificatePath { get; set; }
public string? CertificatePassword { get; set; }
public string SignerName { get; set; } = "DXApp System";
public string Location { get; set; } = "Digital Data AG";
public string Reason { get; set; } = "Dokument digital signiert";
// Position und Größe der sichtbaren Signatur (in Points, 72 DPI)
public double SignatureX { get; set; } = 50;
public double SignatureY { get; set; } = 50;
public double SignatureWidth { get; set; } = 200;
public double SignatureHeight { get; set; } = 80;
public string? SignatureImagePath { get; set; }
}
///
/// Informationen über eine PDF-Signatur
///
public class SignatureInformation
{
public string FieldName { get; set; } = string.Empty;
public string SignerName { get; set; } = string.Empty;
public string Location { get; set; } = string.Empty;
public string Reason { get; set; } = string.Empty;
public DateTime Date { get; set; }
public string CertificationLevel { get; set; } = string.Empty;
public bool IsSignatureValid { get; set; }
public bool IsCertificateValid { get; set; }
public string CertificateIssuer { get; set; } = string.Empty;
public string CertificateSubject { get; set; } = string.Empty;
public DateTime CertificateValidUntil { get; set; }
}