Initial commit for PGP key import. OUT-428
authorThomas
Tue, 10 Apr 2018 10:50:14 +0200
branchOUT-428
changeset 2102 3939ad287c0c
parent 2101 de0d6260331f
child 2105 ecd44ebb48e0
Initial commit for PGP key import. - Add HandshakeItemsControl element - Add wizard - Reorder some code to create handshake dialogs - Add strings for new dialog - Add sending of initial key import message - Add handling of received key import messages
MsgProcessor.cs
Properties/Resources.Designer.cs
Properties/Resources.de.resx
Properties/Resources.resx
ThisAddIn.cs
UI/HandshakeDialog.xaml
UI/HandshakeDialog.xaml.cs
UI/HandshakeItem.cs
UI/HandshakeItemsControl.xaml
UI/HandshakeItemsControl.xaml.cs
UI/KeySyncWizard.xaml
UI/KeySyncWizard.xaml.cs
UI/RibbonCustomizations.cs
UI/RibbonCustomizationsExplorer.xml
UI/ValueConverters.cs
pEpForOutlook.csproj
--- a/MsgProcessor.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/MsgProcessor.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -21,6 +21,12 @@
 
         private BackgroundWorker backgroundProcessor;
 
+        /// <summary>
+        /// Event handler for when a sync message is being detected during decryption.
+        /// </summary>
+        public delegate void SyncMessageReceivedHandler(object sender, SyncMessageEventArgs e);
+        public static event SyncMessageReceivedHandler SyncMessageReceived;
+
         /**************************************************************
          * 
          * Constructors/Destructors
@@ -945,6 +951,45 @@
                 // Copy over lost properties
                 destMessage.SetNonEnginePropertiesFrom(sourceMessage);
                 keyList = dstKeyList;
+
+                // Check if key sync wizard is open
+                if (UI.KeySyncWizard.WizardInProcess)                    
+                {
+                    Log.Verbose("Decrypt: Message decrypted while key sync wizard is open.");
+
+                    // Check if formal requirements are met
+                    if ((destMessage.To.Count == 1) &&
+                        (destMessage.To[0].Address.Equals(destMessage.From.Address)))
+                    {
+                        Log.Verbose("Decrypt: Message to myself");
+
+                        // Detect message type
+                        if ((destMessage.Rating == pEpRating.pEpRatingReliable) &&
+                            (flags.HasFlag(pEpDecryptFlags.pEpDecryptFlagOwnPrivateKey) == false))
+                        {
+                            // Message with public key
+                            Log.Verbose("Decrypt: Message rating is reliable and has no private key import flag. Assuming message with public key.");
+                            MsgProcessor.SyncMessageReceived?.Invoke(this, new SyncMessageEventArgs(destMessage, SyncMessageEventArgs.MessageTypes.PublicKey));
+                        }
+                        else if ((destMessage.Rating > pEpRating.pEpRatingReliable) &&
+                                 (flags.HasFlag(pEpDecryptFlags.pEpDecryptFlagOwnPrivateKey)))
+                        {
+                            // Message with private key
+                            Log.Verbose("Decrypt: Message rating is trusted and has private key attached. Assuming message with private key.");
+                            MsgProcessor.SyncMessageReceived?.Invoke(this, new SyncMessageEventArgs(destMessage, SyncMessageEventArgs.MessageTypes.PrivateKey));
+                        }
+                        else
+                        {
+                            // Message type cannot be determined
+                            Log.Verbose("Decrypt: No known message type. Rating is " + Enum.GetName(typeof(pEpRating), destMessage.Rating) + ". Has private key flag == " + flags.HasFlag(pEpDecryptFlags.pEpDecryptFlagOwnPrivateKey).ToString());
+                        }
+                    }
+                    else
+                    {
+                        // Message doesn't match formal criteria
+                        Log.Verbose("Decrypt: Message not processed as keysync wizard message. Recipients count or addresses do not match.");
+                    }
+                }
             }
             else
             {
@@ -1111,5 +1156,36 @@
                 this.ProcessedStatus = Globals.ReturnStatus.Success;
             }
         }
+
+        /// <summary>
+        /// Class used to store the arguments in the SyncMessageReceived event.
+        /// </summary>
+        internal class SyncMessageEventArgs : EventArgs
+        {
+            /// <summary>
+            /// The message types for sync messages.
+            /// </summary>
+            public enum MessageTypes
+            {
+                Undefined,
+                PublicKey,
+                PrivateKey
+            }
+
+            public PEPMessage Message = null;
+            public MessageTypes MessageType = SyncMessageEventArgs.MessageTypes.Undefined;
+
+            /// <summary>
+            /// Constructs a new SyncMessageEventArgs with the given arguments.
+            /// </summary>
+            /// <param name="message">The message that has been received.</param>
+            /// <param name="messageType">The message type of the received message.</param>
+            public SyncMessageEventArgs(PEPMessage message,
+                                        MessageTypes messageType)
+            {
+                this.Message = message;
+                this.MessageType = messageType;
+            }
+        }
     }
 }
--- a/Properties/Resources.Designer.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/Properties/Resources.Designer.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -691,6 +691,95 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to Finish.
+        /// </summary>
+        public static string KeySyncWizard_Finish {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Finish", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Next.
+        /// </summary>
+        public static string KeySyncWizard_Next {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Next", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Step 1/4
+        ///
+        ///A message with your public key has been sent to yourself.
+        ///
+        ///Please import it on your PGP device and reply with an encrypted message where you attach your PGP key and encrypt using the newly imported p≡p key..
+        /// </summary>
+        public static string KeySyncWizard_Step1PGPExplanationText {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Step1PGPExplanationText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Schritt 2/4
+        ///
+        ///Please confirm the following PGP fingerprint with the one of your private key on your PGP device..
+        /// </summary>
+        public static string KeySyncWizard_Step2PGPExplanationText {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Step2PGPExplanationText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Step 3/4
+        ///
+        ///Please attach your private key from your PGP device to a message and send it encrypted to yourself using again the public key from p≡p for encryption.
+        ///.
+        /// </summary>
+        public static string KeySyncWizard_Step3PGPExplanationText {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Step3PGPExplanationText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Step 4/4
+        ///
+        ///Error
+        ///
+        ///An error occured and your key could not be imported. Please try again..
+        /// </summary>
+        public static string KeySyncWizard_Step4ErrorExplanationText {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Step4ErrorExplanationText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Step 4/4
+        ///
+        ///Key successfully imported
+        ///
+        ///Your private PGP key has been successfully imported and will now be used as default p≡p key..
+        /// </summary>
+        public static string KeySyncWizard_Step4SuccessExplanationText {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_Step4SuccessExplanationText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Key Import.
+        /// </summary>
+        public static string KeySyncWizard_WindowTitlePGP {
+            get {
+                return ResourceManager.GetString("KeySyncWizard_WindowTitlePGP", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to Catalan.
         /// </summary>
         public static string Language_Catalan {
@@ -1852,6 +1941,15 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to Key Import....
+        /// </summary>
+        public static string Ribbon_ButtonSyncLabel {
+            get {
+                return ResourceManager.GetString("Ribbon_ButtonSyncLabel", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to Enable protection.
         /// </summary>
         public static string Ribbon_EnableProtection {
@@ -1942,6 +2040,24 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to Import private keys from other devices.
+        /// </summary>
+        public static string Ribbon_GroupSyncHelperText {
+            get {
+                return ResourceManager.GetString("Ribbon_GroupSyncHelperText", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Key Import.
+        /// </summary>
+        public static string Ribbon_GroupSyncLabel {
+            get {
+                return ResourceManager.GetString("Ribbon_GroupSyncLabel", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to Store Protected.
         /// </summary>
         public static string Ribbon_NeverUnsecure {
--- a/Properties/Resources.de.resx	Wed Apr 04 10:03:36 2018 +0200
+++ b/Properties/Resources.de.resx	Tue Apr 10 10:50:14 2018 +0200
@@ -711,4 +711,53 @@
   <data name="PreviewMessage_RemoteImagesWarningText" xml:space="preserve">
     <value>Klicken Sie hier, um Bilder herunterzuladen. Um den Datenschutz zu erhöhen, hat Outlook den automatischen Download von Bildern in dieser Nachricht verhindert.</value>
   </data>
+  <data name="KeySyncWizard_Next" xml:space="preserve">
+    <value>Weiter</value>
+  </data>
+  <data name="KeySyncWizard_Finish" xml:space="preserve">
+    <value>Fertig stellen</value>
+  </data>
+  <data name="KeySyncWizard_Step1PGPExplanationText" xml:space="preserve">
+    <value>Schritt 1/4
+
+Eine Nachricht mit Ihrem öffentlichen Schlüssel wurde an Sie selbst versendet.
+
+Bitte importieren Sie diesen auf Ihrem PGP-Gerät und schicken Sie anschließend eine Antwortnachricht an sich selbst, an die Sie Ihren öffentlichen PGP-Schlüssel hängen und die Sie mit dem öffentlichen p≡p-Schlüssel verschlüsseln.</value>
+  </data>
+  <data name="KeySyncWizard_Step2PGPExplanationText" xml:space="preserve">
+    <value>Schritt 2/4
+
+Bitte vergleichen Sie den folgenden PGP-Fingerabdruck mit demjenigen Ihres privaten Schlüssels auf Ihrem PGP-Gerät:</value>
+  </data>
+  <data name="KeySyncWizard_Step3PGPExplanationText" xml:space="preserve">
+    <value>Schritt 3/4
+
+Bitte hängen Sie nun Ihren privaten PGP-Schlüssel an eine weitere verschlüsselte Nachricht von Ihrem PGP-Gerät an sich selbst und verschlüsseln Sie diese erneut mit dem soeben importierten, öffentlichen p≡p-Schlüssel.</value>
+  </data>
+  <data name="KeySyncWizard_Step4SuccessExplanationText" xml:space="preserve">
+    <value>Schritt 4/4
+
+Schlüsselimport erfolgreich
+
+Ihr privater PGP-Schlüssel wurde erfolgreich importiert und wird nun standardmäßig auch als p≡p-Schlüssel verwendet.</value>
+  </data>
+  <data name="KeySyncWizard_Step4ErrorExplanationText" xml:space="preserve">
+    <value>Schritt 4/4
+
+Fehler
+
+Es ist ein Fehler aufgetreten und Ihr Schlüssel konnte nicht importiert werden. Bitte versuchen Sie es erneut.</value>
+  </data>
+  <data name="KeySyncWizard_WindowTitlePGP" xml:space="preserve">
+    <value>Schlüsselimport</value>
+  </data>
+  <data name="Ribbon_GroupSyncHelperText" xml:space="preserve">
+    <value>Private Schlüssel von anderen Geräten importieren</value>
+  </data>
+  <data name="Ribbon_GroupSyncLabel" xml:space="preserve">
+    <value>Schlüsselimport</value>
+  </data>
+  <data name="Ribbon_ButtonSyncLabel" xml:space="preserve">
+    <value>Schlüsselimport...</value>
+  </data>
 </root>
\ No newline at end of file
--- a/Properties/Resources.resx	Wed Apr 04 10:03:36 2018 +0200
+++ b/Properties/Resources.resx	Tue Apr 10 10:50:14 2018 +0200
@@ -795,4 +795,54 @@
   <data name="PreviewMessage_RemoteImagesWarningText" xml:space="preserve">
     <value>Click here to download pictures. To help to protect your privacy, Outlook prevented automatic download of some pictures in this message.</value>
   </data>
+  <data name="KeySyncWizard_Next" xml:space="preserve">
+    <value>Next</value>
+  </data>
+  <data name="KeySyncWizard_Finish" xml:space="preserve">
+    <value>Finish</value>
+  </data>
+  <data name="KeySyncWizard_Step1PGPExplanationText" xml:space="preserve">
+    <value>Step 1/4
+
+A message with your public key has been sent to yourself.
+
+Please import it on your PGP device and reply with an encrypted message where you attach your PGP key and encrypt using the newly imported p≡p key.</value>
+  </data>
+  <data name="KeySyncWizard_Step2PGPExplanationText" xml:space="preserve">
+    <value>Schritt 2/4
+
+Please confirm the following PGP fingerprint with the one of your private key on your PGP device.</value>
+  </data>
+  <data name="KeySyncWizard_Step3PGPExplanationText" xml:space="preserve">
+    <value>Step 3/4
+
+Please attach your private key from your PGP device to a message and send it encrypted to yourself using again the public key from p≡p for encryption.
+</value>
+  </data>
+  <data name="KeySyncWizard_Step4SuccessExplanationText" xml:space="preserve">
+    <value>Step 4/4
+
+Key successfully imported
+
+Your private PGP key has been successfully imported and will now be used as default p≡p key.</value>
+  </data>
+  <data name="KeySyncWizard_Step4ErrorExplanationText" xml:space="preserve">
+    <value>Step 4/4
+
+Error
+
+An error occured and your key could not be imported. Please try again.</value>
+  </data>
+  <data name="KeySyncWizard_WindowTitlePGP" xml:space="preserve">
+    <value>Key Import</value>
+  </data>
+  <data name="Ribbon_GroupSyncHelperText" xml:space="preserve">
+    <value>Import private keys from other devices</value>
+  </data>
+  <data name="Ribbon_GroupSyncLabel" xml:space="preserve">
+    <value>Key Import</value>
+  </data>
+  <data name="Ribbon_ButtonSyncLabel" xml:space="preserve">
+    <value>Key Import...</value>
+  </data>
 </root>
\ No newline at end of file
--- a/ThisAddIn.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/ThisAddIn.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -720,6 +720,13 @@
                     // Do not allow TNEF/RTF format with 'winmail.dat' attachment
                     MapiHelper.SetProperty(newItem, MapiProperty.PidLidUseTnef, false);
 
+                    // If ForceUnencrypted property is set, add it to mail item
+                    if (message.ForceUnencrypted)
+                    {
+                        newItem.SetPEPProperty(MailItemExtensions.PEPProperty.ForceUnencrypted, true);
+                        newItem.Save();
+                    }
+
                     /* Send
                      * 
                      * Note: For ActiveSync accounts, the DeleteAfterSubmit property is ignored.
--- a/UI/HandshakeDialog.xaml	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/HandshakeDialog.xaml	Tue Apr 10 10:50:14 2018 +0200
@@ -13,6 +13,7 @@
         Width="Auto"
         ResizeMode="NoResize"
         SizeToContent="WidthAndHeight"
+        Topmost="True"
         Background="{x:Static SystemColors.MenuBarBrush}"
         Icon="pack://application:,,,/pEp;component/Resources/ImageLogoIcon.png"
         WindowStartupLocation="CenterScreen">
@@ -20,250 +21,6 @@
         <ResourceDictionary>
             <!-- Converters -->
             <BooleanToVisibilityConverter x:Key="BoolToVisibility" />
-            <local:IsEnabledToColorConverter x:Key="IsEnabledToColor" />
-            <local:IsActiveTabToBoolConverter x:Key="IsActiveTabToBool" />
-            <local:ValueConverterGroup x:Key="IsActiveTabToVisibility">
-                <local:IsActiveTabToBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-            <local:ValueConverterGroup x:Key="IsActiveTabToBackground">
-                <local:IsActiveTabToBoolConverter />
-                <local:BooleanToBackgroundConverter />
-            </local:ValueConverterGroup>
-            <local:InvertBoolConverter x:Key="InvertBool" />
-            <local:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibility" />
-            <local:ValueConverterGroup x:Key="InvertBoolToVisibility">
-                <local:InvertBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-            <local:ValueConverterGroup x:Key="IsStandardModeToVisibility">
-                <local:IsStandardModeToBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-            <local:ValueConverterGroup x:Key="IsNotStandardModeToVisibility">
-                <local:IsStandardModeToBoolConverter />
-                <local:InvertBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-            <local:ValueConverterGroup x:Key="IsStringEmptyToVisibility">
-                <local:IsStringEmptyConverter />
-                <local:InvertBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-            <local:ValueConverterGroup x:Key="IsNotSyncModeToVisibility">
-                <local:IsSyncModeToBoolConverter />
-                <local:InvertBoolConverter />
-                <BooleanToVisibilityConverter />
-            </local:ValueConverterGroup>
-
-            <!--Template for identities-->
-            <DataTemplate x:Key="HandshakeItemsTemplate">
-                <StackPanel>
-                    <StackPanel.Style>
-                        <Style TargetType="StackPanel">
-                            <Setter Property="Background"
-                                    Value="Transparent" />
-                            <Style.Triggers>
-                                <MultiDataTrigger>
-                                    <MultiDataTrigger.Conditions>
-                                        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
-                                                   Value="True" />
-                                        <Condition Binding="{Binding Path=IsExpanded}"
-                                                   Value="False" />
-                                        <Condition Binding="{Binding Path=IsButtonVisible}"
-                                                   Value="False" />
-                                        <Condition Binding="{Binding Path=IsClickable}"
-                                                   Value="True" />
-                                    </MultiDataTrigger.Conditions>
-                                    <Setter Property="Background"
-                                            Value="{x:Static SystemColors.ControlLightBrush}" />
-                                </MultiDataTrigger>
-                            </Style.Triggers>
-                        </Style>
-                    </StackPanel.Style>
-
-                    <!--Separator between items-->
-                    <Separator Margin="5,5,5,0"
-                               Background="LightGray"
-                               Visibility="{Binding Path=IsSeparatorVisible, Converter={StaticResource BoolToVisibility}}" />
-
-                    <!--The list entry-->
-                    <Grid Name="IdentityGrid"
-                          Background="Transparent"
-                          Margin="5"
-                          MinHeight="38"
-                          MouseLeftButtonUp="IdentityGrid_MouseLeftButtonUp"
-                          Visibility="{Binding Path=Mode, Converter={StaticResource IsNotSyncModeToVisibility}}">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="Auto" />
-                            <ColumnDefinition Width="*" />
-                            <ColumnDefinition Width="Auto" />
-                        </Grid.ColumnDefinitions>
-
-                        <!--The identity rating-->
-                        <Image Grid.Column="0"
-                               Height="15"
-                               Stretch="Uniform"
-                               VerticalAlignment="Stretch"
-                               HorizontalAlignment="Center"
-                               Margin="0,0,5,0"
-                               Source="{Binding Path=ItemImage, Mode=OneWay}">
-                        </Image>
-
-                        <!--The identity name-->
-                        <TextBlock Grid.Column="1"
-                                   HorizontalAlignment="Left"
-                                   VerticalAlignment="Center"
-                                   Margin="5,0"
-                                   Text="{Binding Path=ItemName, Mode=OneWay}" />
-
-                        <!--Trust button-->
-                        <Button Grid.Column="2"
-                                Style="{StaticResource StyleTrustButton}"
-                                Visibility="{Binding Path=IsButtonVisible, Converter={StaticResource BoolToVisibility}}"
-                                Margin="5"
-                                HorizontalAlignment="Right"
-                                Content="{Binding Path=ButtonText, Mode=OneWay}"
-                                Click="ButtonTrust_Click" />
-                    </Grid>
-
-                    <!--Advanced section-->
-                    <StackPanel Visibility="{Binding Path=IsExpanded, Converter={StaticResource BoolToVisibility}}">
-
-                        <!--Tabs-->
-                        <StackPanel Orientation="Horizontal"
-                                    HorizontalAlignment="Right">
-                            <Label Name="TrustwordsTabControl"
-                                   MouseLeftButtonUp="TabControl_MouseLeftButtonUp"
-                                   Content="{x:Static p:Resources.Handshake_TrustwordsText}"
-                                   Visibility="{Binding Path=AreTabControlsVisible, Converter={StaticResource BoolToVisibility}}">
-                                <Label.Style>
-                                    <Style TargetType="Label">
-                                        <Setter Property="BorderBrush"
-                                                Value="LightGray" />
-                                        <Setter Property="Background"
-                                                Value="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBackground}, ConverterParameter=Trustwords}" />
-                                        <Setter Property="BorderThickness"
-                                                Value="1" />
-                                        <Setter Property="Padding"
-                                                Value="10,5" />
-                                        <Style.Triggers>
-                                            <MultiDataTrigger>
-                                                <MultiDataTrigger.Conditions>
-                                                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
-                                                               Value="True" />
-                                                    <Condition Binding="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Trustwords}"
-                                                               Value="False" />
-                                                </MultiDataTrigger.Conditions>
-                                                <Setter Property="Background"
-                                                        Value="AliceBlue" />
-                                                <Setter Property="BorderBrush"
-                                                        Value="LightSkyBlue" />
-                                            </MultiDataTrigger>
-                                        </Style.Triggers>
-                                    </Style>
-                                </Label.Style>
-                            </Label>
-                            <Label Name="FingerprintTabControl"
-                                   MouseLeftButtonUp="TabControl_MouseLeftButtonUp"
-                                   Content="{x:Static p:Resources.Handshake_FingerprintText}"
-                                   Visibility="{Binding Path=AreTabControlsVisible, Converter={StaticResource BoolToVisibility}}">
-                                <Label.Style>
-                                    <Style TargetType="Label">
-                                        <Setter Property="BorderBrush"
-                                                Value="LightGray" />
-                                        <Setter Property="Background"
-                                                Value="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBackground}, ConverterParameter=Fingerprint}" />
-                                        <Setter Property="BorderThickness"
-                                                Value="1" />
-                                        <Setter Property="Padding"
-                                                Value="10,5" />
-                                        <Style.Triggers>
-                                            <MultiDataTrigger>
-                                                <MultiDataTrigger.Conditions>
-                                                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
-                                                               Value="True" />
-                                                    <Condition Binding="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Fingerprint}"
-                                                               Value="False" />
-                                                </MultiDataTrigger.Conditions>
-                                                <Setter Property="Background"
-                                                        Value="AliceBlue" />
-                                                <Setter Property="BorderBrush"
-                                                        Value="LightSkyBlue" />
-                                            </MultiDataTrigger>
-                                        </Style.Triggers>
-                                    </Style>
-                                </Label.Style>
-                            </Label>
-
-                            <!--Language selector-->
-                            <ComboBox Padding="10,2"
-                                      VerticalContentAlignment="Center"
-                                      ItemsSource="{Binding Path=TrustwordsCultureList, Mode=OneWay}"
-                                      DisplayMemberPath="Value"
-                                      SelectedValuePath="Key"
-                                      SelectedValue="{Binding Path=TrustwordsCulture, Mode=TwoWay}"
-                                      IsEnabled="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Trustwords}"
-                                      Foreground="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource IsEnabledToColor}}" />
-                        </StackPanel>
-
-                        <!-- Trustwords -->
-                        <StackPanel Background="White"
-                                    Visibility="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToVisibility}, ConverterParameter=Trustwords}">
-                            <TextBlock Text="{Binding Path=TrustwordsShort}"
-                                       Visibility="{Binding Path=AreTrustwordsExpanded, Converter={StaticResource InvertBoolToVisibility}}"
-                                       TextWrapping="Wrap"
-                                       Padding="10" />
-                            <TextBlock Text="{Binding Path=TrustwordsFull}"
-                                       Visibility="{Binding Path=AreTrustwordsExpanded, Converter={StaticResource BoolToVisibility}}"
-                                       TextWrapping="Wrap"
-                                       Padding="10" />
-                            <Expander ExpandDirection="Down"
-                                      Margin="10,10,5,5"
-                                      ToolTip="{Binding Path=ExpanderToolTip, Mode=OneWay}"
-                                      Collapsed="TrustwordsExpander_Toggled"
-                                      Expanded="TrustwordsExpander_Toggled" />
-                        </StackPanel>
-
-                        <!--Fingerprints-->
-                        <StackPanel Background="White"
-                                    Visibility="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToVisibility}, ConverterParameter=Fingerprint}">
-                            <TextBlock Text="{Binding Path=UIDPartner}"
-                                       Margin="10,10,10,2" />
-                            <TextBlock Text="{Binding Path=FingerprintPartner}"
-                                       Margin="10,2,10,22" />
-                            <TextBlock Text="{Binding Path=UIDMyself}"
-                                       Margin="10,10,10,2" />
-                            <TextBlock Text="{Binding Path=FingerprintMyself}"
-                                       Margin="10,2,10,10" />
-                        </StackPanel>
-
-                        <!-- Buttons -->
-                        <StackPanel Grid.Row="5"
-                                    Orientation="Horizontal"
-                                    HorizontalAlignment="Right"
-                                    Margin="0,5,0,0">
-                            <Button Style="{StaticResource StyleWrongButton}"
-                                    HorizontalAlignment="Center"
-                                    Margin="0,5,5,5"
-                                    Content="{Binding Path=ExpandedButton2Text, FallbackValue=Wrong}"
-                                    Click="ButtonWrong_Click" />
-                            <Button Style="{StaticResource StyleConfirmButton}"
-                                    HorizontalAlignment="Center"
-                                    Margin="5,5,0,5"
-                                    Content="{Binding Path=ExpandedButton1Text, FallbackValue=Confirm}"
-                                    Click="ButtonConfirm_Click"
-                                    IsDefault="True"
-                                    Loaded="ConfirmButton_Loaded" />
-                        </StackPanel>
-                    </StackPanel>
-                </StackPanel>
-            </DataTemplate>
-
-            <!-- Dictionary -->
-            <ResourceDictionary.MergedDictionaries>
-                <ResourceDictionary Source="pack://application:,,,/pEp;component/Resources/Dictionary.xaml" />
-            </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
     </Window.Resources>
 
@@ -277,8 +34,10 @@
                    Margin="5,5,5,15" />
 
         <!--Identities section-->
-        <ItemsControl ItemsSource="{Binding Path=Items}"
-                      ItemTemplate="{StaticResource HandshakeItemsTemplate}" />
+        <local:HandshakeItemsControl ItemsSource="{Binding Path=Items}"
+                                     ButtonConfirm_Clicked="ButtonConfirm_Clicked"
+                                     ButtonTrust_Clicked="ButtonTrust_Clicked"
+                                     ButtonWrong_Clicked="ButtonWrong_Clicked" />
 
         <!--Expander for identities without color-->
         <StackPanel Visibility="{Binding Path=IsExpanderVisible, Converter={StaticResource BoolToVisibility}}">
@@ -296,9 +55,11 @@
             </StackPanel>
 
             <!--Identities section-->
-            <ItemsControl ItemsSource="{Binding Path=NonColorItems}"
-                          ItemTemplate="{StaticResource HandshakeItemsTemplate}"
-                          Visibility="{Binding Path=IsExpanderExpanded, Converter={StaticResource BoolToVisibility}}"/>
+            <local:HandshakeItemsControl ItemsSource="{Binding Path=NonColorItems}"
+                                         ButtonConfirm_Clicked="ButtonConfirm_Clicked"
+                                         ButtonTrust_Clicked="ButtonTrust_Clicked"
+                                         ButtonWrong_Clicked="ButtonWrong_Clicked"
+                                         Visibility="{Binding Path=IsExpanderExpanded, Converter={StaticResource BoolToVisibility}}" />
 
         </StackPanel>
     </StackPanel>
--- a/UI/HandshakeDialog.xaml.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/HandshakeDialog.xaml.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -386,77 +386,9 @@
          *************************************************************/
 
         /// <summary>
-        /// Event handler for when an identity entry was clicked.
-        /// </summary>
-        private void IdentityGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
-        {
-            Grid grid = sender as Grid;
-            if (grid != null)
-            {
-                HandshakeItem handshakeItem = grid.DataContext as HandshakeItem;
-
-                if ((handshakeItem != null) &&
-                    (handshakeItem.IsClickable))
-                {
-                    foreach (var item in this._Items)
-                    {
-                        if (item.Equals(handshakeItem) == false)
-                        {
-                            item.IsExpanded = false;
-                            item.IsButtonVisible = false;
-                        }
-                    }
-
-                    if (handshakeItem.IsExpandable)
-                    {
-                        handshakeItem.IsExpanded = !handshakeItem.IsExpanded;
-                    }
-                    else if (handshakeItem.Color != pEpColor.pEpColorNoColor)
-                    {
-                        handshakeItem.IsButtonVisible = true;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Event handler for when a custom tab control is clicked.
-        /// </summary>
-        private void TabControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
-        {
-            Label label = sender as Label;
-
-            if (label != null)
-            {
-                HandshakeItem handshakeItem = label.DataContext as HandshakeItem;
-
-                if (handshakeItem != null)
-                {
-                    if (label.Name == "TrustwordsTabControl")
-                    {
-                        handshakeItem.ActiveTab = HandshakeItem.Tabs.Trustwords;
-                    }
-                    else if (label.Name == "FingerprintTabControl")
-                    {
-                        handshakeItem.ActiveTab = HandshakeItem.Tabs.Fingerprint;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Event handler for when a property is changed within the display state.
-        /// </summary>
-        private void DisplayState_PropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            this.PropertyChanged?.Invoke(this, e);
-            return;
-        }
-
-        /// <summary>
         /// Event handler for when the confirm button is clicked.
         /// </summary>
-        private void ButtonConfirm_Click(object sender, RoutedEventArgs e)
+        private void ButtonConfirm_Clicked(object sender, RoutedEventArgs e)
         {
             // In standard mode, trust identity's key
             if (this._Mode == HandshakeMode.Standard)
@@ -488,7 +420,7 @@
         /// <summary>
         /// Event handler for when the Wrong button is clicked.
         /// </summary>
-        private void ButtonWrong_Click(object sender, RoutedEventArgs e)
+        private void ButtonWrong_Clicked(object sender, RoutedEventArgs e)
         {
             // In standard mode, mistrust identity's key
             if (this._Mode == HandshakeMode.Standard)
@@ -520,7 +452,7 @@
         /// <summary>
         /// Event handler for when the start/stop trusting button is clicked.
         /// </summary>
-        private void ButtonTrust_Click(object sender, RoutedEventArgs e)
+        private void ButtonTrust_Clicked(object sender, RoutedEventArgs e)
         {
             PEPIdentity identityPartner = ((sender as Button)?.DataContext as HandshakeItem)?.Partner;
 
@@ -540,27 +472,6 @@
         }
 
         /// <summary>
-        /// Event handler for when the Trustwords expander is toggled.
-        /// </summary>
-        private void TrustwordsExpander_Toggled(object sender, RoutedEventArgs e)
-        {
-            HandshakeItem handshakeItem = (sender as Expander)?.DataContext as HandshakeItem;
-
-            if (handshakeItem != null)
-            {
-                handshakeItem.AreTrustwordsExpanded = ((sender as Expander)?.IsExpanded == true);
-            }
-        }
-
-        /// <summary>
-        /// Event handler for when the non color recipients expander is expanded or collapsed.
-        /// </summary>
-        private void NonColorRecipientsExpander_Toggled(object sender, RoutedEventArgs e)
-        {
-            this.IsExpanderExpanded = ((sender as Expander)?.IsExpanded == true);
-        }
-
-        /// <summary>
         /// Event handler for when the Confirm button has been loaded.
         /// </summary>
         private void ConfirmButton_Loaded(object sender, RoutedEventArgs e)
@@ -574,6 +485,14 @@
             }
         }
 
+        /// <summary>
+        /// Event handler for when the non color recipients expander is expanded or collapsed.
+        /// </summary>
+        private void NonColorRecipientsExpander_Toggled(object sender, RoutedEventArgs e)
+        {
+            this.IsExpanderExpanded = ((sender as Expander)?.IsExpanded == true);
+        }
+
         /**************************************************************
          * 
          * Methods
@@ -743,7 +662,7 @@
                                         {
                                             // Undo handshake
                                             item.ItemImage = imageGreen;
-                                            item.IsButtonVisible = (expandedItemCount++ == 0);
+                                            item.IsTrustButtonVisible = (expandedItemCount++ == 0);
                                             item.ButtonText = Properties.Resources.PrivacyStatus_StopTrusting;
 
                                             break;
@@ -761,7 +680,7 @@
                                         {
                                             // Hide if expander is collapsed
                                             item.ItemImage = imageNoColor;
-                                            item.IsButtonVisible = false;
+                                            item.IsTrustButtonVisible = false;
                                             item.IsClickable = false;
                                             break;
                                         }
@@ -770,7 +689,7 @@
                                         {
                                             // Red identities can not be interacted with
                                             item.ItemImage = imageNoColor;
-                                            item.IsButtonVisible = false;
+                                            item.IsTrustButtonVisible = false;
                                             item.IsClickable = false;
                                             break;
                                         }
@@ -880,144 +799,63 @@
             else if ((this._Mode == HandshakeMode.ForceProtectionSendKey) ||
                      (this._Mode == HandshakeMode.ForceProtectionImportKey))
             {
-                // Create one item and add it to collection
-                item = new HandshakeItem();
-
-                try
+                // Adapt wording according to mode
+                this.Title = Properties.Resources.Handshake_StandardFormText;
+                switch (this._Mode)
                 {
-                    item.Myself = this._Myself;
-                    item.Partner = this._SyncPartner;
-                    item.IsClickable = false;
-                    item.IsExpanded = true;
-                    item.IsSeparatorVisible = false;
-                    item.ActiveTab = HandshakeItem.Tabs.Trustwords;
-
-                    if (string.IsNullOrEmpty(item.TrustwordsFull) == true || string.IsNullOrEmpty(item.TrustwordsShort) == true)
-                    {
-                        Log.Error("BuildDialog. No Trustwords");
-                        return false;
-                    }
-
-                    // Adapt wording according to mode
-                    this.Title = Properties.Resources.Handshake_StandardFormText;
-                    switch (this._Mode)
-                    {
-                        case HandshakeMode.ForceProtectionSendKey:
-                            this.ExplanationText = Properties.Resources.Handshake_StandardExplanationText;
-                            break;
-                        case HandshakeMode.ForceProtectionImportKey:
-                            this.ExplanationText = Properties.Resources.Handshake_ForceProtectionImportKeyExplanationText;
-                            break;
-                        default:
-                            this.ExplanationText = string.Empty;
-                            break;
-                    }
-
-                    // Update the partner identity
-                    pEpIdentity syncPartner = this.SyncPartner.ToCOMType();
-                    PEPIdentity partnerIdentity = new PEPIdentity(ThisAddIn.PEPEngine.UpdateIdentity(syncPartner));
-
-                    try
-                    {
-                        partnerIdentity.Rating = ThisAddIn.PEPEngine.IdentityRating(syncPartner);
-                    }
-                    catch (COMException ex)
-                    {
-                        Log.Error("HandshakeDialog.BuildDialog: Error getting partner identity rating. " + ex.ToString());
-                        partnerIdentity.Rating = pEpRating.pEpRatingUndefined;
-                    }
-
-                    // Set partner user name
-                    if (string.IsNullOrEmpty(this.SyncPartner.UserName) == false)
-                    {
-                        item.ItemName = this.SyncPartner.UserName;
-
-                        if (string.IsNullOrEmpty(this.SyncPartner.Address) == false)
-                        {
-                            item.ItemName += " (" + this.SyncPartner.Address + ")";
-                        }
-                    }
-                    else
-                    {
-                        item.ItemName = this.SyncPartner.Address;
-                    }
-
-                    // Set rating image
-                    switch (partnerIdentity.Rating.ToColor())
-                    {
-                        case pEpColor.pEpColorGreen:
-                            item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusGreen.png", UriKind.RelativeOrAbsolute));
-                            break;
-                        case pEpColor.pEpColorYellow:
-                            item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusYellow.png", UriKind.RelativeOrAbsolute));
-                            break;
-                        case pEpColor.pEpColorRed:
-                            item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusRed.png", UriKind.RelativeOrAbsolute));
-                            break;
-                        case pEpColor.pEpColorNoColor:
-                            item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusNoColor.png", UriKind.RelativeOrAbsolute));
-                            break;
-                        default:
-                            item.ItemImage = null;
-                            break;
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Log.Error("HandshakeDialog.BuildDialog: Error creating key sync or FPP item. " + ex.ToString());
-                    return false;
+                    case HandshakeMode.ForceProtectionSendKey:
+                        this.ExplanationText = Properties.Resources.Handshake_StandardExplanationText;
+                        break;
+                    case HandshakeMode.ForceProtectionImportKey:
+                        this.ExplanationText = Properties.Resources.Handshake_ForceProtectionImportKeyExplanationText;
+                        break;
+                    default:
+                        this.ExplanationText = string.Empty;
+                        break;
                 }
 
-                this.Items.Add(item);
+                // Create one item and add it to collection
+                if (HandshakeItem.Create(this.Myself, this.SyncPartner, true, out item) == Globals.ReturnStatus.Success)
+                {
+                    this.Items.Add(item);
+                }
+                else
+                {
+                    Log.Error("BuildDialog: Error creating handshake item.");
+                    return false;
+                }                
             }
             // Key sync dialog
             else
             {
-                // Create one single sync item and add it to collection
-                item = new HandshakeItem();
-
-                try
+                // Adapt wording according to sync mode
+                this.Title = Properties.Resources.Handshake_SyncFormText;
+                switch (this._Mode)
                 {
-                    item.Myself = this._Myself;
-                    item.Partner = this._SyncPartner;
-                    item.IsClickable = false;
-                    item.IsExpanded = true;
-                    item.IsSeparatorVisible = false;
-                    item.ActiveTab = HandshakeItem.Tabs.Trustwords;
-                    item.ExpandedButton1Text = Properties.Resources.Handshake_Accept;
-                    item.ExpandedButton2Text = Properties.Resources.Handshake_Reject;
-
-                    if (string.IsNullOrEmpty(item.TrustwordsFull) == true || string.IsNullOrEmpty(item.TrustwordsShort) == true)
-                    {
-                        Log.Error("BuildDialog. No Trustwords");
-                        return false;
-                    }
+                    case HandshakeMode.SyncTypeA:
+                        this.ExplanationText = Properties.Resources.Handshake_SyncTypeAExplanationText;
+                        break;
+                    case HandshakeMode.SyncTypeB:
+                        this.ExplanationText = Properties.Resources.Handshake_SyncTypeBExplanationText;
+                        break;
+                    case HandshakeMode.SyncTypeC:
+                        this.ExplanationText = Properties.Resources.Handshake_SyncTypeCExplanationText;
+                        break;
+                    default:
+                        this.ExplanationText = string.Empty;
+                        break;
+                }
 
-                    // Adapt wording according to sync mode
-                    this.Title = Properties.Resources.Handshake_SyncFormText;
-                    switch (this._Mode)
-                    {
-                        case HandshakeMode.SyncTypeA:
-                            this.ExplanationText = Properties.Resources.Handshake_SyncTypeAExplanationText;
-                            break;
-                        case HandshakeMode.SyncTypeB:
-                            this.ExplanationText = Properties.Resources.Handshake_SyncTypeBExplanationText;
-                            break;
-                        case HandshakeMode.SyncTypeC:
-                            this.ExplanationText = Properties.Resources.Handshake_SyncTypeCExplanationText;
-                            break;
-                        default:
-                            this.ExplanationText = string.Empty;
-                            break;
-                    }
+                // Create one item and add it to collection
+                if (HandshakeItem.Create(this.Myself, this.SyncPartner, false, out item) == Globals.ReturnStatus.Success)
+                {
+                    this.Items.Add(item);
                 }
-                catch (Exception ex)
+                else
                 {
-                    Log.Error("HandshakeDialog.BuildDialog: Error creating key sync item. " + ex.ToString());
+                    Log.Error("BuildDialog: Error creating handshake item.");
                     return false;
                 }
-
-                this.Items.Add(item);
             }
 
             return result;
--- a/UI/HandshakeItem.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/HandshakeItem.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -32,6 +32,7 @@
         }
 
         private Tabs                                        _ActiveTab;
+        private bool                                        _AreHandshakeButtonsVisible;
         private bool                                        _AreTabControlsVisible;
         private bool                                        _AreTrustwordsExpanded;
         private EventHandler                                _ButtonOnClick;
@@ -42,7 +43,7 @@
         private string                                      _ExpandedButton1Text;
         private string                                      _ExpandedButton2Text;
         private string                                      _ExpanderToolTip;
-        private bool                                        _IsButtonVisible;
+        private bool                                        _IsTrustButtonVisible;
         private bool                                        _IsClickable;
         private bool                                        _IsExpanded;
         private bool                                        _IsExpandable;
@@ -78,7 +79,7 @@
          *************************************************************/
 
         /// <summary>
-        /// Gets or sets the active tab
+        /// Gets or sets the active tab.
         /// </summary>
         public Tabs ActiveTab
         {
@@ -93,7 +94,20 @@
         }
 
         /// <summary>
-        /// Gets or sets whether the tab controls are visible
+        /// Gets or sets whether the buttons are visible.
+        /// </summary>
+        public bool AreHandshakeButtonsVisible
+        {
+            get { return this._AreHandshakeButtonsVisible; }
+            set
+            {
+                this._AreHandshakeButtonsVisible = value;
+                this.RaisePropertyChangedEvent(nameof(this._AreHandshakeButtonsVisible));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets whether the tab controls are visible.
         /// </summary>
         public bool AreTabControlsVisible
         {
@@ -106,7 +120,7 @@
         }
 
         /// <summary>
-        /// Gets or sets whether the short or long trustwords are shown
+        /// Gets or sets whether the short or long trustwords are shown.
         /// </summary>
         public bool AreTrustwordsExpanded
         {
@@ -224,15 +238,15 @@
         }
 
         /// <summary>
-        /// Gets or sets whether the button is visible next to the item.
+        /// Gets or sets whether the trust button.
         /// </summary>
-        public bool IsButtonVisible
+        public bool IsTrustButtonVisible
         {
-            get { return (this._IsButtonVisible); }
+            get { return (this._IsTrustButtonVisible); }
             set
             {
-                this._IsButtonVisible = value;
-                this.RaisePropertyChangedEvent(nameof(this.IsButtonVisible));
+                this._IsTrustButtonVisible = value;
+                this.RaisePropertyChangedEvent(nameof(this.IsTrustButtonVisible));
             }
         }
 
@@ -501,6 +515,7 @@
             HandshakeItem copy = new HandshakeItem();
 
             copy.ActiveTab = this._ActiveTab;
+            copy.AreHandshakeButtonsVisible = this._AreHandshakeButtonsVisible;
             copy.AreTabControlsVisible = this._AreTabControlsVisible;
             copy.AreTrustwordsExpanded = this._AreTrustwordsExpanded;
             copy.ButtonOnClick = (this._ButtonOnClick == null ? null : (EventHandler)this._ButtonOnClick.Clone());
@@ -517,7 +532,7 @@
             copy.Myself = this._Myself;
             copy.Partner = this._Partner;
             copy.TrustwordsCulture = this._TrustwordsCulture;
-            copy.IsButtonVisible = this._IsButtonVisible;
+            copy.IsTrustButtonVisible = this._IsTrustButtonVisible;
             copy.IsClickable = this._IsClickable;
             copy.IsExpanded = this._IsExpanded;
             copy.IsExpandable = this._IsExpandable;
@@ -533,6 +548,7 @@
         private void Reset()
         {
             this._ActiveTab = Tabs.Trustwords;
+            this._AreHandshakeButtonsVisible = true;
             this._AreTabControlsVisible = false;
             this._AreTrustwordsExpanded = false;
             this._ButtonOnClick = null;
@@ -543,7 +559,7 @@
             this._ExpandedButton1Text = null;
             this._ExpandedButton2Text = null;
             this._ExpanderToolTip = null;
-            this._IsButtonVisible = false;
+            this._IsTrustButtonVisible = false;
             this._IsClickable = true;
             this._IsExpanded = false;
             this._IsExpandable = false;
@@ -559,6 +575,7 @@
 
 
             this.RaisePropertyChangedEvent(nameof(this.ActiveTab));
+            this.RaisePropertyChangedEvent(nameof(this.AreHandshakeButtonsVisible));
             this.RaisePropertyChangedEvent(nameof(this.AreTabControlsVisible));
             this.RaisePropertyChangedEvent(nameof(this.AreTrustwordsExpanded));
             this.RaisePropertyChangedEvent(nameof(this.ButtonOnClick));
@@ -569,7 +586,7 @@
             this.RaisePropertyChangedEvent(nameof(this.ExpandedButton1Text));
             this.RaisePropertyChangedEvent(nameof(this.ExpandedButton2Text));
             this.RaisePropertyChangedEvent(nameof(this.ExpanderToolTip));
-            this.RaisePropertyChangedEvent(nameof(this.IsButtonVisible));
+            this.RaisePropertyChangedEvent(nameof(this.IsTrustButtonVisible));
             this.RaisePropertyChangedEvent(nameof(this.IsClickable));
             this.RaisePropertyChangedEvent(nameof(this.IsExpanded));
             this.RaisePropertyChangedEvent(nameof(this.IsExpandable));
@@ -636,5 +653,114 @@
                 this.RaisePropertyChangedEvent(nameof(this.TrustwordsShort));
             }
         }
+
+        /**************************************************************
+         * 
+         * Static methods
+         * 
+         *************************************************************/
+
+        /// <summary>
+        /// Creates a handshake item.
+        /// Note: Can return a null HandshakeItem.
+        /// </summary>
+        /// <param name="myself">The Myself identity.</param>
+        /// <param name="partner">The handshake partner identity.</param>
+        /// <param name="showUserName">Whether to show the user name and the color.</param>
+        /// <param name="handshakeItem">The created handshake item or null if an error occured.</param>
+        /// <returns>The status of this method.</returns>
+        public static Globals.ReturnStatus Create(PEPIdentity myself,
+                                                  PEPIdentity partner,
+                                                  bool showUserName,
+                                                  out HandshakeItem handshakeItem)
+        {
+            HandshakeItem item = null;
+            Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
+
+            if (partner.IsAddressValid)
+            {
+                item = new HandshakeItem();
+
+                try
+                {
+                    item.Myself = myself;
+                    item.Partner = partner;
+                    item.IsClickable = false;
+                    item.IsExpanded = true;
+                    item.IsSeparatorVisible = false;
+                    item.ActiveTab = HandshakeItem.Tabs.Trustwords;
+
+                    if (string.IsNullOrEmpty(item.TrustwordsFull) == true || string.IsNullOrEmpty(item.TrustwordsShort) == true)
+                    {
+                        throw new Exception("Trustwords are null or empty");
+                    }
+
+                    if (showUserName)
+                    {
+                        try
+                        {
+                            pEpIdentity comPartner = partner.ToCOMType();
+                            partner.Rating = ThisAddIn.PEPEngine.IdentityRating(comPartner);
+                        }
+                        catch (Exception ex)
+                        {
+                            Log.Error("HandshakeItem.Create: Error getting partner identity rating. " + ex.ToString());
+                            partner.Rating = pEpRating.pEpRatingUndefined;
+                        }
+
+                        // Set partner user name
+                        if (string.IsNullOrEmpty(partner.UserName) == false)
+                        {
+                            item.ItemName = partner.UserName;
+
+                            if (string.IsNullOrEmpty(partner.Address) == false)
+                            {
+                                item.ItemName += " (" + partner.Address + ")";
+                            }
+                        }
+                        else
+                        {
+                            item.ItemName = partner.Address;
+                        }
+
+                        // Set rating image
+                        switch (partner.Rating.ToColor())
+                        {
+                            case pEpColor.pEpColorGreen:
+                                item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusGreen.png", UriKind.RelativeOrAbsolute));
+                                break;
+                            case pEpColor.pEpColorYellow:
+                                item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusYellow.png", UriKind.RelativeOrAbsolute));
+                                break;
+                            case pEpColor.pEpColorRed:
+                                item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusRed.png", UriKind.RelativeOrAbsolute));
+                                break;
+                            case pEpColor.pEpColorNoColor:
+                                item.ItemImage = new BitmapImage(new Uri("pack://application:,,,/pEp;component/Resources/ImagePrivacyStatusNoColor.png", UriKind.RelativeOrAbsolute));
+                                break;
+                            default:
+                                item.ItemImage = null;
+                                break;
+                        }
+                    }
+
+                    status = Globals.ReturnStatus.Success;
+                }
+                catch (Exception ex)
+                {
+                    item = null;
+                    Log.Error("HandshakeItem.Create: Error creating handshake item. " + ex.ToString());
+                }
+            }
+            else // Invalid identity
+            {
+                item = null;
+                Log.Error("HandshakeItem.Create: Address invalid.");
+                Log.SensitiveData("HandshakeItem.Create: Invalid address: " + partner.Address);
+            }
+
+            handshakeItem = item;
+            return status;
+        }
     }
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UI/HandshakeItemsControl.xaml	Tue Apr 10 10:50:14 2018 +0200
@@ -0,0 +1,261 @@
+<ItemsControl x:Class="pEp.UI.HandshakeItemsControl"
+              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+              xmlns:local="clr-namespace:pEp.UI"
+              xmlns:p="clr-namespace:pEp.Properties"
+              mc:Ignorable="d">
+    <ItemsControl.Resources>
+        <ResourceDictionary>
+            <!-- Converters -->
+            <BooleanToVisibilityConverter x:Key="BoolToVisibility" />
+            <local:IsEnabledToColorConverter x:Key="IsEnabledToColor" />
+            <local:IsActiveTabToBoolConverter x:Key="IsActiveTabToBool" />
+            <local:ValueConverterGroup x:Key="IsActiveTabToVisibility">
+                <local:IsActiveTabToBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsActiveTabToBackground">
+                <local:IsActiveTabToBoolConverter />
+                <local:BooleanToBackgroundConverter />
+            </local:ValueConverterGroup>
+            <local:InvertBoolConverter x:Key="InvertBool" />
+            <local:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibility" />
+            <local:ValueConverterGroup x:Key="InvertBoolToVisibility">
+                <local:InvertBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsStandardModeToVisibility">
+                <local:IsStandardModeToBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsNotStandardModeToVisibility">
+                <local:IsStandardModeToBoolConverter />
+                <local:InvertBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsStringEmptyToVisibility">
+                <local:IsStringEmptyConverter />
+                <local:InvertBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsNotSyncModeToVisibility">
+                <local:IsSyncModeToBoolConverter />
+                <local:InvertBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+
+            <!-- Dictionary -->
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="pack://application:,,,/pEp;component/Resources/Dictionary.xaml" />
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </ItemsControl.Resources>
+
+    <ItemsControl.ItemTemplate>
+        <DataTemplate>
+            <StackPanel>
+                <StackPanel.Style>
+                    <Style TargetType="StackPanel">
+                        <Setter Property="Background"
+                                Value="Transparent" />
+                        <Style.Triggers>
+                            <MultiDataTrigger>
+                                <MultiDataTrigger.Conditions>
+                                    <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
+                                               Value="True" />
+                                    <Condition Binding="{Binding Path=IsExpanded}"
+                                               Value="False" />
+                                    <Condition Binding="{Binding Path=IsButtonVisible}"
+                                               Value="False" />
+                                    <Condition Binding="{Binding Path=IsClickable}"
+                                               Value="True" />
+                                </MultiDataTrigger.Conditions>
+                                <Setter Property="Background"
+                                        Value="{x:Static SystemColors.ControlLightBrush}" />
+                            </MultiDataTrigger>
+                        </Style.Triggers>
+                    </Style>
+                </StackPanel.Style>
+
+                <!--Separator between items-->
+                <Separator Margin="5,5,5,0"
+                           Background="LightGray"
+                           Visibility="{Binding Path=IsSeparatorVisible, Converter={StaticResource BoolToVisibility}}" />
+
+                <!--The list entry-->
+                <Grid Name="IdentityGrid"
+                      Background="Transparent"
+                      Margin="5"
+                      MinHeight="38"
+                      MouseLeftButtonUp="IdentityGrid_MouseLeftButtonUp"
+                      Visibility="{Binding Path=Mode, Converter={StaticResource IsNotSyncModeToVisibility}}">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="Auto" />
+                        <ColumnDefinition Width="*" />
+                        <ColumnDefinition Width="Auto" />
+                    </Grid.ColumnDefinitions>
+
+                    <!--The identity rating-->
+                    <Image Grid.Column="0"
+                           Height="15"
+                           Stretch="Uniform"
+                           VerticalAlignment="Stretch"
+                           HorizontalAlignment="Center"
+                           Margin="0,0,5,0"
+                           Source="{Binding Path=ItemImage, Mode=OneWay}">
+                    </Image>
+
+                    <!--The identity name-->
+                    <TextBlock Grid.Column="1"
+                               HorizontalAlignment="Left"
+                               VerticalAlignment="Center"
+                               Margin="5,0"
+                               Text="{Binding Path=ItemName, Mode=OneWay}" />
+
+                    <!--Trust button-->
+                    <Button Grid.Column="2"
+                            Style="{StaticResource StyleTrustButton}"
+                            Visibility="{Binding Path=IsTrustButtonVisible, Converter={StaticResource BoolToVisibility}}"
+                            Margin="5"
+                            HorizontalAlignment="Right"
+                            Content="{Binding Path=ButtonText, Mode=OneWay}"
+                            Click="ButtonTrust_Click" />
+                </Grid>
+
+                <!--Advanced section-->
+                <StackPanel Visibility="{Binding Path=IsExpanded, Converter={StaticResource BoolToVisibility}}">
+
+                    <!--Tabs-->
+                    <StackPanel Orientation="Horizontal"
+                                HorizontalAlignment="Right">
+                        <Label Name="TrustwordsTabControl"
+                               MouseLeftButtonUp="TabControl_MouseLeftButtonUp"
+                               Content="{x:Static p:Resources.Handshake_TrustwordsText}"
+                               Visibility="{Binding Path=AreTabControlsVisible, Converter={StaticResource BoolToVisibility}}">
+                            <Label.Style>
+                                <Style TargetType="Label">
+                                    <Setter Property="BorderBrush"
+                                            Value="LightGray" />
+                                    <Setter Property="Background"
+                                            Value="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBackground}, ConverterParameter=Trustwords}" />
+                                    <Setter Property="BorderThickness"
+                                            Value="1" />
+                                    <Setter Property="Padding"
+                                            Value="10,5" />
+                                    <Style.Triggers>
+                                        <MultiDataTrigger>
+                                            <MultiDataTrigger.Conditions>
+                                                <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
+                                                           Value="True" />
+                                                <Condition Binding="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Trustwords}"
+                                                           Value="False" />
+                                            </MultiDataTrigger.Conditions>
+                                            <Setter Property="Background"
+                                                    Value="AliceBlue" />
+                                            <Setter Property="BorderBrush"
+                                                    Value="LightSkyBlue" />
+                                        </MultiDataTrigger>
+                                    </Style.Triggers>
+                                </Style>
+                            </Label.Style>
+                        </Label>
+                        <Label Name="FingerprintTabControl"
+                               MouseLeftButtonUp="TabControl_MouseLeftButtonUp"
+                               Content="{x:Static p:Resources.Handshake_FingerprintText}"
+                               Visibility="{Binding Path=AreTabControlsVisible, Converter={StaticResource BoolToVisibility}}">
+                            <Label.Style>
+                                <Style TargetType="Label">
+                                    <Setter Property="BorderBrush"
+                                            Value="LightGray" />
+                                    <Setter Property="Background"
+                                            Value="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBackground}, ConverterParameter=Fingerprint}" />
+                                    <Setter Property="BorderThickness"
+                                            Value="1" />
+                                    <Setter Property="Padding"
+                                            Value="10,5" />
+                                    <Style.Triggers>
+                                        <MultiDataTrigger>
+                                            <MultiDataTrigger.Conditions>
+                                                <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
+                                                           Value="True" />
+                                                <Condition Binding="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Fingerprint}"
+                                                           Value="False" />
+                                            </MultiDataTrigger.Conditions>
+                                            <Setter Property="Background"
+                                                    Value="AliceBlue" />
+                                            <Setter Property="BorderBrush"
+                                                    Value="LightSkyBlue" />
+                                        </MultiDataTrigger>
+                                    </Style.Triggers>
+                                </Style>
+                            </Label.Style>
+                        </Label>
+
+                        <!--Language selector-->
+                        <ComboBox Padding="10,2"
+                                  VerticalContentAlignment="Center"
+                                  ItemsSource="{Binding Path=TrustwordsCultureList, Mode=OneWay}"
+                                  DisplayMemberPath="Value"
+                                  SelectedValuePath="Key"
+                                  SelectedValue="{Binding Path=TrustwordsCulture, Mode=TwoWay}"
+                                  IsEnabled="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToBool}, ConverterParameter=Trustwords}"
+                                  Foreground="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource IsEnabledToColor}}" />
+                    </StackPanel>
+
+                    <!-- Trustwords -->
+                    <StackPanel Background="White"
+                                Visibility="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToVisibility}, ConverterParameter=Trustwords}">
+                        <TextBlock Text="{Binding Path=TrustwordsShort}"
+                                   Visibility="{Binding Path=AreTrustwordsExpanded, Converter={StaticResource InvertBoolToVisibility}}"
+                                   TextWrapping="Wrap"
+                                   Padding="10" />
+                        <TextBlock Text="{Binding Path=TrustwordsFull}"
+                                   Visibility="{Binding Path=AreTrustwordsExpanded, Converter={StaticResource BoolToVisibility}}"
+                                   TextWrapping="Wrap"
+                                   Padding="10" />
+                        <Expander ExpandDirection="Down"
+                                  Margin="10,10,5,5"
+                                  ToolTip="{Binding Path=ExpanderToolTip, Mode=OneWay}"
+                                  Collapsed="TrustwordsExpander_Toggled"
+                                  Expanded="TrustwordsExpander_Toggled" />
+                    </StackPanel>
+
+                    <!--Fingerprints-->
+                    <StackPanel Background="White"
+                                Visibility="{Binding Path=ActiveTab, Converter={StaticResource IsActiveTabToVisibility}, ConverterParameter=Fingerprint}">
+                        <TextBlock Text="{Binding Path=UIDPartner}"
+                                   Margin="10,10,10,2" />
+                        <TextBlock Text="{Binding Path=FingerprintPartner}"
+                                   Margin="10,2,10,22" />
+                        <TextBlock Text="{Binding Path=UIDMyself}"
+                                   Margin="10,10,10,2" />
+                        <TextBlock Text="{Binding Path=FingerprintMyself}"
+                                   Margin="10,2,10,10" />
+                    </StackPanel>
+
+                    <!-- Buttons -->
+                    <StackPanel Grid.Row="5"
+                                Orientation="Horizontal"
+                                HorizontalAlignment="Right"
+                                Margin="0,5,0,0"
+                                Visibility="{Binding Path=AreHandshakeButtonsVisible, Converter={StaticResource BoolToVisibility}}">
+                        <Button Style="{StaticResource StyleWrongButton}"
+                                HorizontalAlignment="Center"
+                                Margin="0,5,5,5"
+                                Content="{Binding Path=ExpandedButton2Text, FallbackValue=Wrong}"
+                                Click="ButtonWrong_Click" />
+                        <Button Style="{StaticResource StyleConfirmButton}"
+                                HorizontalAlignment="Center"
+                                Margin="5,5,0,5"
+                                Content="{Binding Path=ExpandedButton1Text, FallbackValue=Confirm}"
+                                Click="ButtonConfirm_Click"
+                                IsDefault="True" />
+                    </StackPanel>
+                </StackPanel>
+            </StackPanel>
+        </DataTemplate>
+    </ItemsControl.ItemTemplate>
+
+</ItemsControl>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UI/HandshakeItemsControl.xaml.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -0,0 +1,131 @@
+using pEpCOMServerAdapterLib;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace pEp.UI
+{
+    /// <summary>
+    /// Interaction logic for HandshakeItemsControl.xaml
+    /// </summary>
+    public partial class HandshakeItemsControl : ItemsControl, 
+                                                 INotifyPropertyChanged
+    {
+        /// <summary>
+        /// Event raised when a property is changed on a component.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        public event RoutedEventHandler ButtonConfirm_Clicked;
+        public event RoutedEventHandler ButtonWrong_Clicked;
+        public event RoutedEventHandler ButtonTrust_Clicked;
+
+        public HandshakeItemsControl()
+        {
+            InitializeComponent();
+        }
+
+        /**************************************************************
+        * 
+        * Event Handling
+        * 
+        *************************************************************/
+
+        /// <summary>
+        /// Event handler for when an identity entry was clicked.
+        /// </summary>
+        private void IdentityGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+        {
+            Grid grid = sender as Grid;
+            if (grid != null)
+            {
+                HandshakeItem handshakeItem = grid.DataContext as HandshakeItem;
+
+                if ((handshakeItem != null) &&
+                    (handshakeItem.IsClickable))
+                {
+                    foreach (var item in this.Items)
+                    {
+                        if ((item as HandshakeItem).Equals(handshakeItem) == false)
+                        {
+                            (item as HandshakeItem).IsExpanded = false;
+                            (item as HandshakeItem).IsTrustButtonVisible = false;
+                        }
+                    }
+
+                    if (handshakeItem.IsExpandable)
+                    {
+                        handshakeItem.IsExpanded = !handshakeItem.IsExpanded;
+                    }
+                    else if (handshakeItem.Color != pEpColor.pEpColorNoColor)
+                    {
+                        handshakeItem.IsTrustButtonVisible = true;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Event handler for when a custom tab control is clicked.
+        /// </summary>
+        private void TabControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+        {
+            Label label = sender as Label;
+
+            if (label != null)
+            {
+                HandshakeItem handshakeItem = label.DataContext as HandshakeItem;
+
+                if (handshakeItem != null)
+                {
+                    if (label.Name == "TrustwordsTabControl")
+                    {
+                        handshakeItem.ActiveTab = HandshakeItem.Tabs.Trustwords;
+                    }
+                    else if (label.Name == "FingerprintTabControl")
+                    {
+                        handshakeItem.ActiveTab = HandshakeItem.Tabs.Fingerprint;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Event handler for when the confirm button is clicked.
+        /// </summary>
+        private void ButtonConfirm_Click(object sender, RoutedEventArgs e)
+        {
+            this.ButtonConfirm_Clicked?.Invoke(sender, e);
+        }
+
+        /// <summary>
+        /// Event handler for when the Wrong button is clicked.
+        /// </summary>
+        private void ButtonWrong_Click(object sender, RoutedEventArgs e)
+        {
+            this.ButtonWrong_Clicked?.Invoke(sender, e);
+        }
+
+        /// <summary>
+        /// Event handler for when the start/stop trusting button is clicked.
+        /// </summary>
+        private void ButtonTrust_Click(object sender, RoutedEventArgs e)
+        {
+            this.ButtonTrust_Clicked?.Invoke(sender, e);
+        }
+
+        /// <summary>
+        /// Event handler for when the Trustwords expander is toggled.
+        /// </summary>
+        private void TrustwordsExpander_Toggled(object sender, RoutedEventArgs e)
+        {
+            HandshakeItem handshakeItem = (sender as Expander)?.DataContext as HandshakeItem;
+
+            if (handshakeItem != null)
+            {
+                handshakeItem.AreTrustwordsExpanded = ((sender as Expander)?.IsExpanded == true);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UI/KeySyncWizard.xaml	Tue Apr 10 10:50:14 2018 +0200
@@ -0,0 +1,76 @@
+<Window x:Class="pEp.UI.KeySyncWizard"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:core="clr-namespace:System;assembly=mscorlib"
+        xmlns:p="clr-namespace:pEp.Properties"
+        xmlns:local="clr-namespace:pEp.UI"
+        mc:Ignorable="d"
+        MinHeight="5"
+        Height="Auto"
+        Width="Auto"
+        x:ClassModifier="internal"
+        ResizeMode="NoResize"
+        SizeToContent="WidthAndHeight"        
+        Topmost="True"
+        Background="{x:Static SystemColors.MenuBarBrush}"
+        Title="{x:Static p:Resources.KeySyncWizard_WindowTitlePGP}"
+        Closed="Window_Closed"
+        Icon="pack://application:,,,/pEp;component/Resources/ImageLogoIcon.png"
+        WindowStartupLocation="CenterScreen">
+    <Window.Resources>
+        <ResourceDictionary>
+            <!-- Converters -->
+            <BooleanToVisibilityConverter x:Key="BoolToVisibility" />
+            <local:InvertBoolConverter x:Key="InvertBool" />
+            <local:ValueConverterGroup x:Key="IsWizardStateToVisibility">
+                <local:IsWizardStateConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsNotWizardStateToVisibility">
+                <local:IsWizardStateConverter />
+                <local:InvertBoolConverter />
+                <BooleanToVisibilityConverter />
+            </local:ValueConverterGroup>
+            <local:ValueConverterGroup x:Key="IsNotWizardStateToBool">
+                <local:IsWizardStateConverter />
+                <local:InvertBoolConverter />
+            </local:ValueConverterGroup>
+        </ResourceDictionary>
+    </Window.Resources>
+
+    <!--The window content-->
+    <StackPanel Margin="10"
+                Width="470">
+
+        <!--Information section-->
+        <TextBlock Text="{Binding Path=ExplanationText}"
+                   TextWrapping="Wrap"
+                   Margin="5,5,5,15" />
+
+        <!--Identities section-->
+        <local:HandshakeItemsControl ItemsSource="{Binding Path=Items}"
+                                     Visibility="{Binding Path=State, Converter={StaticResource IsWizardStateToVisibility}, ConverterParameter=Step2}" />
+
+        <StackPanel Orientation="Horizontal"                    
+                    HorizontalAlignment="Right"
+                    Margin="5">
+            <Button x:Name="OKNextButton"
+                    Margin="5"
+                    Padding="5,2"
+                    MinWidth="50"
+                    IsEnabled="{Binding Path=State, Converter={StaticResource IsNotWizardStateToBool}, ConverterParameter=Step1|Step3}"
+                    Click="OKNextButton_Click"
+                    Content="{Binding Path=NextButtonText}"/>
+            <Button x:Name="CancelButton"
+                    Margin="5"
+                    Padding="5,2"
+                    MinWidth="50"
+                    Click="CancelButton_Click"
+                    Visibility="{Binding Path=State, Converter={StaticResource IsNotWizardStateToVisibility}, ConverterParameter=Step4}"
+                    Content="{x:Static p:Resources.Options_CancelText}" />
+        </StackPanel>
+    </StackPanel>
+</Window>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UI/KeySyncWizard.xaml.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -0,0 +1,566 @@
+using pEpCOMServerAdapterLib;
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Windows;
+using Outlook = Microsoft.Office.Interop.Outlook;
+
+namespace pEp.UI
+{
+    /// <summary>
+    /// Interaction logic for KeySyncWizard.xaml
+    /// </summary>
+    internal partial class KeySyncWizard : Window,
+                                           INotifyPropertyChanged,
+                                           Interfaces.IReset
+    {
+        /// <summary>
+        /// Defines the wizard type.
+        /// </summary>
+        public enum WizardType
+        {
+            Undefined,
+            PGP,
+            pEp
+        }
+
+        /// <summary>
+        /// Defines the state (step) this wizard is currently in.
+        /// </summary>
+        public enum WizardState
+        {
+            Undefined,
+            Step1,
+            Step2,
+            Step3,
+            Step4
+        }
+
+        /// <summary>
+        /// Event raised when a property is changed on a component.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        private ObservableCollection<string>            _KeysToExport;
+        private ObservableCollection<string>            _KeysToImport;
+        private string                                  _NextButtonText;
+        private string                                  _ExplanationText;
+        private WizardType                              _Type;
+        private WizardState                             _State;
+        private bool                                    _Success;
+        private PEPIdentity                             _SyncPartner;
+        private ObservableCollection<HandshakeItem>     _Items;
+
+        public static bool                              WizardInProcess = false;
+        private static KeySyncWizard                    currentWizard = null;
+
+        /// <summary>
+        /// Default constructor
+        /// </summary>
+        public KeySyncWizard(WizardType type)
+        {
+            // Only allow one wizard at a time
+            if (KeySyncWizard.currentWizard != null)
+            {
+                KeySyncWizard.currentWizard.Close();
+            }
+
+            // Initialize the window
+            InitializeComponent();
+
+            // Reset all values
+            this.Reset();
+
+            // Initialize the wizard
+            this.InitializeWizard(type);
+
+            // Set global flag to true
+            KeySyncWizard.WizardInProcess = true;
+
+            // Attach event handler for new sync messages
+            MsgProcessor.SyncMessageReceived += MsgProcessor_SyncMessageReceived;
+
+            // Send initial message
+            this.SendInitialMessage();
+
+            // Set current wizard
+            KeySyncWizard.currentWizard = this;
+        }
+
+        #region Property Accessors
+        /**************************************************************
+         * 
+         * Property Accessors
+         * 
+         *************************************************************/
+
+        /// <summary>
+        /// Gets or sets the list of keys to export.
+        /// </summary>
+        public ObservableCollection<string> KeysToExport
+        {
+            get { return this._KeysToExport; }
+            set
+            {
+                this._KeysToExport = value;
+                this.RaisePropertyChangedEvent(nameof(this.KeysToExport));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the list of keys to import.
+        /// </summary>
+        public ObservableCollection<string> KeysToImport
+        {
+            get { return this._KeysToImport; }
+            set
+            {
+                this._KeysToImport = value;
+                this.RaisePropertyChangedEvent(nameof(this.KeysToImport));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the Items collection.
+        /// </summary>
+        public ObservableCollection<HandshakeItem> Items
+        {
+            get { return this._Items; }
+            set
+            {
+                this._Items = value;
+                this.RaisePropertyChangedEvent(nameof(this.Items));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the wizard state.
+        /// </summary>
+        public WizardState State
+        {
+            get { return this._State; }
+            set
+            {
+                this._State = value;
+                this.RaisePropertyChangedEvent(nameof(this.State));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the success state.
+        /// </summary>
+        public bool Success
+        {
+            get { return this._Success; }
+            set
+            {
+                this._Success = value;
+                this.RaisePropertyChangedEvent(nameof(this.Success));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the sync partner.
+        /// </summary>
+        public PEPIdentity SyncPartner
+        {
+            get { return this._SyncPartner; }
+            set
+            {
+                this._SyncPartner = value;
+                this.RaisePropertyChangedEvent(nameof(this.SyncPartner));
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the wizard type.
+        /// </summary>
+        public WizardType Type
+        {
+            get { return this._Type; }
+            set
+            {
+                this._Type = value;
+                this.RaisePropertyChangedEvent(nameof(this.Type));
+            }
+        }
+
+        /// <summary>
+        /// Gets the Next/Finish button's text depending on the wizard's state.
+        /// </summary>
+        public string NextButtonText
+        {
+            get { return this._NextButtonText; }
+            set
+            {
+                this._NextButtonText = value;
+                this.RaisePropertyChangedEvent(nameof(this.NextButtonText));
+            }
+        }
+
+        /// <summary>
+        /// Gets the explanatory text in the wizard depending on its state.
+        /// </summary>
+        public string ExplanationText
+        {
+            get { return this._ExplanationText; }
+            set
+            {
+                this._ExplanationText = value;
+                this.RaisePropertyChangedEvent(nameof(this.ExplanationText));
+            }
+        }
+
+        #endregion
+
+        #region Event handlers
+        /**************************************************************
+         * 
+         * Event handlers
+         * 
+         *************************************************************/
+
+        private void CancelButton_Click(object sender, RoutedEventArgs e)
+        {
+            this.Close();
+        }
+
+        private void OKNextButton_Click(object sender, RoutedEventArgs e)
+        {
+            // Navigation logic for PGP import
+            if (this.Type == WizardType.PGP)
+            {
+                switch (this.State)
+                {
+                    case WizardState.Step2:
+                        {
+                            // User accepts the fingerprint. Make channel green.
+                            this.TrustKey();
+                        }
+                        break;
+                    /* 'Step 1' and 'Step 3' are not clickable. 
+                     * 'Step 4' finishes the process. 
+                     * 'Undefined' means an error occured.
+                     */
+                    case WizardState.Step1:
+                    case WizardState.Step3:
+                    case WizardState.Step4:
+                    case WizardState.Undefined:
+                    default:
+                        {
+                            this.Close();
+                        }
+                        break;
+                }
+            }
+            else if (this.Type == WizardType.pEp)
+            {
+                // TODO: Navigation logic for pEp sync
+                this.Close();
+            }
+            else
+            {
+                this.Close();
+            }
+        }
+
+        /// <summary>
+        /// Raises the property changed event, if possible, with the given arguments.
+        /// </summary>
+        /// <param name="propertyName">The name of the property that changed.</param>
+        private void RaisePropertyChangedEvent(string propertyName)
+        {
+            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+            return;
+        }
+
+        /// <summary>
+        /// Event handler for when a sync message arrives while the wizard is open and in progress.
+        /// </summary>
+        private void MsgProcessor_SyncMessageReceived(object sender, MsgProcessor.SyncMessageEventArgs e)
+        {
+            // Check message type
+            if ((e.Message != null) &&
+                (e.MessageType == MsgProcessor.SyncMessageEventArgs.MessageTypes.PublicKey))
+            {
+                // Marshall to main thread
+                this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
+                {
+                    // Public key message received. Show trustwords.
+                    PEPIdentity myself = new PEPIdentity(ThisAddIn.PEPEngine.Myself(e.Message.To[0].ToCOMType()));
+                    PEPIdentity partner = myself.Copy();
+                    string[] keys = e.Message?.KeyList.Split(',');
+                    string partnerKey  = null;
+                    foreach (var key in keys)
+                    {
+                        if (key?.Equals(myself?.Fingerprint) == false)
+                        {
+                            partnerKey = key;
+                            break;
+                        }
+                    }
+                    partner.Fingerprint = partnerKey;
+                    this.KeysToImport.Add(partnerKey);
+
+                    HandshakeItem item;
+                    if (HandshakeItem.Create(myself, partner, false, out item) == Globals.ReturnStatus.Success)
+                    {
+                        item.AreTabControlsVisible = true;
+                        item.ActiveTab = HandshakeItem.Tabs.Fingerprint;
+                        item.AreHandshakeButtonsVisible = false;
+                        this.Items.Add(item);
+
+                        this.GoToNextStep();
+                    }
+                    else
+                    {
+                        this.GoToLastStep(false);
+                    }
+
+                }));
+            }
+            else if ((e.Message != null) &&
+                     (e.MessageType == MsgProcessor.SyncMessageEventArgs.MessageTypes.PrivateKey))
+            {
+                // Marshall to main thread
+                this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
+                {
+                    // Private key message received. Set key as default.
+                    try
+                    {
+                        ThisAddIn.PEPEngine.SetOwnKey(this._SyncPartner.ToCOMType(), this.KeysToImport[0]);
+                        this.GoToLastStep(true);
+                    }
+                    catch (Exception ex)
+                    {
+                        this.GoToLastStep(false);
+                        Log.Error("MsgProcessor_SyncMessageReceived: Error trusting partner key. " + ex.ToString());
+                    }
+                }));
+            }
+            else
+            {
+                Log.Error("MsgProcessor_SyncMessageReceived: Invalid data received. Message is " + ((e.Message != null) ? "not" : "" + " null. Message type is " + Enum.GetName(typeof(MsgProcessor.SyncMessageEventArgs.MessageTypes), e.MessageType)));
+            }
+        }
+
+        /// <summary>
+        /// Event handler for when the window is closed.
+        /// </summary>
+        private void Window_Closed(object sender, EventArgs e)
+        {
+            // Remove event handler
+            MsgProcessor.SyncMessageReceived += MsgProcessor_SyncMessageReceived;
+
+            // Unset flag
+            KeySyncWizard.WizardInProcess = false;
+
+            // Remove static reference
+            KeySyncWizard.currentWizard = null;
+        }
+
+        #endregion
+
+        #region Methods
+        /**************************************************************
+         * 
+         * Methods
+         * 
+         *************************************************************/
+
+        /// <summary>
+        /// Navigates to the last step of the wizard.
+        /// </summary>
+        /// <param name="success">Whether or not the wizard has been completed successfully.</param>
+        private void GoToLastStep(bool success)
+        {
+            // Go to last step
+            this.State = WizardState.Step4;
+            this.Success = success;
+
+            // Update content
+            this.UpdateContent();
+        }
+
+        /// <summary>
+        /// Navigates to the next step of the wizard.
+        /// </summary>
+        private void GoToNextStep()
+        {
+            // Go to next step
+            this.State++;
+
+            // Update content
+            this.UpdateContent();
+        }
+
+        /// <summary>
+        /// Initializes the wizard.
+        /// </summary>
+        private void InitializeWizard(WizardType type)
+        {
+            // Set initial state
+            this.Type = type;
+            this.DataContext = this;
+            this.State = WizardState.Step1;
+            this.UpdateContent();
+        }
+
+        /// <summary>
+        /// Sends the initial message to myself to start the sync process.
+        /// </summary>
+        private void SendInitialMessage()
+        {
+            PEPIdentity ownIdentity = null;
+            Outlook.Account account = null;
+            Outlook.Accounts accounts = null;
+            Outlook.Store deliveryStore = null;
+            Outlook.Explorer explorer = null;
+            Outlook.Folder folder = null;
+            Outlook.NameSpace ns = null;
+
+            // Try to get own identity for current account
+            try
+            {
+                explorer = Globals.ThisAddIn.Application.ActiveExplorer();
+                folder = explorer?.CurrentFolder as Outlook.Folder;
+
+                ns = Globals.ThisAddIn.Application.Session;
+                accounts = ns.Accounts;
+
+                // Note: Index starts at 1
+                for (int i = 1; i <= accounts.Count; i++)
+                {
+                    account = accounts[i];
+
+                    // Getting delivery store can fail for new accounts when Outlook is not restarted
+                    try
+                    {
+                        deliveryStore = account.DeliveryStore;
+                    }
+                    catch (Exception ex)
+                    {
+                        deliveryStore = null;
+                        Log.Error("InitializeWizard: Error getting delivery store. " + ex.ToString());
+                    }
+
+                    if (deliveryStore?.StoreID?.Equals(folder?.StoreID) == true)
+                    {
+                        if (PEPIdentity.GetOwnIdentity(account, out ownIdentity) != Globals.ReturnStatus.Success)
+                        {
+                            ownIdentity = null;
+                            Log.Error("InitializeWizard: Error getting own identity. ReturnStatus != Success.");
+                        }
+                        break;
+                    }
+
+                    account = null;
+                    deliveryStore = null;
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Error("InitializeWizard: Error getting default account. " + ex.ToString());
+            }
+            finally
+            {
+                account = null;
+                accounts = null;
+                deliveryStore = null;
+                explorer = null;
+                folder = null;
+                ns = null;
+            }
+
+            // If we have an own identity, send initial message
+            if (ownIdentity != null)
+            {
+                PEPMessage message = new PEPMessage
+                {
+                    From = ownIdentity,
+                    ShortMsg = "pEp",
+                    ForceUnencrypted = true
+                };
+                message.To.Add(ownIdentity);
+
+                Globals.ThisAddIn.CreateAndSendMessage(message, true, true, true);
+            }
+        }
+
+        /// <summary>
+        /// Trusts the communication partner key (makes the channel green)
+        /// </summary>
+        private void TrustKey()
+        {
+            if (this.Items?.Count == 1)
+            {
+                try
+                {
+                    this._SyncPartner = this.Items[0].Partner;
+                    pEpIdentity partner = this._SyncPartner.ToCOMType();
+                    ThisAddIn.PEPEngine.TrustPersonalKey(partner);
+
+                    this.GoToNextStep();
+                }
+                catch (Exception ex)
+                {
+                    this.GoToLastStep(false);
+                    Log.Error("TrustPartnerKey: Error trusting partner key. " + ex.ToString());
+                }
+            }
+            else
+            {
+                this.GoToLastStep(false);
+                Log.Error("TrustPartnerKey: Error trusting partner key. Items.Count = " + this.Items?.Count);
+            }
+        }
+
+        /// <summary>
+        /// Updates the dialog content according to its current state.
+        /// </summary>
+        private void UpdateContent()
+        {
+            // Set content according to state
+            switch (this.State)
+            {
+                case WizardState.Step1:
+                    this.NextButtonText = Properties.Resources.KeySyncWizard_Next;
+                    this.ExplanationText = Properties.Resources.KeySyncWizard_Step1PGPExplanationText;
+                    break;
+                case WizardState.Step2:
+                    this.NextButtonText = Properties.Resources.KeySyncWizard_Next;
+                    this.ExplanationText = Properties.Resources.KeySyncWizard_Step2PGPExplanationText;
+                    break;
+                case WizardState.Step3:
+                    this.NextButtonText = Properties.Resources.Handshake_ConfirmFingerprint;
+                    this.ExplanationText = Properties.Resources.KeySyncWizard_Step3PGPExplanationText;
+                    break;
+                case WizardState.Step4:
+                    this.NextButtonText = Properties.Resources.KeySyncWizard_Next;
+                    this.ExplanationText = (this.Success ? Properties.Resources.KeySyncWizard_Step4SuccessExplanationText : Properties.Resources.KeySyncWizard_Step4ErrorExplanationText);
+                    break;
+                case WizardState.Undefined:
+                default:
+                    this.NextButtonText = Properties.Resources.KeySyncWizard_Finish;
+                    this.ExplanationText = string.Empty;
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Resets the object to its defaults.
+        /// </summary>
+        public void Reset()
+        {
+            this._KeysToExport = new ObservableCollection<string>();
+            this._KeysToImport = new ObservableCollection<string>();
+            this._Items = new ObservableCollection<HandshakeItem>();
+            this._Type = WizardType.Undefined;
+            this._State = WizardState.Undefined;
+            this._Success = false;
+            this._SyncPartner = null;
+        }
+
+        #endregion
+    }
+}
--- a/UI/RibbonCustomizations.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/RibbonCustomizations.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -663,7 +663,7 @@
                 }
             }
 
-            return visible;                    
+            return visible;
         }
 
         ///////////////////////////////////////////////////////////
@@ -887,6 +887,14 @@
         }
 
         /// <summary>
+        /// Callback to get the image for the ButtonCompatibility.
+        /// </summary>
+        public System.Drawing.Bitmap ButtonSync_GetImage(Office.IRibbonControl control)
+        {
+            return (Properties.Resources.ImageBackstageCompatibility);
+        }
+
+        /// <summary>
         /// Callback to get the image for the ImageControlLogo.
         /// </summary>
         public System.Drawing.Bitmap ImageControlLogo_GetImage(Office.IRibbonControl control)
@@ -911,6 +919,14 @@
         }
 
         /// <summary>
+        /// Callback to get the helper text for the GroupSync.
+        /// </summary>
+        public string GroupSync_GetHelperText(Office.IRibbonControl control)
+        {
+            return (Properties.Resources.Ribbon_GroupSyncHelperText);
+        }
+
+        /// <summary>
         /// Callback to get the label text for the GroupAccounts.
         /// </summary>
         public string GroupAccounts_GetLabel(Office.IRibbonControl control)
@@ -927,6 +943,14 @@
         }
 
         /// <summary>
+        /// Callback to get the label text for the GroupSync.
+        /// </summary>
+        public string GroupSync_GetLabel(Office.IRibbonControl control)
+        {
+            return (Properties.Resources.Ribbon_GroupSyncLabel);
+        }
+
+        /// <summary>
         /// Callback to get the label text for the ButtonAccounts.
         /// </summary>
         public string ButtonAccounts_GetLabel(Office.IRibbonControl control)
@@ -943,6 +967,14 @@
         }
 
         /// <summary>
+        /// Callback to get the label text for the ButtonSync.
+        /// </summary>
+        public string ButtonSync_GetLabel(Office.IRibbonControl control)
+        {
+            return (Properties.Resources.Ribbon_ButtonSyncLabel);
+        }
+
+        /// <summary>
         /// Callback to get the label text for the LabelControlName.
         /// </summary>
         public string LabelControlName_GetLabel(Office.IRibbonControl control)
@@ -1063,6 +1095,7 @@
 
             return;
         }
+
         /**************************************************************
          *
          * Event Handling (Backstage)
@@ -1099,6 +1132,16 @@
             return;
         }
 
+        /// <summary>
+        /// Event handler for when the KeysSyncWizard button is clicked.
+        /// This will open the wizard.
+        /// </summary>
+        public void ButtonSync_Click(Office.IRibbonControl control)
+        {
+            var wizard = new KeySyncWizard(KeySyncWizard.WizardType.PGP);
+            wizard.Show();
+        }
+
         #region IRibbonExtensibility Members
 
         public string GetCustomUI(string ribbonID)
--- a/UI/RibbonCustomizationsExplorer.xml	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/RibbonCustomizationsExplorer.xml	Tue Apr 10 10:50:14 2018 +0200
@@ -71,6 +71,18 @@
                     getLabel="ButtonCompatibility_GetLabel"/>
           </primaryItem>
         </group>
+        <!-- Key sync button -->
+        <group id="GroupSync"
+               getLabel="GroupSync_GetLabel"
+               getHelperText="GroupSync_GetHelperText">
+          <primaryItem>
+            <button id="ButtonSync"
+                    isDefinitive="true"
+                    onAction="ButtonSync_Click"
+                    getImage="ButtonSync_GetImage"
+                    getLabel="ButtonSync_GetLabel"/>
+          </primaryItem>
+        </group>
       </firstColumn>
       <secondColumn>
         <!-- About -->
--- a/UI/ValueConverters.cs	Wed Apr 04 10:03:36 2018 +0200
+++ b/UI/ValueConverters.cs	Tue Apr 10 10:50:14 2018 +0200
@@ -276,6 +276,61 @@
     }
 
     /// <summary>
+    /// Returns a bool value indicating if the wizard state matches any of the given parameter.
+    /// </summary>
+    public class IsWizardStateConverter : IValueConverter
+    {
+        public object Convert(object value,
+                              Type targetType,
+                              object parameter,
+                              CultureInfo culture)
+        {
+            bool success;
+            KeySyncWizard.WizardState param;
+
+            if ((value is KeySyncWizard.WizardState) &&
+                (parameter is string))
+            {
+                string[] parameters = (parameter as string).Split('|');
+
+                if (parameters?.Length > 0)
+                {
+                    foreach (var p in parameters)
+                    {
+                        success = Enum.TryParse(p, out param);
+
+                        if (success)
+                        {
+                            if (((KeySyncWizard.WizardState)value) == param)
+                            {
+                                return true;
+                            }
+                        }
+                        else
+                        {
+                            throw new ArgumentException();
+                        }
+                    }
+                }
+
+                return false;
+            }
+            else
+            {
+                throw new ArgumentException();
+            }
+        }
+
+        public object ConvertBack(object value,
+                                  Type targetType,
+                                  object parameter,
+                                  CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+
+    /// <summary>
     /// Converter to check if a list is empty.
     /// </summary>
     public class IsListEmptyConverter : IValueConverter
--- a/pEpForOutlook.csproj	Wed Apr 04 10:03:36 2018 +0200
+++ b/pEpForOutlook.csproj	Tue Apr 10 10:50:14 2018 +0200
@@ -400,6 +400,12 @@
       <DependentUpon>HandshakeDialog.xaml</DependentUpon>
     </Compile>
     <Compile Include="UI\HandshakeItem.cs" />
+    <Compile Include="UI\HandshakeItemsControl.xaml.cs">
+      <DependentUpon>HandshakeItemsControl.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="UI\KeySyncWizard.xaml.cs">
+      <DependentUpon>KeySyncWizard.xaml</DependentUpon>
+    </Compile>
     <Compile Include="UI\RibbonCustomizations.cs" />
     <Compile Include="UI\FormManagePrivacyStatus.cs">
       <SubType>Form</SubType>
@@ -599,6 +605,14 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="UI\HandshakeItemsControl.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="UI\KeySyncWizard.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="UI\PrivacyView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>