Entwickler-Ecke

Verteilte Systeme - Sichere Updates


Csharp-programmierer - Do 05.01.17 19:49
Titel: Sichere Updates
Hallo liebes Entwickler Team,
wie kann man Updates sicherer machen?

Vorab, bis jetzt funktioniert der Update Prozess so: Auf einem Server wird eine Textdatei mit der neuesten Versionsnummer gehostet. Das Programm lädt sich diese runter und vergleicht den Inhalt mit der jetzigen Programmversion. Gibt es Unterschiede, wird ein ZIP heruntergeladen und installiert. Fertig.

Jetzt kann ja ein Angreifer die den ZIP abfangen, manipulieren und die anderen PCs installieren dann den modifizierten ZIP.
Wie kann man das sicherer machen? Ich dachte an einen Schlüssel, mit dem die Text Datei signiert ist und an einen Gegenschlüssel, den das Programm hat. Wenn diese beiden übereinstimmen, wird der ZIP heruntergeladen.

Kann mir mal jemand sagen, was er davon hält und über welchen Sicherungsalgorithmus kann ich das machen? Sha256?


Ralf Jansen - Do 05.01.17 20:06

Hatten wir das Thema Codesigning nicht gerade erst?

Besorg die ein entsprechendes Zertifikat und signiere damit deinen Installer. Ob du noch eine spezielle clientseitige Prüfung brauchst musst du selbst wissen aber ich finde die Zweifelhaft. Den kann man ja vermutlich auch so runterladen für zum Beispiel eine Erstinstallation ohne das das jetzt deine individuelle Updatefunktion aus deiner Anwendung verwendet wird. Es gibt dann keinen clientseitigen Code der da was prüfen könnte. Man kann den User nur anleiten wie er die Signatur selbst prüft wenn ihm das wichtig ist oder man verläßt sich auf die Signaturprüfung von Windows selbst.


Csharp-programmierer - Do 05.01.17 20:13

Achso, aber ich habe keine Website gesehen, wo man ein kostenloses Zertifikat bekommt. Und ich kann jetzt nicht nochmal Geld in sowas reinstecken :/


Ralf Jansen - Do 05.01.17 20:36

Dann denke ich wirst du keine sichere (automatische) Downloadmöglichkeit hinbekommen.

Ohne asymmetrischen Schlüssel der von einer glaubwürdigen Stelle als glaubwürdig zertifiziert ist, wäre eine andere Möglichkeit denn Hash des bekannten sicheren Downloads zu veröffentlichen. Sowas schien dir ja auch vorzuschweben. Das geht aber nicht automatisch. Wenn du oder dein User dem Kanal nicht trauen über denn du den Download bekommst warum solltest man dem Kanal trauen um den Hash zum User zu bringen? Das mit dem Hash funktioniert nur einigermaßen wenn da ein 2.ter Kanal mit Aktivität vom User aus besteht. (2 verschieden Wege gleichzeitig passend zu hacken ist schwer) Dazu könntest du die Hashes der bekannten Sicheren Dateien auf deiner Webseite veröffentlichen. Dann muss aber der User selbst aktiv werden und sich diesen Hash aktiv von deiner Webseite holen und gegen deine Anwendung prüfen und zwar mit einem Hashing-Tool das nicht zu deiner Anwendung gehört. Denn wenn du dem Download nicht traust (sonst wärst du nicht hier und würdest diese Frage nicht stellen ;) ) gibt es keinen Grund einem Tool aus gleicher Quelle zu trauen.

Heißt:
- Hash auf die Webseite packen
- Dort dokumentieren wie man den Hash prüft (wo man ein passendes Tool bekommt und am besten wie man das verwendet)
- In der Anwendung maximal eine Anleitung geben wie man manuell die Sicherheit des Downloads prüft aber nichts automatisch machen da automatisch nicht wirklich möglich ist bzw. nur Scheinsicherheit wäre


jfheins - Fr 06.01.17 00:01

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Dann denke ich wirst du keine sichere (automatische) Downloadmöglichkeit hinbekommen.

Naja, es ist noch nicht aller Tage Abend ;-)

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:

- Hash auf die Webseite packen
- Dort dokumentieren wie man den Hash prüft (wo man ein passendes Tool bekommt und am besten wie man das verwendet)
- In der Anwendung maximal eine Anleitung geben wie man manuell die Sicherheit des Downloads prüft aber nichts automatisch machen da automatisch nicht wirklich möglich ist bzw. nur Scheinsicherheit wäre

Es ist in der Tat schwierig, die Erstinstallation abzusichern. Aber Updates sollten sich ja machen lassen.

Dazu einfach ein asymmetrisches Schlüsselpaar erzeugen und den public-Key im Programm hinterlegen.
Für ein Update muss man dann das zip-Archiv signieren. Das heißt, eine Checksumme (z.B. sha256) wird mit dem privaten Schlüssel verschlüsselt. Der Updater lädt nun das zip und die Signatur getrennt runter und kann schließlich das zip wieder hashen, die Signatur entschlüsseln und beides vergleichen.

Da kann man auch problemlos seine eigene CA aufmachen und muss kein Geld ausgeben. Aber es sind dann halt nur die Updates abgesichert.


Holgerx - Fr 06.01.17 06:51

Hmm..

Also der TE hat die Download und Prüfroutinen selber geschrieben, braucht also kein externes Tool zum Prüfen.

Ich denke, vereinfacht würde ich das so machen:

- Von Update-Datei einen Hash erzeugen
- Diesen Hash verschlüsselt in die Info-Datei

- Client lädt die Info-Datei und dekodiert den HASH
- Client lädt die Update-Datei und vergleicht den Datei-Hash

- Wenn übereinstimmend, dann alles OK

Durch das verschlüsseln des Hashes ist das Problem reduziert, das sowohl die Update-Datei, wie auch die Info-Datei bearbeitet wurden.
Die Keys für die Verschlüsselung kennst nur Du und dein Download-Tool. Somit kann ein Dritter nicht unbemerkt deine Update-Datei bearbeiten, ohne dass Du es merkst..

Wie gesagt, nur eine vereinfachte Idee, ohne Zertifikate.

Um das ganze noch sicherer zu machen, könntest Du bei einem Update auch einen neuen Key mitschicken, welcher dann für den nächsten Download gültig ist.
Dann müssen die Updates nur in einer festen Reihenfolge geladen und eingespielt werden.


Csharp-programmierer - Sa 07.01.17 17:58

Die Idee von Holgerx gefällt mir schon ganz gut. Aber angenommen in dem Quellcode ist ein Hash, man lädt sich das Update herunter und der andere Hash stimmt mit diesem überein. Aber wenn man das Programm dekompilliert, kommt man ja auch an den Hash, oder nicht?

Und wenn man eine Version bsp überspringt, kann es doch dann auch zu Problemen kommen, oder nicht?


Ralf Jansen - Sa 07.01.17 18:16

In deinem Programm ist kein Hash der zu dekompilieren ist. Und wenn es so wäre wäre es auch nicht schlimm ein Hash ist kein Geheimnis. Deine Frage zeigt das dir scheinbar nicht ganz klar ist was ein Hash ist. Du solltest da mal nachforschen und dir denn Unterschied zwischen einem Verschlüsselungsverfahren und einem Hashingverfahren klar machen. Hashing ist nicht verschlüsseln.

Die Idee von HolgerX bringt dich meiner Meinung nicht weiter. Er möchte das du nicht nur das Update sondern auch den Hash runterlädst. Aber wenn man dem Kanal über den man das Update bekommt nicht traut wieso sollte der Kanal vertrauenswürdig sein wenn man über diesen Weg auch den Hash holt? Wenn das eine zu hacken ist wird man einfach auch beides hacken können. Zu einem falschen Update bekommst du also auch einfach einen passenden falschen Hash. Denn Hash mit runterzuladen hilf vielleicht einen kaputten Dateidownloader zu entlarven aber das Verfahren hält keinen potentiellen Angreifer auf. Es hilft ein wenig 2 dezidiert unterschiedliche Wege zu benutzen um an Hash und Update zu kommen. Letzlich finde ich das Verfahren dann aber immer noch fraglich.

Da ist die Idee von jfheins schon deutlich besser. Ich persönlich mag das nicht weil was hilft es wenn ich den Update einer Anwendung absichere das zu dieser Absicherung ein bestimmtes Mitwirken der bisherigen Installation der Anwendung braucht ich die Erstinstallation aber schon gar nicht so hinbekommen habe das diese Erstinstallation sicher (aka unverändert) ist.


Holgerx - Sa 07.01.17 20:03

Hmmm..

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:

Die Idee von HolgerX bringt dich meiner Meinung nicht weiter. Er möchte das du nicht nur das Update sondern auch den Hash runterlädst. Aber wenn man dem Kanal über den man das Update bekommt nicht traut wieso sollte der Kanal vertrauenswürdig sein wenn man über diesen Weg auch den Hash holt? Wenn das eine zu hacken ist wird man einfach auch beides hacken können. Zu einem falschen Update bekommst du also auch einfach einen passenden falschen Hash. Denn Hash mit runterzuladen hilf vielleicht einen kaputten Dateidownloader zu entlarven aber das Verfahren hält keinen potentiellen Angreifer auf. Es hilft ein wenig 2 dezidiert unterschiedliche Wege zu benutzen um an Hash und Update zu kommen. Letzlich finde ich das Verfahren dann aber immer noch fraglich.



Und deshalb sollte der HASH verschlüsselt sein, und die Keys für die Entschlüsselung kennen nut CLient und der Ersteller des Original-Updates.
Somit können BEIDE Dateien abgefangen werden, aber wenn der Hacker nicht den Key kennt, dann kann er HASH-Werte erzeugen, wie er will, der Client akzeptiert das Update nicht..

Ist, wie ich geschrieben hatte ein 'vereinfachtes' Verfahren.

Wenn noch dazu die Updates von einem Server per z.B. HTTPS heruntergeladen werden (mit Authentifizierung) dann ist auch dies wieder eine weitere Absicherung.

Dies kann zwar immer weiter auf die Spitze getrieben werden, aber wenn der User mit irgendwelchen Dritttools irgendwelche Zertifikate prüfen soll, bevor er diese Updates installiert, dann ist das aus meiner Sicht Overkill.
Dann brauche ich auch gar kein Online-Update und der User bekommt das Update per z.B. CD von mir in die Hand gedrückt.. ;)
So spare ich mir den ganzen Aufwand..


jfheins - Sa 07.01.17 22:54

Der wirklich "gute" Trick ist aber ja die asymetrische Verschlüsselung. Ich nehme mal an, du meintest hier:

user profile iconHolgerx hat folgendes geschrieben Zum zitierten Posting springen:
- Von Update-Datei einen Hash erzeugen
- Diesen Hash verschlüsselt in die Info-Datei

- Client lädt die Info-Datei und dekodiert den HASH
- Client lädt die Update-Datei und vergleicht den Datei-Hash

eine symmetrische Verschlüsselung. Das Problem daran ist ja, dass jeder der den Updater dekompilieren kann an den Schlüssel bekommt.
Und da das Programm ja als bekannt gilt, ist die Maßnahme wenig sinnvoll.

Zum Einstieg kannst du dir ja mal die Klasse RSACryptoServiceProvider [https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider(v=vs.110).aspx] anschauen.
Damit solltest du einSchlüsselpaar erzeugen können, dass du bei die speichern musst. Dann kannst du für ein Update die Methode SignData() aufrufen: https://msdn.microsoft.com/en-us/library/mt132675(v=vs.110).aspx

In dem Updater gibst du den public Key als XML mit und rufst dann VerifyData() [https://msdn.microsoft.com/en-us/library/mt132677(v=vs.110).aspx] auf.

Bei zip-Dateien kannst du sogar noch Bytes hinter das eigentliche Archiv schreiben. Damit brauchst du keine separate Datei sondern fügst die Signatur einfach an die Datei an. Den Signaturprozess kannst du auch per Powershell aufrufen, wenn du dafür kein eigenes C#-Programm schreiben willst.


jfheins - Sa 07.01.17 23:57

Weil ich gerade Zeit und Interesse hatte, habe ich mal ein Demo-Programm gemacht:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
namespace Test_1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Schlüssel erzeugen, einmalig!
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                var privateKey = rsa.ToXmlString(includePrivateParameters: true);
                var publicKey = rsa.ToXmlString(includePrivateParameters: false);

                File.WriteAllText(@"./private.xml", privateKey);
                File.WriteAllText(@"./public.xml", publicKey);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // Signieren, beliebig oft
            var filename = @"./nextUpdate.zip";

            var fileContent = File.ReadAllBytes(filename);
            var rsaKey = File.ReadAllText(@"./private.xml");
            byte[] signature;

            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(rsaKey);
                signature = rsa.SignData(fileContent, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            }

            // Signatur anhängen
            using (var stream = new BinaryWriter(new FileStream(filename, FileMode.Append)))
            {
                stream.Write(signature);
                stream.Write(signature.Length);
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            // Signatur prüfen

            var filename = @"./nextUpdate.zip";

            var fileContent = File.ReadAllBytes(filename);

            if (fileContent.Length < 260)
            {
                MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung");
                return;
            }

            var rsaKey = File.ReadAllText(@"./public.xml"); // Hier wird nur der public-Key benötigt!
            byte[] signature = null;
            int signatureLength = 0;

            // Signatur auslesen, sie hängt hinten an der Datei
            using (var stream = new MemoryStream(fileContent))
            {
                using (var reader = new BinaryReader(stream))
                {
                    stream.Seek(-4, SeekOrigin.End);
                    signatureLength = reader.ReadInt32();
                    if (signatureLength != 256)
                    {
                        MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung");
                        return;
                    }
                    else
                    {
                        stream.Seek(-4 - signatureLength, SeekOrigin.End);
                        signature = reader.ReadBytes(signatureLength);
                    }
                }
            }
            var contentLength = fileContent.Length - signatureLength - 4;

            // Signatur gegen den vorhergehenden Teil (= das zip Archiv) prüfen
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(rsaKey);
                var result = rsa.VerifyData(fileContent, 0, contentLength, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                if (result)
                    MessageBox.Show("Signatur erfolgreich verifiziert :-)""Signaturprüfung");
                else
                    MessageBox.Show("Beschädigte oder fehlerhafte Signatur!""Signaturprüfung");
            }
        }
    }
}


Du musst dann nur aufpassen, dass du gut auf den privaten Schlüssel aufpasst. Wenn du den verlierst (= vernichtet wird), musst du ein neues Schlüsselpaar generieren und alle Update-Prüfungen werden Alarm schlagen. Wenn der private Schlüssel entwendet wird, dann ist der Updater wieder unsicher.

Sag Bescheid, wenn was unklar ist :wink:


Csharp-programmierer - So 08.01.17 00:46

Bei mir wird Sha256 und Pkcs1 unterkringelt, da irgendein Assembly Verweis fehlt. Welchen muss ich da hinzufügen?


jfheins - So 08.01.17 01:31

Laut der Doku-Seite [https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsasignaturepadding.pkcs1(v=vs.110).aspx]:
Zitat:
Namespace: System.Security.Cryptography
Assembly: mscorlib (in mscorlib.dll)

Ist das schon drin.

Aber ich glaube, die Typen gibt es erst mit .net 4.6. Du müsstest dann das Zielframework anpassen. (Rechtsklick auf das Projekt, Eigenschaften, Zielframework)


Csharp-programmierer - So 08.01.17 13:42

Ah okay. Vielen Dank, dass funktioniert.

Aber ich habe nun noch ein paar Verständnis Fragen:
1) Wenn ich nun einen neuen Update ZIP habe, den muss ich ja dann auswählen, damit der Algorithmus diesen signiert. Die Methode von Button3 muss ja dann in jedes Clienten Programm?
2) Wenn ich mehrere unterschiedliche Projekte habe, haben die dann unterschiedliche Schlüssel? Also denkt sich die Klasse dann zufällige Schlüssel aus? Und dann brauche ich ja unterschiedliche Ordner mit den unterschiedlichen Schlüsseln, oder? Und den public Key muss ich ja dann im ZIP mitliefern, oder?


jfheins - So 08.01.17 15:12

user profile iconCsharp-programmierer hat folgendes geschrieben Zum zitierten Posting springen:
Ah okay. Vielen Dank, dass funktioniert.

Aber ich habe nun noch ein paar Verständnis Fragen:
1) Wenn ich nun einen neuen Update ZIP habe, den muss ich ja dann auswählen, damit der Algorithmus diesen signiert. Die Methode von Button3 muss ja dann in jedes Clienten Programm?

Ja, Button 2 ist quasi das Programm bei dir auf dem Rechner (bevor du das Update veröffentlichst) und der Code aus Button 3 ist in jedem Client.

user profile iconCsharp-programmierer hat folgendes geschrieben Zum zitierten Posting springen:
2) Wenn ich mehrere unterschiedliche Projekte habe, haben die dann unterschiedliche Schlüssel? Und dann brauche ich ja unterschiedliche Ordner mit den unterschiedlichen Schlüsseln, oder?

Können haben, muss aber nicht. Wenn man Geld bezahlen muss, lässt man sich i.d.R. ein Zertifikat pro "Firma/Herausgeber" ausstellen. Wenn du jedem Projekt einen eigenen Schlüssel gibst, wird es bei dir etwas unübersichtlicher.

user profile iconCsharp-programmierer hat folgendes geschrieben Zum zitierten Posting springen:
Also denkt sich die Klasse dann zufällige Schlüssel aus?

Ja, der Konstruktor generiert ein neues, zufälliges Schlüsselpaar. Du solltest das aber nur einmal generieren und dann speichern.

user profile iconCsharp-programmierer hat folgendes geschrieben Zum zitierten Posting springen:
Und den public Key muss ich ja dann im ZIP mitliefern, oder?

Auf keinen Fall! Der public-Key muss bereits im Programm vorhanden sein. Ein Angreifer, der das zip austauschen kann, könnte ja sonst einfach seinen Schlüssel rein tun.

Deshalb sichert das ganze auch nur den Update-Mechanismus. Die Erstinstallation bringt den public-Key auf den Rechner und der Updater prüft, dass alle zukünftigen Updates zu diesem Schlüssel passen.
Du kannst natürlich ein Update herausbringen, welches den Schlüssel austauscht. Dann wäre in dem zip ein neuer public-Key, signiert wäre es aber noch mit dem alten Schlüssel.


Csharp-programmierer - So 08.01.17 15:53

Ah okay.
Für 2017 habe ich mir vorgenommen, Code nicht nur einfach abzuschreiben sondern auch zu verstehen, was da passiert.

Wenn ein Programm nun nach Updates sucht, wird ja die Textdatei mit der neuesten Versionsnummer vom Server heruntergeladen. Da muss ich ja noch nichts verschlüsseln. Für die Installation des neuen Updates brauche ich ja noch ein 2. Programm, den Updater. Und dieser Updater löscht dann das alte Programm, lädt sich den ZIP runter, kontrolliert die Signierung und installiert die Anwendung demnach. Aber wenn ich dem Updaten den Public Key mitgebe, kann ja ein Angreifer das Programm dekompillieren und so an den Schlüssel kommen und wenn der den Schlüssel hat, kann er ja auch den ZIP so signieren, oder habe ich da einen Denkfehler?


Ralf Jansen - So 08.01.17 18:41

Zitat:
Ah okay.
Für 2017 habe ich mir vorgenommen, Code nicht nur einfach abzuschreiben sondern auch zu verstehen, was da passiert.

:zustimm:
Zitat:
Aber wenn ich dem Updaten den Public Key mitgebe, kann ja ein Angreifer das Programm dekompillieren und so an den Schlüssel kommen und wenn der den Schlüssel hat, kann er ja auch den ZIP so signieren, oder habe ich da einen Denkfehler?

Der öffentlichen Schlüssel ist kein Geheimnis. Überleg mal wie das klingt wenn du behauptest du möchtest den öffentlichen Schlüssel Geheim halten. Wenn man den Geheim halten müsste warum sollte der dann öffentlicher Schlüssel heißen? Nein mit dem öffentlichen Schlüssel kann ein Angreifer wenig anfangen damit kann er nicht mehr im Sinne deines System verschlüsseln er kann damit nur entschlüsseln. Verschlüsseln geht nur mit dem privaten Schlüsseln und auf den musst du aufpassen und geheim halten.

Um dieses System zu knacken muss beides ausgetauscht werden öffentlicher Schlüssel und privater Schlüssel. Da sind wir dann wieder an dem Punkt das die Erstinstallation nicht geschützt ist und somit der Angriff veränderter Client und veränderter Download(Update) noch besteht.


Csharp-programmierer - So 08.01.17 20:47

Okay, ich habe gerade einen großen Denkfehler. Angenommen der Hashwert ist xxxxx. Dann ist der ZIP Ordner mit xxxxx signiert und in der Anwendung ist der public key xxxxx. Wenn ein Angreifer jetzt das Programm dekompilliert, bekommt er das xxxxx. Jetzt kann er ja den ZIP abfangen, sein Schadprogramm dort reinmachen und den neuen ZIP mit xxxxx signieren, jetzt denkt ja das Programm, dass sei ein signiertes Update und es wird installiert, oder verstehe ich das vollkommen falsch?


Ralf Jansen - So 08.01.17 21:28

Zitat:
und den neuen ZIP mit xxxxx signieren


Woher sollte er dafür denn privaten Schlüssel haben? Er hat nur den öffentlichen. Zum signieren braucht es aber den privaten Schlüssel. Mit dem öffentlichen kann man (mehr oder weniger) nur prüfen.


Csharp-programmierer - So 08.01.17 21:41

Aber durch den privaten Schlüssel kommt doch ein Hash raus, der bsp xxxxx ist, oder?


Ralf Jansen - So 08.01.17 22:17

Jein. Beim signieren kommt eine Signatur heraus. Bei den Beispiel von jfheins wo er RSA verwendet ist das so das von dem Datenstrom ein Hash berechnet wird und dieser dann per RSA verschlüsselt wird. Diese Kombination ist dann die Signatur. Und diese Signatur kann nur mit dem privaten Schlüssel erstellen und nur mit dem öffentlichen Schlüssel prüfen. Der Hash ist nur mittel zum Zweck und hilft allein nicht.

Du möchtest ja, wie du gesagt hast, verstehen wie dein Code funktioniert. Die verstärkte Version davon ist natürlich die logische Grundlage zu verstehen warum man das so kodiert. Hier jetzt RSA zu erklären und warum man nicht einfach per RSA die Nachricht verschlüsselt/signiert (was denkbar wäre) sondern noch ein Hash dazwischen geschaltet wird liegt im prinzipiellen Aufbau von RSA begründet und führt hier zuweit. Für andere asymmetrische Verschlüsselungen wäre das möglicherweise unnötig. Da kenne ich mich jetzt aber zu wenig aus um Namedropping zu betreiben oder es ist doch ein prinzipielles Problem asymmetrischer Verschlüsselungen die sich mir so nicht erschließt. Letztlich wenn dich die Details interessieren sollte du das einfach an geeigneter Stelle nachlesen wie genau RSA funktioniert und warum man wie damit signiert.


jfheins - Mo 09.01.17 01:21

Also sagen wir mal, der Hashwert ist XYZ.

Mit deinem Privaten Schlüssel ("abcedefgh") verschlüsselst du diesen jetzt, Ergebnis ist "ÜKOOP".
Der öffentliche Schlüssel, der herausgegeben wird, ist aber nur ein Teil von dem privaten Schlüssel, sagen wir "defgh". Dieser Teil genügt, um den Hash zu entschlüsseln und aus "ÜKOOP" wieder XYZ zu machen.
Es ist aber nicht möglich, mit diesem öffentlichen Schlüssel einen anderen Hash zu verschlüsseln.


Genauer gesagt kannst du aus dem privaten Schlüssel immer den öffentlichen Schlüssel (leicht) errechnen, und Botschaften die mit dem einen verschlüsselt wurden mit dem anderen entschlüsseln. Deshalb heißt das auch asymmetrische Verschlüsselung. Wenn du Sachen mit dem privaten Schlüssel verschlüsselst, nennt man das Signatur. Nur du kannst Sachen signieren, aber jeder kann die Signatur prüfen (also, dass du es gewesen sein musst, der das verschlüsselt hat). Andersherum kann jeder mit dem öffentlichen Schlüssel Sachen verschlüsseln, die dann nur du lesen kannst.

