⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssh2connection.cs

📁 Granados是一个基于.NET的SSH客户端库。同时支持SSH1和SSH2。实现了AES, Blowfish, TripleDES, RSA, DSA等加密验证算法。实现TCP协议连接。
💻 CS
📖 第 1 页 / 共 3 页
字号:
			FINISHED
		}
		private Status _status;

		private BigInteger _x;
		private BigInteger _e;
		private BigInteger _k;
		private byte[] _hash;
		private byte[] _sessionID;

		//results
		Cipher _rc;
		Cipher _tc;
		MAC _rm;
		MAC _tm;

		private void TransmitPacket(byte[] payload) {
			_con.TransmitPacket(payload);
		}

		public KeyExchanger(SSH2Connection con, byte[] sessionID) {
			_con = con;
			_param = con.Param;
			_cInfo = (SSH2ConnectionInfo)con.ConnectionInfo;
			_sessionID = sessionID;
			_status = Status.INITIAL;
		}

		public bool SynchronousKexExchange() {
			SendKEXINIT();
			ProcessKEXINIT(_con.ReceivePacket());
			SendKEXDHINIT();
			if(!ProcessKEXDHREPLY(_con.ReceivePacket())) return false;
			SendNEWKEYS();
			ProcessNEWKEYS(_con.ReceivePacket());
			return true;
		}
		public void AsyncStartReexchange() {
			_startedByHost = false;
			_status = Status.WAIT_KEXINIT;
			SendKEXINIT();
		}
		public void AsyncProcessPacket(SSH2Packet packet) {
			switch(_status) {
				case Status.INITIAL:
					_startedByHost = true;
					ProcessKEXINIT(packet);
					SendKEXINIT();
					SendKEXDHINIT();
					break;
				case Status.WAIT_KEXINIT:
					ProcessKEXINIT(packet);
					SendKEXDHINIT();
					break;
				case Status.WAIT_KEXDH_REPLY:
					ProcessKEXDHREPLY(packet);
					SendNEWKEYS();
					break;
				case Status.WAIT_NEWKEYS:
					ProcessNEWKEYS(packet);
					Debug.Assert(_status==Status.FINISHED);
					break;
			}
		}
					
		private void SendKEXINIT() {
			SSH2DataWriter wr = new SSH2DataWriter();
			wr.WritePacketType(PacketType.SSH_MSG_KEXINIT);
			byte[] cookie = new byte[16];
			_param.Random.NextBytes(cookie);
			wr.Write(cookie);
			wr.Write("diffie-hellman-group1-sha1"); //    kex_algorithms
			wr.Write(FormatHostKeyAlgorithmDescription());            //    server_host_key_algorithms
			wr.Write(FormatCipherAlgorithmDescription());      //    encryption_algorithms_client_to_server
			wr.Write(FormatCipherAlgorithmDescription());      //    encryption_algorithms_server_to_client
			wr.Write("hmac-sha1");                  //    mac_algorithms_client_to_server
			wr.Write("hmac-sha1");                  //    mac_algorithms_server_to_client
			wr.Write("none");                       //    compression_algorithms_client_to_server
			wr.Write("none");                       //    compression_algorithms_server_to_client
			wr.Write("");                           //    languages_client_to_server
			wr.Write("");                           //    languages_server_to_client
			wr.Write(false); //Indicates whether a guessed key exchange packet follows
			wr.Write(0);       //reserved for future extension

			_clientKEXINITPayload = wr.ToByteArray();
			_status = Status.WAIT_KEXINIT;
			TransmitPacket(_clientKEXINITPayload);
		}
		private void ProcessKEXINIT(SSH2Packet packet) {
			_serverKEXINITPayload = packet.Data;
			SSH2DataReader re = new SSH2DataReader(_serverKEXINITPayload);
			byte[] head = re.Read(17); //Type and cookie
			if(head[0]!=(byte)PacketType.SSH_MSG_KEXINIT) throw new SSHException(String.Format("Server response is not SSH_MSG_KEXINIT but {0}", head[0]));
			Encoding enc = Encoding.ASCII;
			
			string kex = enc.GetString(re.ReadString());
			_cInfo._supportedKEXAlgorithms = kex;
			CheckAlgorithmSupport("keyexchange", kex, "diffie-hellman-group1-sha1");
			
			string host_key = enc.GetString(re.ReadString());
			_cInfo._supportedHostKeyAlgorithms = host_key;
			_cInfo._algorithmForHostKeyVerification = DecideHostKeyAlgorithm(host_key);
			
			string enc_cs = enc.GetString(re.ReadString());
			_cInfo._supportedCipherAlgorithms = enc_cs;
			_cInfo._algorithmForTransmittion = DecideCipherAlgorithm(enc_cs);
			
			string enc_sc = enc.GetString(re.ReadString());
			_cInfo._algorithmForReception = DecideCipherAlgorithm(enc_sc);

			string mac_cs = enc.GetString(re.ReadString());
			CheckAlgorithmSupport("mac", mac_cs, "hmac-sha1");
			
			string mac_sc = enc.GetString(re.ReadString());
			CheckAlgorithmSupport("mac", mac_sc, "hmac-sha1");
			
			string comp_cs = enc.GetString(re.ReadString());
			CheckAlgorithmSupport("compression", comp_cs, "none");
			string comp_sc = enc.GetString(re.ReadString());
			CheckAlgorithmSupport("compression", comp_sc, "none");
			
			string lang_cs = enc.GetString(re.ReadString());
			string lang_sc = enc.GetString(re.ReadString());
			bool flag = re.ReadBool();
			int reserved = re.ReadInt32();
			Debug.Assert(re.Rest==0);
			if(flag) throw new SSHException("Algorithm negotiation failed"); 
		}


		private void SendKEXDHINIT() {
			//Round1 computes and sends [e]
			byte[] sx = new byte[16];
			_param.Random.NextBytes(sx);
			_x = new BigInteger(sx);
			_e = new BigInteger(2).modPow(_x, DH_PRIME);
			SSH2DataWriter wr = new SSH2DataWriter();
			wr.WritePacketType(PacketType.SSH_MSG_KEXDH_INIT);
			wr.Write(_e);
			_status = Status.WAIT_KEXDH_REPLY;
			TransmitPacket(wr.ToByteArray());
		}
		private bool ProcessKEXDHREPLY(SSH2Packet packet) {
			//Round2 receives response
			SSH2DataReader re = new SSH2DataReader(packet.Data);
			PacketType h = re.ReadPacketType();
			if(h!=PacketType.SSH_MSG_KEXDH_REPLY) throw new SSHException(String.Format("KeyExchange response is not KEXDH_REPLY but {0}", h));
			byte[] key_and_cert = re.ReadString();
			BigInteger f = re.ReadMPInt();
			byte[] signature = re.ReadString();
			Debug.Assert(re.Rest==0);

			//Round3 calc hash H
			SSH2DataWriter wr = new SSH2DataWriter();
			_k = f.modPow(_x, DH_PRIME);
			wr = new SSH2DataWriter();
			wr.Write(_cInfo._clientVersionString);
			wr.Write(_cInfo._serverVersionString);
			wr.WriteAsString(_clientKEXINITPayload);
			wr.WriteAsString(_serverKEXINITPayload);
			wr.WriteAsString(key_and_cert);
			wr.Write(_e);
			wr.Write(f);
			wr.Write(_k);
			_hash = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());

			if(!VerifyHostKey(key_and_cert, signature, _hash)) return false;

			//Debug.WriteLine("hash="+DebugUtil.DumpByteArray(hash));
			if(_sessionID==null) _sessionID = _hash;
			return true;
		}
		private void SendNEWKEYS() {
			_status = Status.WAIT_NEWKEYS;
			_newKeyEvent = new ManualResetEvent(false);
			TransmitPacket(new byte[1] {(byte)PacketType.SSH_MSG_NEWKEYS});

			//establish Ciphers
			_tc = CipherFactory.CreateCipher(SSHProtocol.SSH2, _cInfo._algorithmForTransmittion,
				DeriveKey(_k, _hash, 'C', CipherFactory.GetKeySize(_cInfo._algorithmForTransmittion)), DeriveKey(_k, _hash, 'A', CipherFactory.GetBlockSize(_cInfo._algorithmForTransmittion)));
			_rc = CipherFactory.CreateCipher(SSHProtocol.SSH2, _cInfo._algorithmForReception,
				DeriveKey(_k, _hash, 'D', CipherFactory.GetKeySize(_cInfo._algorithmForReception)), DeriveKey(_k, _hash, 'B', CipherFactory.GetBlockSize(_cInfo._algorithmForReception)));

			//establish MACs
			MACAlgorithm ma = MACAlgorithm.HMACSHA1;
			_tm = MACFactory.CreateMAC(MACAlgorithm.HMACSHA1, DeriveKey(_k, _hash, 'E', MACFactory.GetSize(ma)));
			_rm = MACFactory.CreateMAC(MACAlgorithm.HMACSHA1, DeriveKey(_k, _hash, 'F', MACFactory.GetSize(ma)));
			_newKeyEvent.Set();
		}
		private void ProcessNEWKEYS(SSH2Packet packet) {
			//confirms new key
			try {
				byte[] response = packet.Data;
				if(response.Length!=1 || response[0]!=(byte)PacketType.SSH_MSG_NEWKEYS) throw new SSHException("SSH_MSG_NEWKEYS failed");

				_newKeyEvent.WaitOne();
				_newKeyEvent.Close();
				_con.LockCommunication();
				_con.RefreshKeys(_sessionID, _tc, _rc, _tm, _rm);
				_status = Status.FINISHED;
			}
			finally {
				_con.UnlockCommunication();
			}
		}

		private bool VerifyHostKey(byte[] K_S, byte[] signature, byte[] hash) {
			SSH2DataReader re1 = new SSH2DataReader(K_S);
			string algorithm = Encoding.ASCII.GetString(re1.ReadString());
			if(algorithm!=SSH2Util.PublicKeyAlgorithmName(_cInfo._algorithmForHostKeyVerification))
				throw new SSHException("Protocol Error: Host Key Algorithm Mismatch");

			SSH2DataReader re2 = new SSH2DataReader(signature);
			algorithm = Encoding.ASCII.GetString(re2.ReadString());
			if(algorithm!=SSH2Util.PublicKeyAlgorithmName(_cInfo._algorithmForHostKeyVerification))
				throw new SSHException("Protocol Error: Host Key Algorithm Mismatch");
			byte[] sigbody = re2.ReadString();
			Debug.Assert(re2.Rest==0);

			if(_cInfo._algorithmForHostKeyVerification==PublicKeyAlgorithm.RSA)
				VerifyHostKeyByRSA(re1, sigbody, hash);
			else if(_cInfo._algorithmForHostKeyVerification==PublicKeyAlgorithm.DSA)
				VerifyHostKeyByDSS(re1, sigbody, hash);
			else
				throw new SSHException("Bad host key algorithm "+_cInfo._algorithmForHostKeyVerification);

			//ask the client whether he accepts the host key
			if(!_startedByHost && _param.KeyCheck!=null && !_param.KeyCheck(_cInfo))
				return false;
			else
				return true;
		}

		private void VerifyHostKeyByRSA(SSH2DataReader pubkey, byte[] sigbody, byte[] hash) {
			BigInteger exp = pubkey.ReadMPInt();
			BigInteger mod = pubkey.ReadMPInt();
			Debug.Assert(pubkey.Rest==0);

			//Debug.WriteLine(exp.ToHexString());
			//Debug.WriteLine(mod.ToHexString());

			RSAPublicKey pk = new RSAPublicKey(exp, mod);
			pk.VerifyWithSHA1(sigbody, new SHA1CryptoServiceProvider().ComputeHash(hash));
			_cInfo._hostkey = pk;
		}

		private void VerifyHostKeyByDSS(SSH2DataReader pubkey, byte[] sigbody, byte[] hash) {
			BigInteger p = pubkey.ReadMPInt();
			BigInteger q = pubkey.ReadMPInt();
			BigInteger g = pubkey.ReadMPInt();
			BigInteger y = pubkey.ReadMPInt();
			Debug.Assert(pubkey.Rest==0);

			//Debug.WriteLine(p.ToHexString());
			//Debug.WriteLine(q.ToHexString());
			//Debug.WriteLine(g.ToHexString());
			//Debug.WriteLine(y.ToHexString());


			DSAPublicKey pk = new DSAPublicKey(p,g,q,y);
			pk.Verify(sigbody, new SHA1CryptoServiceProvider().ComputeHash(hash));
			_cInfo._hostkey = pk;
		}

		private byte[] DeriveKey(BigInteger key, byte[] hash, char ch, int length) {
			byte[] result = new byte[length];

			SSH2DataWriter wr = new SSH2DataWriter();
			wr.Write(key);
			wr.Write(hash);
			wr.Write((byte)ch);
			wr.Write(_sessionID);
			byte[] h1 = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());
			if(h1.Length >= length) {
				Array.Copy(h1, 0, result, 0, length);
				return result;
			}
			else {
				wr = new SSH2DataWriter();
				wr.Write(key);
				wr.Write(_sessionID);
				wr.Write(h1);
				byte[] h2 = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());
				if(h1.Length+h2.Length >= length) {
					Array.Copy(h1, 0, result, 0, h1.Length);
					Array.Copy(h2, 0, result, h1.Length, length-h1.Length);
					return result;
				}
				else
					throw new SSHException("necessary key length is too big"); //long key is not supported
			}
		}

		private static void CheckAlgorithmSupport(string title, string data, string algorithm_name) {
			string[] t = data.Split(',');
			foreach(string s in t) {
				if(s==algorithm_name) return; //found!
			}
			throw new SSHException("Server does not support "+algorithm_name+" for "+title);
		}
		private PublicKeyAlgorithm DecideHostKeyAlgorithm(string data) {
			string[] t = data.Split(',');
			foreach(PublicKeyAlgorithm a in _param.PreferableHostKeyAlgorithms) {
				if(SSHUtil.ContainsString(t, SSH2Util.PublicKeyAlgorithmName(a))) {
					return a;
				}
			}
			throw new SSHException("The negotiation of host key verification algorithm is failed");
		}
		private CipherAlgorithm DecideCipherAlgorithm(string data) {
			string[] t = data.Split(',');
			foreach(CipherAlgorithm a in _param.PreferableCipherAlgorithms) {
				if(SSHUtil.ContainsString(t, CipherFactory.AlgorithmToSSH2Name(a))) {
					return a;
				}
			}
			throw new SSHException("The negotiation of encryption algorithm is failed");
		}
		private string FormatHostKeyAlgorithmDescription() {
			StringBuilder b = new StringBuilder();
			if(_param.PreferableHostKeyAlgorithms.Length==0) throw new SSHException("HostKeyAlgorithm is not set");
			b.Append(SSH2Util.PublicKeyAlgorithmName(_param.PreferableHostKeyAlgorithms[0]));
			for(int i=1; i<_param.PreferableHostKeyAlgorithms.Length; i++) {
				b.Append(',');
				b.Append(SSH2Util.PublicKeyAlgorithmName(_param.PreferableHostKeyAlgorithms[i]));
			}
			return b.ToString();
		}
		private string FormatCipherAlgorithmDescription() {
			StringBuilder b = new StringBuilder();
			if(_param.PreferableCipherAlgorithms.Length==0) throw new SSHException("CipherAlgorithm is not set");
			b.Append(CipherFactory.AlgorithmToSSH2Name(_param.PreferableCipherAlgorithms[0]));
			for(int i=1; i<_param.PreferableCipherAlgorithms.Length; i++) {
				b.Append(',');
				b.Append(CipherFactory.AlgorithmToSSH2Name(_param.PreferableCipherAlgorithms[i]));
			}
			return b.ToString();
		}

		/*
		 * the seed of diffie-hellman KX defined in the spec of SSH2
		 */ 
		private static BigInteger _dh_prime = null;
		private static BigInteger DH_PRIME {
			get {
				if(_dh_prime==null) {
					StringBuilder sb = new StringBuilder();
					sb.Append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
					sb.Append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
					sb.Append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
					sb.Append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
					sb.Append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381");
					sb.Append("FFFFFFFFFFFFFFFF");
					_dh_prime = new BigInteger(sb.ToString(), 16);
				}
				return _dh_prime;
			}
		}

	}

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -