浏览代码

Add ability to examen server public key/fingerprint

olegkap_cp 14 年之前
父节点
当前提交
96c512fddb

+ 4 - 1
Renci.SshClient/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -115,6 +115,9 @@
     <Compile Include="..\Renci.SshNet\Common\Extensions.cs">
       <Link>Common\Extensions.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\HostKeyEventArgs.cs">
+      <Link>Common\HostKeyEventArgs.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\NetConfServerException.cs">
       <Link>Common\NetConfServerException.cs</Link>
     </Compile>
@@ -715,7 +718,7 @@
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 4 - 1
Renci.SshClient/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj

@@ -129,6 +129,9 @@
     <Compile Include="..\Renci.SshNet\Common\Extensions.cs">
       <Link>Common\Extensions.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\HostKeyEventArgs.cs">
+      <Link>Common\HostKeyEventArgs.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\ObjectIdentifier.cs">
       <Link>Common\ObjectIdentifier.cs</Link>
     </Compile>
@@ -694,7 +697,7 @@
       <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
         <SilverlightProjectProperties />
       </FlavorProperties>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 4 - 1
Renci.SshClient/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj

@@ -125,6 +125,9 @@
     <Compile Include="..\Renci.SshNet.Silverlight\Common\Extensions.SilverlightShared.cs">
       <Link>Common\Extensions.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Common\HostKeyEventArgs.cs">
+      <Link>Common\HostKeyEventArgs.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Common\ObjectIdentifier.cs">
       <Link>Common\ObjectIdentifier.cs</Link>
     </Compile>
@@ -696,7 +699,7 @@
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 17 - 1
Renci.SshClient/Renci.SshNet/BaseClient.cs

@@ -74,6 +74,11 @@ namespace Renci.SshNet
         /// </summary>
         public event EventHandler<ExceptionEventArgs> ErrorOccurred;
 
+        /// <summary>
+        /// Occurs when host key received.
+        /// </summary>
+        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseClient"/> class.
         /// </summary>
@@ -101,8 +106,9 @@ namespace Renci.SshNet
             }
 
             this.Session = new Session(this.ConnectionInfo);
-            this.Session.Connect();
+            this.Session.HostKeyReceived += Session_HostKeyReceived;
             this.Session.ErrorOccured += Session_ErrorOccured;
+            this.Session.Connect();
 
             this.OnConnected();
         }
@@ -186,6 +192,14 @@ namespace Renci.SshNet
             }
         }
 
+        private void Session_HostKeyReceived(object sender, HostKeyEventArgs e)
+        {
+            if (this.HostKeyReceived != null)
+            {
+                this.HostKeyReceived(this, e);
+            }
+        }
+
         #region IDisposable Members
 
         private bool _isDisposed = false;
@@ -215,12 +229,14 @@ namespace Renci.SshNet
                 {
                     // Dispose managed ResourceMessages.
                     this.Session.ErrorOccured -= Session_ErrorOccured;
+                    this.Session.HostKeyReceived -= Session_HostKeyReceived;
 
                     if (this.Session != null)
                     {
                         this.Session.Dispose();
                         this.Session = null;
                     }
+
                     if (this._keepAliveTimer != null)
                     {
                         this._keepAliveTimer.Dispose();

+ 29 - 0
Renci.SshClient/Renci.SshNet/Common/HostKeyEventArgs.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Renci.SshNet.Security.Cryptography;
+
+namespace Renci.SshNet.Common
+{
+    public class HostKeyEventArgs : EventArgs
+    {
+        public bool CanTrust { get; set; }
+
+        public byte[] HostKey { get; private set; }
+
+        public byte[] FingerPrint { get; private set; }
+
+        public HostKeyEventArgs(byte[] hostKey)
+        {
+            this.CanTrust = true;   //  Set default value
+
+            this.HostKey = hostKey;
+
+            using (var md5 = new MD5Hash())
+            {
+                this.FingerPrint = md5.ComputeHash(hostKey);
+            }
+        }
+    }
+}

+ 1 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -69,6 +69,7 @@
     <Compile Include="Common\ChannelEventArgs.cs" />
     <Compile Include="Common\ChannelOpenFailedEventArgs.cs" />
     <Compile Include="Common\ChannelRequestEventArgs.cs" />
+    <Compile Include="Common\HostKeyEventArgs.cs" />
     <Compile Include="Common\NetConfServerException.NET40.cs" />
     <Compile Include="Common\DerData.cs" />
     <Compile Include="Common\ExceptionEventArgs.cs" />

+ 23 - 10
Renci.SshClient/Renci.SshNet/Security/KeyExchange.cs

@@ -61,6 +61,11 @@ namespace Renci.SshNet.Security
             }
         }
 