Bei einer symmetrischen Verschlüsselung brauchst du zu ver- und entschlüsseln immer den gleichen Schlüssel. (Es gibt nur einen)

Vielleicht noch ein Wikipedia-Link: https://de.wikipedia.org/wiki/Asymmetrisches_Kryptosystem


Csharp-programmierer - Mo 09.01.17 17:23

Ah okay.

Also wird dann jetzt der ZIP an sich signiert, ja? Und wenn das Programm die neuste Programmversion vom Server herunterlädt, muss man in dem Moment noch nichts signieren, oder?


Csharp-programmierer - Do 12.01.17 17:18

So, jetzt bin ich gerade dabei, den Updater zu programmieren, aber anscheinend gibt es einen Codeabschnitt, der nicht ausgeführt wird:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
public void InstallNewUpdate()
        {
            try
            {
                WebClient client = new WebClient();
                Uri URL = new Uri(@"http://meineURL.com/meineDatei.zip");
                string LocalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");

                client.DownloadFile(URL, LocalPath);
                MessageBox.Show("g");
                client.DownloadFileCompleted += DownloadCompleted;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            try
            {
                string LocalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");
                MessageBox.Show("hier 4");
                // Signatur prüfen
                var filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");
                var fileContent = File.ReadAllBytes(filename);

                if (fileContent.Length < 260)
                {
                    MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung");
                }
                else
                {
                    MessageBox.Show("Hier 1");
                }

                var rsaKey = File.ReadAllText(@"./public.xml");
                byte[] signature = null;
                int signatureLength = 0;

                // Signatur auslesen, sie hängt hinten an der Datei
                using (var stream = new MemoryStream(fileContent))
                {
                    using (var reader = new BinaryReader(stream))
                    {
                        stream.Seek(-4, SeekOrigin.End);
                        signatureLength = reader.ReadInt32();
                        if (signatureLength != 256)
                        {
                            MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung");
                            return;
                        }
                        else
                        {
                            stream.Seek(-4 - signatureLength, SeekOrigin.End);
                            signature = reader.ReadBytes(signatureLength);
                            MessageBox.Show("Hier 2");
                        }
                    }
                }
                var contentLength = fileContent.Length - signatureLength - 4;

                // Signatur gegen den vorhergehenden Teil (= das zip Archiv) prüfen
                using (var rsa = new RSACryptoServiceProvider(2048))
                {
                    rsa.FromXmlString(rsaKey);
                    var result = rsa.VerifyData(fileContent, 0, contentLength, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                    if (result)
                    {
                        // Weitere Progeamminstallation
                        using (StreamReader reader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mainlysoft""mainlysoft_devassist")))
                        {
                            string InstallationsPfad = reader.ReadLine();
                            if (InstallationsPfad != "")
                            {
                                string[] files = Directory.GetFiles(InstallationsPfad);
                                foreach (var file in files)
                                    File.Delete(file);

                                MessageBox.Show("Hier");
                                ZipFile.ExtractToDirectory(LocalPath, InstallationsPfad);
                                File.Delete(LocalPath);
                                System.Diagnostics.Process.Start(Path.Combine(InstallationsPfad, "Entwicklungstool.exe"));
                                Application.Exit();
                            }
                            else
                            {
                                MessageBox.Show("Hier 3");

                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("Beschädigte oder fehlerhafte Signatur!\rDieser Ordner wurde nicht von dem Mainlysoft Team freigegeben. Es kann sich um Schadsoftware handeln.\rBitte löschen Sie diesen Ordner, oder wenden Sie sich an den Mainlysoft Support.""Signaturprüfung");

                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            InstallNewUpdate();
        }


Wenn ich das so jetzt ausführe, bekomme ich nur die Meldung "g", also gibt es irgendein Problem bei dem Download, oder?


Ralf Jansen - Do 12.01.17 17:30

Nein? Du hast den entsprechenenden MessageBox.Show() in einem ganz normalen Codepfad.

DownloadFile ist blockierend. Heißt die Methode kommt erst zurück wenn das File runtergeladen ist. Danach bekommst du dann dein "g". Completed wird nie aufgerufen weil da du ja nicht asynchron runterlädst (das wäre DownloadFileAsync). Ich sehe hier aber keine Notwendigkeit für Asynchronität. Macht es Sinn das Programm weiter benutzen zu können während dein Update runtergeladen wird?

Übrigens selbst wenn das asynchron wäre und Completed aufgerufen wird wäre deine Reihenfolge trotzdem falsch. Den Completed Event solltem man immer vor der eigentlichen Aktion verdrahten danach besteht immer die Möglichkeit das es zu spät ist. Aka der asynchrone Vorgang ist fertig bevor man den Event verdrahted hat und wurde bereits geworfen.


Csharp-programmierer - Do 12.01.17 19:50

Vielen Dank, jetzt wird der Code ausgeführt.
Aber in dem selben Code, wie gepostet, kommt die Fehlermeldung: Der Prozess kann nicht auf die Datei NewUpdate zugreifen, da sie von einem anderen Prozess verwendet wird. Woran kann das jetzt liegen?


Ralf Jansen - Do 12.01.17 20:12

Zitat:
Vielen Dank, jetzt wird der Code ausgeführt.
Aber in dem selben Code, wie gepostet, kommt die Fehlermeldung


Äh. Du hast nichts geändert aber jetzt gehts. Genau. Ich kann Code reparieren allein durch Fernkritik (bzw. anders kaputt machen). Bettet mich an :flehan:

Ich vermute mal du hast DownloadFile in DownloadFileAsync geändert. Das ist dann ein Problem mit der Implementierung im WebClient (um nicht zu sagen Bug).
Der Webclient hat weiterhin ein Handle auf das File offen. DownloadFileAsync schließt leider den FileStream nicht bevor er DownloadFileCompleted feuert.
Du solltest besser DownloadFile verwenden wenn du das File sofort im Anschluss nach dem Download öffnen willst. Wenn du es unbedingt asynchron brauchst musst du leider das asynchrone um Downloadfile selber kodieren.


Christian S. - Do 12.01.17 20:17

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
Vielen Dank, jetzt wird der Code ausgeführt.
Aber in dem selben Code, wie gepostet, kommt die Fehlermeldung


Äh. Du hast nichts geändert aber jetzt gehts. Genau. Ich kann Code reparieren allein durch Fernkritik (bzw. anders kaputt machen). Bettet mich an :flehan:
An was sollen wir Dich betten? :gruebel: "Ralf Jansen an Champignonrahm"? :D


Ralf Jansen - Do 12.01.17 20:22

Äh .... da hat die Rechtschreibprüfung versagt. Wen man sich mal auf die verlässt. Mann, Mann, Mann :oops:


jfheins - Do 12.01.17 20:37

Ich habe den Code mal etwas angepasst:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
    public void LoadAndInstallNewUpdate() // Besserer Name
    {
      try
      {
        WebClient client = new WebClient();
        Uri URL = new Uri(@"http://meineURL.com/meineDatei.zip");
        string LocalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");

        client.DownloadFile(URL, LocalPath);
        MessageBox.Show("g");
        // Nachfolgeoperationen einfach direkt aufrufen...
        // Alternativ korrekt asynchron und das Completed-Event möglichst früh zuweisen.
        if (HasValidSignature(LocalPath))
        {
          InstallUpdate(LocalPath);
        }        
        else
        {
          MessageBox.Show("Beschädigte oder fehlerhafte Signatur!\rDieser Ordner wurde nicht von dem Mainlysoft Team freigegeben. Es kann sich um Schadsoftware handeln.\rBitte löschen Sie diesen Ordner, oder wenden Sie sich an den Mainlysoft Support.""Signaturprüfung");
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }
    
    // Signaturprüfung als eigene Aufgabe, die mit einem Parameter auskommt und einen einfachen Rückgabewert hat :-)
    private bool HasValidSignature(string filename)
    {
      var fileContent = File.ReadAllBytes(filename);

      if (fileContent.Length < 260)
      {
        MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung"); // Je nach Anwendungsfall vll. eine Exception
        return false;
      }
      
      MessageBox.Show("Hier 1");

      byte[] signature = null;
      int signatureLength = 0;

      // Signatur auslesen, sie hängt hinten an der Datei
      using (var stream = new MemoryStream(fileContent))
      {
        using (var reader = new BinaryReader(stream))
        {
          stream.Seek(-4, SeekOrigin.End);
          signatureLength = reader.ReadInt32();
          if (signatureLength != 256)
          {
            MessageBox.Show("Fehlende Signatur oder unbekannter Algorithmus!""Signaturprüfung"); // Je nach Anwendungsfall vll. eine Exception
            return false;
          }
          else
          {
            stream.Seek(-4 - signatureLength, SeekOrigin.End);
            signature = reader.ReadBytes(signatureLength);
            MessageBox.Show("Hier 2");
          }
        }
      }
      
      var contentLength = fileContent.Length - signatureLength - 4;
      var rsaKey = File.ReadAllText(@"./public.xml");
      
      // Signatur gegen den vorhergehenden Teil der Datei (= das zip Archiv) prüfen
      using (var rsa = new RSACryptoServiceProvider(2048))
      {
        rsa.FromXmlString(rsaKey);
        return rsa.VerifyData(fileContent, 0, contentLength, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
      }
    }

    // Installationsvorgang trennen
    private void InstallUpdate(string filename)
    {
      // Weitere Programminstallation
      using (StreamReader reader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mainlysoft""mainlysoft_devassist")))
      {
        string InstallationsPfad = reader.ReadLine();
        if (InstallationsPfad != "")
        {
          string[] files = Directory.GetFiles(InstallationsPfad);
          foreach (var file in files)
            File.Delete(file);

          MessageBox.Show("Hier");
          ZipFile.ExtractToDirectory(filename, InstallationsPfad);
          File.Delete(filename);
          System.Diagnostics.Process.Start(Path.Combine(InstallationsPfad, "Entwicklungstool.exe"));
          Application.Exit();
        }
        else
        {
          MessageBox.Show("Hier 3");
        }
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      LoadAndInstallNewUpdate();
    }


Arbeitspakete am besten schön in Funktionen einpacken, damit bleibt es übersichtlicher und die vermeidest die Situation, dass du den Dateipfad x-Mal bestimmst. (Ich habe das jetzt in Naotepad angepasst, es könnte sein, dass ich noch eine Kleinigkeit vergessen hab...)

Und eben nach dem Downloadvorgang die selbst die Funktionen aufrufen.


Csharp-programmierer - Sa 14.01.17 10:29

So guten Morgen erstmal.
Soweit funktioniert erstmal alles, der ZIP wird heruntergeladen, die Signierung überprüft (wenn Falsch, wird gleich Alarm geschlagen), der ZIP wird extrahiert. Soweit so gut, aber wenn ich diesen Code ausführe, bekomme ich diese Fehlermeldungen nacheinander:

1. Das System kann die angegebene Datei nicht finden
2. Die Datei .../prog_version.txt ist bereits vorhanden
3: Die Datei .../prog_version.txt ist bereits vorhanden


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
public void LoadAndInstallNewUpdate()
        {
            try
            {
                WebClient client = new WebClient();
                Uri URL = new Uri(@"http://meineDaten.zip");
                string LocalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");

                client.DownloadFile(URL, LocalPath);

                if (HasValidSignature(LocalPath) == 1)
                {
                    InstallUpdate(LocalPath);
                }
                if (HasValidSignature(LocalPath) == 0)
                {
                    Form2 f = new Form2();
                    f.ShowDialog();

                    if (f.Action == 1)
                    {
                        File.Delete(LocalPath);
                        Application.Exit();
                    }
                }
                if (HasValidSignature(LocalPath) == 2)
                {
                    MessageBox.Show("Bei der Prüfung der Signatur ist ein unbekannter Fehler aufgetreten.\rBitte versuchen Sie es erneut.""unbekannter Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private int HasValidSignature(string filename)
        {
            try
            {
                var fileContent = File.ReadAllBytes(filename);

                if (fileContent.Length < 260)
                {
                    return 0;
                }

                byte[] signature = null;
                int signatureLength = 0;

                // Signatur auslesen, sie hängt hinten an der Datei
                using (var stream = new MemoryStream(fileContent))
                {
                    using (var reader = new BinaryReader(stream))
                    {
                        stream.Seek(-4, SeekOrigin.End);
                        signatureLength = reader.ReadInt32();
                        if (signatureLength != 256)
                        {
                            return 0;
                        }
                        else
                        {
                            stream.Seek(-4 - signatureLength, SeekOrigin.End);
                            signature = reader.ReadBytes(signatureLength);
                        }
                    }
                }

                var updatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Update");
                ZipFile.ExtractToDirectory(filename, updatePath);

                var contentLength = fileContent.Length - signatureLength - 4;
                var rsaKey = File.ReadAllText(Path.Combine(updatePath, "public.xml"));

                // Signatur gegen den vorhergehenden Teil der Datei (= das zip Archiv) prüfen
                using (var rsa = new RSACryptoServiceProvider(2048))
                {
                    rsa.FromXmlString(rsaKey);
                    if (rsa.VerifyData(fileContent, 0, contentLength, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1))
                        return 1;
                    else
                        return 0;
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
                return 2;
            }
        }
        private void InstallUpdate(string filename)
        {
            try
            {
                using (StreamReader reader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mainlysoft""mainlysoft_devassist.txt")))
                {
                    string InstallationsPfad = reader.ReadLine();
                    if (InstallationsPfad != "")
                    {
                        string[] files = Directory.GetFiles(InstallationsPfad);
                        foreach (var file in files)
                        {
                            MessageBox.Show(file);
                        }

                        MessageBox.Show(filename);
                        //File.Delete(filename);
                        System.Diagnostics.Process.Start(Path.Combine(InstallationsPfad, "Entwicklertool.exe"));
                        Application.Exit();
                    }
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            LoadAndInstallNewUpdate();
        }


Soo, ich extrahiere den ZIP ja nur, weil die eine Methode die public.xml Dateo einlesen muss. Ich habe auch mal geguckt, die Ordner wurden alle korrekt erstellt und haben den erwünschten Inhalt. Aber irgenwie finde ich den Fehler einfach nicht :gruebel:


jfheins - Sa 14.01.17 13:58

Zum Febugging: Es wäre viel hilfreicher, wenn du dazu sagen könntest, wo genau die Fehler auftreten.
Du hattest irgendwie sowas wie Exception-Panik, weil du überall try-catch Blöcke hinschreibst und die Message anzeigst. Das führt aber dazu, dass du nicht mehr sagen kannst, wo genau der Fehler auftritt. Wenn du mit dem Debugger (Visual Studio oben das grüne Play Symbol) startest, sollte er eigentlich genau dort anhalten wo der Fehler auftritt.

Dann weiter:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        private int HasValidSignature(string filename)
        {
            try
            {
                // Sehr viel Code
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
                return 2;
            }
        }

Das ist relativ sinnlos. Du ersetzt die informative Exception durch einen nichtssagen Fehlercode 2 und zeigst dem Benutzer eine Nachricht, mit der er vermutlich nichts anfangen kann. Aber das eigentlich sinnlose ist ja, dass um den Aufruf ebenfalls ein try-catch drum ist, was das gleiche macht! Also könnte man auch einfach die Exception hochlaufen lassen und nur in LoadAndInstallNewUpdate() behandeln.

Dann ganz wichtig:

C#-Quelltext
1:
2:
3:
4:
5:
                var updatePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Update");
                ZipFile.ExtractToDirectory(filename, updatePath);

                var contentLength = fileContent.Length - signatureLength - 4;
                var rsaKey = File.ReadAllText(Path.Combine(updatePath, "public.xml"));

Ich hab dir schon geschrieben, dass der public Key NICHT aus dem Update kommen darf. Aber du entzippt hier das Update um den Key zu lesen. Dann kannst du dir das ganze auch sparen.

Zu deinen Fehlern: Bitte führe dein Programm mal im Debugger aus und gehe Schritt für Schritt vor (mit F10 und F11) und finde heraus, an welcher Stelle der erste Fehler auftritt.

Es gab hier irgendwo auch mal ein Debugger-Tutorial, ich suche das mal...
Hab auf die Schnelle nur MSDN gefunden: https://msdn.microsoft.com/de-de/library/y740d9d3.aspx


Csharp-programmierer - Di 24.01.17 20:17

Soo vielen Dank Leute, jetzt funktioniert fast alles.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
public void LoadAndInstallNewUpdate()
        {
            using (StreamReader reader = new StreamReader(file))
            {
                x1 = reader.ReadLine();
                c2 = reader.ReadLine();
                c3 = reader.ReadLine();

                if (reader.ReadLine() == "update")
                {
                    WebClient client = new WebClient();
                    Uri URL = new Uri(@".......zip");
                    string LocalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "NewUpdate.zip");

                    client.DownloadFile(URL, LocalPath);
                    int HashValue = HasValidSignature(LocalPath);
                    MessageBox.Show("Hier 100000");

                    if (HashValue == 1)
                    {
                        MessageBox.Show("Begin installation");
                        InstallUpdate(LocalPath);
                    }
                    if (HashValue == 0)
                    {
                        Form2 f = new Form2();
                        f.ShowDialog();

                        if (f.Action == 1)
                        {
                            File.Delete(LocalPath);
                            Application.Exit();
                        }
                    }
                    if (HasValidSignature(LocalPath) == 2)
                    {
                        MessageBox.Show("Bei der Prüfung der Signatur ist ein unbekannter Fehler aufgetreten.\rBitte versuchen Sie es erneut.""unbekannter Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
                    }
                }
            }

        }
        private int HasValidSignature(string filename)
        {

            var fileContent = File.ReadAllBytes(filename);

            if (fileContent.Length < 260)
            {
                return 0;
            }

            byte[] signature = null;
            int signatureLength = 0;

            // Signatur auslesen, sie hängt hinten an der Datei
            using (var stream = new MemoryStream(fileContent))
            {
                using (var reader = new BinaryReader(stream))
                {
                    stream.Seek(-4, SeekOrigin.End);
                    signatureLength = reader.ReadInt32();
                    if (signatureLength != 256)
                    {
                        return 0;
                    }
                    else
                    {
                        stream.Seek(-4 - signatureLength, SeekOrigin.End);
                        signature = reader.ReadBytes(signatureLength);
                    }
                }
            }

            var contentLength = fileContent.Length - signatureLength - 4;
            var rsaKey = "...";

            // Signatur gegen den vorhergehenden Teil der Datei (= das zip Archiv) prüfen
            using (var rsa = new RSACryptoServiceProvider(2048))
            {
                rsa.FromXmlString(rsaKey);
                if (rsa.VerifyData(fileContent, 0, contentLength, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1))
                    return 1;
                else
                    return 0;
            }

        }
        private void InstallUpdate(string filename)
        {
            InstallationsPfad = "....";
            if (InstallationsPfad != x1)
            {
                // Alte Programmdaten löschen
                // Installationspfad: Dokumente -> Mainlysoft Devassist -> Programm DLL's, .exe's ...
                DeleteFiles(InstallationsPfad);

                // Neue Programmdaten aufspielen
                Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MSUpd"));
                ZipFile.ExtractToDirectory(filename, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MSUpd"));
                DirectoryCopy(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MSUpd"), InstallationsPfad, true);

                // Installationsordner löschen
                if (File.Exists(filename))
                {
                    File.Delete(filename);
                    MessageBox.Show("Hier 3");
                }

                MessageBox.Show("HIer 1234");
            }

            using (StreamWriter writer = new StreamWriter(file))
            {
                writer.WriteLine(InstallationsPfad);
                writer.WriteLine(x1);
                writer.WriteLine(c2);
                writer.WriteLine("update installed");
                writer.Close();
            }

            System.Diagnostics.Process.Start(...);
            Application.Exit();


An sich funktioniert alles, die Daten werden transferiert, die Text Datei wird umschrieben und das Hauptprogramm wird gestartet. Wenn das Hauptprogramm startet, stürzt es nach 1s wieder ab und bei dem Updater kommt ein Fehler, genau in dieser Zeile: var fileContent = File.ReadAllBytes(filename);

Das ist ja logisch, weil der ZIP ja gelöscht wurde, aber was ich jetzt nicht verstehe ist, warum er den Codeblock nochmal ausführt :(


jfheins - Mi 25.01.17 00:27

In Zeile 35: if (HasValidSignature(LocalPath) == 2)

Zu der Frage, woher der Aufruf kommt, kannst du dir den Call Stack angucken: https://msdn.microsoft.com/en-us/library/a3694ts5.aspx


Csharp-programmierer - Mi 25.01.17 19:00

Vielen Dank, an sich funktioniert die Installation an sich jetzt problemfrei. Jetzt habe ich eine Datei (.txt) die alle wichtigen Daten beinhaltet. Wenn das Update installiert wurde, muss eine Zeile in der Datei geändert werden. Aber bis jetzt funktioniert es nicht. Erst werden die Daten gelesen, in den Zwischenspeicher geholt und dann muss der StreamReader diese Daten wieder in die Datei schreiben und die letzte Zeile ändern. Wenn ich das Programm so ausführe, dass werden die Werte einfach an die letzte Zeile dran gehängt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
string x1 = streamReader.ReadLine();
string x2 = streamReader.ReadLine();

//.....

streamWriter.Write("");
streamWriter.WriteLine(x1);
streamWriter.WriteLine(x2);


Also muss ich iwie die Datei leer bekommen, damit der StreamReader von vorne anfangen kann. Wie mache ich das?


Ralf Jansen - Mi 25.01.17 19:23

Habe ich nicht ganz verstanden. Dem StreamWriter kannst du mitgeben ob er anhängen oder überschreiben soll.


Csharp-programmierer - Mi 25.01.17 21:31

Ich lasse den StreamWriter über einen FileStrem laufen und deshalb akzeptiert er keinen Boolean :(
Gibt es noch eine Möglichkeit, dass der StreamWriter eine Datei überschreibt und trotzdem einen FileStream benutzt?


jfheins - Mi 25.01.17 21:40

Du kannst den (zugrundeliegenden) FileStream wieder auf Anfang setzen: stream.Seek(0, SeekOrigin.Begin);

https://msdn.microsoft.com/de-de/library/system.io.filestream.seek(v=vs.110).aspx


Csharp-programmierer - Do 26.01.17 22:01

Soo vielen Dank, jetzt funktioniert wirklich alles.
Auf meinem Entwicklungs PC funktioniert alles, aber wenn ich das Programm auf meinem Laptop ausführe, bekomme ich von dem Update diesen Fehler: Der Typ "System.Security.Ceyptography.HashAlgorithmName" In der Assembly "mscorlib...." konnte nicht gefunden werden. Wieso kommt jetzt dieser Fehler? :eyes:


Delete - Do 26.01.17 23:09

- Nachträglich durch die Entwickler-Ecke gelöscht -


jfheins - Fr 27.01.17 00:39

Das Problem ist vermutlich, dass dein Programm .net in Version 4.6 voraussetzt. (Du hast ja das Zielframework eingestellt) Das heißt, es läuft auch unter 4.5 (was vermutlich installiert ist) aber du verwendest den Methodenaufruf

C#-Quelltext
1:
RSACryptoServiceProvider.SignData(Byte[],&#8194;HashAlgorithmName, RSASignaturePadding)                    

den es so erst in Version 4.6 gibt. Der Typ HashAlgorithmName kann deshalb nicht in der Assembly gefunden werden.

Die möglichen Lösungen sind (hoffentlich) logisch:
1. Auf den Zielrechner das .net Framework 4.6 installieren
2. Das Zielframework wieder auf 4.5 stellen und alle Aufrufe entsprechend anpassen um andere Methoden zu benutzen. Zum Beispiel dieser Aufruf:
RSACryptoServiceProvider.SignData(Byte[],&#8194;Object) ist ab .net 1.1 drin. Ist aber halt nicht so schon typsicher, Verwendungsbeispiel gibt es beim msdn: https://msdn.microsoft.com/en-us/library/9tsc5d0z(v=vs.110).aspx
Entsprechend dann mit SHA256CryptoServiceProvider() als zweiten Parameter.