Autor Beitrag
doublecross
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 149
Erhaltene Danke: 27

Windows 7
C#; Visual Studio 2015
BeitragVerfasst: Di 28.01.20 13:58 
Hallo,

ich beschäftige mich gerade mit dem Diffie-Hellman Schlüsseltausch unter C# und irgendwie macht es bei mir nicht wirklich klick.
Generell bietet Microsoft ja schon nette Werkzeuge und so habe ich mich an diesem Beispiel orientiert um einmal eine solche Kommunikation zu simulieren (siehe unten).

Hierbei erschließt sich mir aber nicht, warum der "Man in the Middle" (Eve) sich nicht auch den "Secret Key" errechnen kann, wenn sie doch den Austausch des public Keys mitliest.

Könnte mich hier bitte jemand erleuchten?


Beispielprogramm:
Dieses Beispiel findet sich auch als ZIPim Anhang. Im wesentlichen besteht es aber auch diesen drei Dateien:

DiffieHellman.cs
Die eigentliche "Verschlüsselungsklasse" deren wesentliche Bestandteile dem oben verlinkten Beispiel entliehen sind, von mir aber in kleinere Funktionen zerlegt wurde, damit ich es besser verstehe ;).

ausblenden volle Höhe 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:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace DiffieHellmanTest
{
    class DiffieHellman : IDisposable
    {

        public DiffieHellman() : base()
        {
            generatePunblicKey();
        }

        private ECDiffieHellmanCng dhCng = new ECDiffieHellmanCng();

        protected void generatePunblicKey()
        {
            dhCng.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            dhCng.HashAlgorithm = CngAlgorithm.Sha256;
            PublicKey = dhCng.PublicKey.ToByteArray();
        }

        protected void generateDerivedKey(byte[] PartnerPublicKey)
        {
            secretKey = dhCng.DeriveKeyMaterial(CngKey.Import(PartnerPublicKey, CngKeyBlobFormat.EccPublicBlob));
        }

        public byte[] PublicKey = null;

        public byte[] secretKey = null;

        protected byte[] partnerPublicKey = null;

        public void Encrypt(byte[] key, string secretMessage, out byte[] encryptedMessage, out byte[] iv)
        {
            using (Aes aes = new AesCryptoServiceProvider())
            {
                aes.Key = key;
                iv = aes.IV;

                // Encrypt the message
                using (MemoryStream ciphertext = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ciphertext, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        byte[] plaintextMessage = Encoding.UTF8.GetBytes(secretMessage);
                        cs.Write(plaintextMessage, 0, plaintextMessage.Length);
                        cs.Close();
                        encryptedMessage = ciphertext.ToArray();
                    }
                }
            }
        }

        public string Decrypt(byte[] encryptedMessage, byte[] iv)
        {

            using (Aes aes = new AesCryptoServiceProvider())
            {
                aes.Key = secretKey;
                aes.IV = iv;
                // Decrypt the message
                using (MemoryStream plaintext = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(plaintext, aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(encryptedMessage, 0, encryptedMessage.Length);
                        cs.Close();
                        string message = Encoding.UTF8.GetString(plaintext.ToArray());
                        return message;
                    }
                }
            }
        }

        public void SetPartnerPublicKey(byte[] PartnerPublicKey)
        {
            partnerPublicKey = PartnerPublicKey;
            generateDerivedKey(partnerPublicKey);
        }

        public void Dispose()
        {
            if (dhCng != null)
            {
                dhCng.Dispose();
            }
        }
    }
}


MainWindow.xaml

Eine kleine Oberfäche, welcher mir links und Rechts die Gesprächspartner anzeigt und in der Mitte den "Man in the Middle". Die ersten beiden Knöpfe sollten von links nach rechts betätigt werden, danach können Nachrichten hin und her geschickt werden.

ausblenden volle Höhe XML-Daten
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:
<Window x:Class="DiffieHellmanTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DiffieHellmanTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowState="Maximized">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="30" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="30" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <!-- Alice -->
        <GroupBox Grid.Row="0" Grid.Column="0" Margin="3" Header=" Alice ">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <ListBox x:Name="AliceLog" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5"/>
                <Label Grid.Row="1" Grid.Column="0">Public Key</Label>
                <TextBlock x:Name="AlicePK" Grid.Row="1" Grid.Column="1" Margin="3" TextWrapping="Wrap" FontFamily="Consolas"/>

                <Label Grid.Row="2" Grid.Column="0">Secret Key</Label>
                <TextBlock x:Name="AliceCK" Grid.Row="2" Grid.Column="1" Margin="3" TextWrapping="Wrap" FontFamily="Consolas"/>
                
            </Grid>
        </GroupBox>

        <!-- Eve -->
        <GroupBox Grid.Row="0" Grid.Column="2" Margin="3" Header=" Eve ">
            <ListBox x:Name="EveLog" Margin="5"/>
        </GroupBox>

        <!-- Bob -->
        <GroupBox Grid.Row="0" Grid.Column="4" Margin="3" Header=" Bob ">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <ListBox x:Name="BobLog" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5"/>
                <Label Grid.Row="1" Grid.Column="0">Public Key</Label>
                <TextBlock x:Name="BobPK" Grid.Row="1" Grid.Column="1" Margin="3" TextWrapping="Wrap" FontFamily="Consolas" />

                <Label Grid.Row="2" Grid.Column="0">Secret Key</Label>
                <TextBlock x:Name="BobCK" Grid.Row="2" Grid.Column="1" Margin="3" TextWrapping="Wrap" FontFamily="Consolas" />

            </Grid>
        </GroupBox>

        <WrapPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="5" Margin="5">
            <Button Margin="5" Padding="5,3" Click="Button_Click">Initialisieren</Button>
            <Button Margin="5" Padding="5,3" Click="Button_Click_1">Schlüsseltausch</Button>
            <StackPanel Orientation="Horizontal" Margin="15,0">
                <TextBox x:Name="AlliceText" Width="120" Margin="3" VerticalContentAlignment="Center" />
                <Button Margin="3" Padding="5,3" Click="Button_Click_2">Sende Alice</Button>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="15,0">
                <TextBox x:Name="BobText" Width="120" Margin="3" VerticalContentAlignment="Center" />
                <Button Margin="3" Padding="5,3" Click="Button_Click_3">Sende Bob</Button>
            </StackPanel>
        </WrapPanel>
    </Grid>
</Window>


MainWindow.xaml.cs
Die Logik hinter dem Form. Hier wird je ein Alice und ein Bob Objekt erstellt und alles diese aneinander schicken müssten, wenn man nicht direkt zuweisen könnte wird durch die Funktion LogCommunication() geschickt.

ausblenden volle Höhe 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:
using System.Text;
using System.Windows;

namespace DiffieHellmanTest
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private DiffieHellman Alice = null;
        private DiffieHellman Bob = null;

        public string asString(byte[] Value)
        {
            StringBuilder Result = new StringBuilder();
            foreach (byte Byte in Value)
            {
                Result.AppendFormat("{0:X2} ", Byte);
            }

            return Result.ToString();
        }

        public void LogCommunication(string From, string To, string Message, string Hint)
        {
            EveLog.Items.Add(string.Format("{0} ==> {1} ({3})\n  {2}", From, To, Message, Hint));
        }

        public void LogAlice(string Message)
        {
            AliceLog.Items.Add(string.Format("{0}", Message));
        }
        public void LogBob(string Message)
        {
            BobLog.Items.Add(string.Format("{0}", Message));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Alice = new DiffieHellman();
            AlicePK.Text = asString(Alice.PublicKey);
            Bob = new DiffieHellman();
            BobPK.Text = asString(Bob.PublicKey);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            Bob.SetPartnerPublicKey(Alice.PublicKey);
            LogCommunication("Alice""Bob", asString(Alice.PublicKey), "public Key Alice");
            LogBob("From Alice: " + asString(Alice.PublicKey));
            BobCK.Text = asString(Bob.secretKey);

            Alice.SetPartnerPublicKey(Bob.PublicKey);
            LogCommunication("Bob""Alice", asString(Bob.PublicKey), "public Key Bob");
            LogAlice("From Bob: " + asString(Bob.PublicKey));
            AliceCK.Text = asString(Alice.secretKey);
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            // Sende Alice
            string Message = AlliceText.Text;
            if (!string.IsNullOrWhiteSpace(Message))
            {
                byte[] encryptedMessage;
                byte[] iv;
                Alice.Encrypt(Alice.secretKey, Message, out encryptedMessage, out iv);
                LogAlice(string.Format("Send to Bob: {0} => {1}", Message, Encoding.UTF8.GetString(encryptedMessage)));
                LogCommunication("Alice""Bob", Encoding.UTF8.GetString(encryptedMessage), "encrypted Message");
                LogCommunication("Alice""Bob", Encoding.UTF8.GetString(iv), "Initialization Vector");

                string DecryptedMessage = Bob.Decrypt(encryptedMessage, iv);
                LogBob(string.Format("From Alice: {0} => {1}", Encoding.UTF8.GetString(encryptedMessage), DecryptedMessage));
            }
        }

        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            // Sende Bob
            string Message = BobText.Text;
            if (!string.IsNullOrWhiteSpace(Message))
            {
                byte[] encryptedMessage;
                byte[] iv;
                Bob.Encrypt(Bob.secretKey, Message, out encryptedMessage, out iv);
                LogBob(string.Format("Send to Alice: {0} => {1}", Message, Encoding.UTF8.GetString(encryptedMessage)));
                LogCommunication("Bob""Alice", Encoding.UTF8.GetString(encryptedMessage), "encrypted Message");
                LogCommunication("Bob""Alice", Encoding.UTF8.GetString(iv), "Initialization Vector");

                string DecryptedMessage = Alice.Decrypt(encryptedMessage, iv);
                LogAlice(string.Format("From Bob: {0} => {1}", Encoding.UTF8.GetString(encryptedMessage), DecryptedMessage));
            }
        }
    }
}
Einloggen, um Attachments anzusehen!
FinnO
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1331
Erhaltene Danke: 123

Mac OSX, Arch
TypeScript (Webstorm), Kotlin, Clojure (IDEA), Golang (VSCode)
BeitragVerfasst: Mi 29.01.20 00:18 
Moin,

ich finde der Wikipedia-Artikel zu Diffie-Hellmann erklärt eigentlich ganz gut, wie der Austausch funktioniert, und wieso der Algorithmus sicher ist.

Der Trick liegt darin, eine sog. Einweg-Funktion zu finden. Das ist eine Funktion, die in eine Richtung sehr einfach berechnet werden kann, in umgekehrte Richtung aber sehr schwierig ist.

Im Wikipedia-Beispiel wird hier die modular exponentiation genutzt:

Gegeben zwei öffentliche Parameter, die base g = 5 und den modulo p = 23, sowie den secret key a = 4, kann Alice sehr einfach berechnen:
ausblenden Quelltext
1:
A = g^a mod p = 5^4 mod 23 = 4					


Der man in the middle Eve kennt aber nur die öffentlichen Parameter g = 5, p = 23, sowie das Ergebnis A = 4. Eve müsste also folgenden Gleichung lösen:

ausblenden Quelltext
1:
4 = 5^a mod 23					


Und das ist schwer (siehe erneut Wikipedia). In diesem Beispiel kann man natürlich relativ schnell durchprobieren, da durch den modulo 23 die Anzahl der möglichen Ergebnisse stark beschränkt ist. Aus diesem Grund wird in der Realität p sehr groß gewählt.


Ich hoffe, dass das deine Frage einigermaßen erklärt hat.

Gruß

Für diesen Beitrag haben gedankt: Narses