+        /// <summary>
+        /// Occurs when host key received.
+        /// </summary>
+        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
+
         /// <summary>
         /// Starts key exchange algorithm
         /// </summary>
@@ -208,11 +213,6 @@ namespace Renci.SshNet.Security
             //  Resolve Session ID
             var sessionId = this.Session.SessionId ?? this.ExchangeHash;
 
-            //  Create server HMac
-            //var serverHMac = this._serverHmacAlgorithmType.CreateInstance<HMac>();
-
-            //serverHMac.Init(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)));
-
             //return serverHMac;
             return this._serverHmacAlgorithmType(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)));
         }
@@ -226,11 +226,6 @@ namespace Renci.SshNet.Security
             //  Resolve Session ID
             var sessionId = this.Session.SessionId ?? this.ExchangeHash;
 
-            //  Create client HMac
-            //var clientHMac = this._cientHmacAlgorithmType.CreateInstance<HMac>();
-
-            //clientHMac.Init(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)));
-
             //return clientHMac;
             return this._cientHmacAlgorithmType(this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)));
         }
@@ -267,6 +262,24 @@ namespace Renci.SshNet.Security
             return decompressor;
         }
 
+        /// <summary>
+        /// Determines whether the specified host key can be trusted.
+        /// </summary>
+        /// <param name="hostKey">The host key.</param>
+        /// <returns>
+        ///   <c>true</c> if the specified host key can be trusted; otherwise, <c>false</c>.
+        /// </returns>
+        protected bool CanTrustHostKey(byte[] hostKey)
+        {
+            var args = new HostKeyEventArgs(hostKey);
+
+            if (this.HostKeyReceived != null)
+            {
+                this.HostKeyReceived(this, args);
+            }
+
+            return args.CanTrust;
+        }
 
         /// <summary>
         /// Validates the exchange hash.

+ 12 - 5
Renci.SshClient/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs

@@ -71,15 +71,22 @@ namespace Renci.SshNet.Security
         /// </returns>
         protected override bool ValidateExchangeHash()
         {
-            var exchangeHash = this.CalculateHash();
+            if (this.CanTrustHostKey(this._hostKey))
+            {
+                var exchangeHash = this.CalculateHash();
 
-            var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]);
+                var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]);
 
-            var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length);
+                var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length);
 
-            var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey);
+                var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey);
 
-            return key.VerifySignature(exchangeHash, this._signature);
+                return key.VerifySignature(exchangeHash, this._signature);
+            }
+            else
+            {
+                return false;
+            }
         }
 
         /// <summary>

+ 18 - 1
Renci.SshClient/Renci.SshNet/Session.cs

@@ -247,6 +247,11 @@ namespace Renci.SshNet
         /// </summary>
         public event EventHandler<EventArgs> Disconnected;
 
+        /// <summary>
+        /// Occurs when host key received.
+        /// </summary>
+        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
+
         #region Message events
 
         /// <summary>
@@ -625,7 +630,7 @@ namespace Renci.SshNet
             var index = EventWaitHandle.WaitAny(waitHandles, this.ConnectionInfo.Timeout);
 
             if (index < 1)
-            {
+            {                
                 throw this._exception;
             }
             else if (index > 1)
@@ -1122,6 +1127,8 @@ namespace Renci.SshNet
             //  Create instance of key exchange algorithm that will be used
             this._keyExchange = this.ConnectionInfo.KeyExchangeAlgorithms[keyExchangeAlgorithmName].CreateInstance<KeyExchange>();
 
+            this._keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
+
             //  Start the algorithm implementation
             this._keyExchange.Start(this, message);
 
@@ -1167,6 +1174,7 @@ namespace Renci.SshNet
             //  Dispose of old KeyExchange object as it is no longer needed.
             if (this._keyExchange != null)
             {
+                this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
                 this._keyExchange.Dispose();
                 this._keyExchange = null;
             }
@@ -1417,6 +1425,14 @@ namespace Renci.SshNet
 
         #endregion
 
+        private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e)
+        {
+            if (this.HostKeyReceived != null)
+            {
+                this.HostKeyReceived(this, e);
+            }
+        }
+
         /// <summary>
         /// Reads the specified length of bytes from the server
         /// </summary>
@@ -1624,6 +1640,7 @@ namespace Renci.SshNet
 
                     if (this._keyExchange != null)
                     {
+                        this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived;
                         this._keyExchange.Dispose();
                         this._keyExchange = null;
                     }