24 Apr. 2025
Signierte Imports statt Authentifizierung
Grundlagen (was man wissen muss)
RSA bildet eine mathematische Grundlage für asymetrische Verschlüsselung. Man kennt eine Verwendung bei https
und verschlüsselten Zugriffen auf eine Webseite. Der Server kennt den privaten und öffentlichen Schlüssel, der Client nur den öffentlichen Schlüssel. Der Client kann mit den öffentlichen Schlüssel einen Text verschlüsseln, der Server benötigt -und hat- den privaten Schlüssel (!) zum Entschlüsseln der Nachricht. Der öffentliche Schlüssel ist eine Einbahnstraße und kann nur zum verschlüsseln verwendet werden.
Der Signierung liegt eine ähnliche Idee zu Grunde. Es wird ein Hash über einen Text gebildet und mit dem privaten Schlüssel sozusagen "verschlüsselt". Mit dem öffentlichen Schlüssel kann man prüfen, ob der zugehörige private Schlüssel den Hash "verschlüsselt" hat. Der öffentliche Schlüssel kann also hier nur prüfen, wurde der zugehörige private Schlüssel verwendet.
Und mehr muss man erst einmal nicht wissen.
Export
Der Export eines Posts oder des gesamten Blogs ist trivial. Die entsprechenden Datenbankentitäten werden in ein "Export-View-Model" überführt und anschließend mittels JSON Serializer als Zeichenkette serialisiert. Für das Konzept ist es unerheblich, ob es sich bei dem Export um ein JSON, BSON, XML oder irgendetwas proprietäres handelt, solange der Export und Import symmetrisch implementiert sind.
Hier ein kleines Beispiel für JSON.
// load full blog
blog = await context.Blogs
.Include(x => x.Posts).ThenInclude(x => x.HeaderImage)
.Include(x => x.PostImages)
.FirstOrDefaultAsync(m => m.UniqueId == postsConfiguration.CurrentValue.BlogUniqueId);
if (blog is null) throw new ArgumentException($"Blog {postsConfiguration.CurrentValue.BlogUniqueId} not Found");
var export = CreateBlogExport(blog); // this is where the Blog Export Model is created
var filename = $"{blog!.Title}.{DateTime.Now:yyyyMMdd-hhmm}.json";
return File(Encoding.UTF8.GetBytes(export.AsJson()), "application/json", filename);
Signierung
Für das Signieren benötigt man einen privaten und den zugehörigen öffentlichen Schlüssel. Am einfachsten erstellt man das Schlüsselpaar über openssl
(link) (favorisiert) und hat dann direkt zwei Dateien für jeweils den öffentlichen und privaten Schlüssel.
Das Signieren kann über eine kleines Tool oder PowerShell durchgeführt werden. In diesem Fall entsteht eine weitere Datei mit der Endung .sig
, welche den "verschlüsselten Hash" als Base64 enthält.
Zum signieren und verifizieren wird der folgende Code benutzt. Das SignatureUtil
ist unten als Gist für ein Copy & Paste.
// Signieren
var json = File.ReadAllText(Source);
var cert = X509Certificate2.CreateFromPemFile(PublicKey, PrivateKey);
var signature = SignatureUtil.Sign(json, cert);
File.WriteAllText(sigFile, signature);
Import
Der Import benötigt nun zwei Dinge, den Export selbst und den verschlüsselten Hash. Zunächst wird der Export verifiziert.
// Verifizieren
var cert = new X509Certificate2(PublicKey);
if (!Verify(JsonDocument!, Signature, cert))
throw new Exception("Signature is Invalid");
Anschließend kann der Export importiert werden, in diesem Fall als JSON über das Blog-Export-Model.
Der Code
Hier die kleine Helferklasse, welche eine Signatur erstellt und verifitiziert.