📄 ssh1connection.cs
字号:
/* ---------------------------------------------------------------------------
*
* Copyright (c) Routrek Networks, Inc. All Rights Reserved..
*
* This file is a part of the Granados SSH Client Library that is subject to
* the license included in the distributed package.
* You may not use this file except in compliance with the license.
*
* ---------------------------------------------------------------------------
*/
using System;
using System.IO;
using System.Security.Cryptography;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;
using Routrek.PKI;
using Routrek.SSHC;
using Routrek.Toolkit;
namespace Routrek.SSHCV1
{
public sealed class SSH1Connection : SSHConnection {
private const int AUTH_NOT_REQUIRED = 0;
private const int AUTH_REQUIRED = 1;
private SSH1ConnectionInfo _cInfo;
private int _shellID;
private SSH1PacketBuilder _packetBuilder;
private bool _executingShell;
public SSH1Connection(SSHConnectionParameter param, ISSHConnectionEventReceiver er, string serverversion, string clientversion) : base(param, er) {
_cInfo = new SSH1ConnectionInfo();
_cInfo._serverVersionString = serverversion;
_cInfo._clientVersionString = clientversion;
_shellID = -1;
_packetBuilder = new SSH1PacketBuilder(new SynchronizedSSH1PacketHandler());
}
public override SSHConnectionInfo ConnectionInfo {
get {
return _cInfo;
}
}
internal override IByteArrayHandler PacketBuilder {
get {
return _packetBuilder;
}
}
public override int ChannelCount {
get {
return base.ChannelCount + 1; //'1' is for the shell
}
}
internal override AuthenticationResult Connect(AbstractSocket s) {
_stream = s;
// Phase2 receives server keys
ReceiveServerKeys();
if(_param.KeyCheck!=null && !_param.KeyCheck(_cInfo)) {
_stream.Close();
return AuthenticationResult.Failure;
}
// Phase3 generates session key
byte[] session_key = GenerateSessionKey();
// Phase4 establishes the session key
try {
_packetBuilder.SetSignal(false);
SendSessionKey(session_key);
InitCipher(session_key);
}
finally {
_packetBuilder.SetSignal(true);
}
ReceiveKeyConfirmation();
// Phase5 user authentication
SendUserName(_param.UserName);
if(ReceiveAuthenticationRequirement()==AUTH_REQUIRED) {
if(_param.AuthenticationType==AuthenticationType.Password) {
SendPlainPassword();
} else if(_param.AuthenticationType==AuthenticationType.PublicKey) {
DoRSAChallengeResponse();
}
bool auth = ReceiveAuthenticationResult();
if(!auth) throw new SSHException(Strings.GetString("AuthenticationFailed"));
}
_packetBuilder.Handler = new CallbackSSH1PacketHandler(this);
return AuthenticationResult.Success;
}
internal void Transmit(SSH1Packet p) {
lock(this) {
p.WriteTo(_stream, _tCipher);
}
}
public override void Disconnect(string msg) {
if(_closed) return;
SSH1DataWriter w = new SSH1DataWriter();
w.Write(msg);
SSH1Packet p = SSH1Packet.FromPlainPayload(PacketType.SSH_MSG_DISCONNECT, w.ToByteArray());
p.WriteTo(_stream, _tCipher);
_stream.Flush();
_closed = true;
_stream.Close();
}
public override void Close() {
if(_closed) return;
_closed = true;
_stream.Close();
}
public override void SendIgnorableData(string msg) {
SSH1DataWriter w = new SSH1DataWriter();
w.Write(msg);
SSH1Packet p = SSH1Packet.FromPlainPayload(PacketType.SSH_MSG_IGNORE, w.ToByteArray());
Transmit(p);
}
private void ReceiveServerKeys() {
SSH1Packet SSH1Packet = ReceivePacket();
if(SSH1Packet.Type!=PacketType.SSH_SMSG_PUBLIC_KEY) throw new SSHException("unexpected SSH SSH1Packet type " + SSH1Packet.Type, SSH1Packet.Data);
SSH1DataReader reader = new SSH1DataReader(SSH1Packet.Data);
_cInfo._serverinfo = new SSHServerInfo(reader);
_cInfo._hostkey = new RSAPublicKey(_cInfo._serverinfo.host_key_public_exponent, _cInfo._serverinfo.host_key_public_modulus);
//read protocol support parameters
int protocol_flags = reader.ReadInt32();
int supported_ciphers_mask = reader.ReadInt32();
_cInfo.SetSupportedCipherAlgorithms(supported_ciphers_mask);
int supported_authentications_mask = reader.ReadInt32();
//Debug.WriteLine(String.Format("ServerOptions {0} {1} {2}", protocol_flags, supported_ciphers_mask, supported_authentications_mask));
if(reader.Rest>0) throw new SSHException("data length mismatch", SSH1Packet.Data);
//Debug Info
/*
System.out.println("Flags="+protocol_flags);
System.out.println("Cipher="+supported_ciphers_mask);
System.out.println("Auth="+supported_authentications_mask);
*/
bool found = false;
foreach(CipherAlgorithm a in _param.PreferableCipherAlgorithms) {
if(a!=CipherAlgorithm.Blowfish && a!=CipherAlgorithm.TripleDES)
continue;
else if(a==CipherAlgorithm.Blowfish && (supported_ciphers_mask & (1 << (int)CipherAlgorithm.Blowfish))==0)
continue;
else if(a==CipherAlgorithm.TripleDES && (supported_ciphers_mask & (1 << (int)CipherAlgorithm.TripleDES))==0)
continue;
_cInfo._algorithmForReception = _cInfo._algorithmForTransmittion = a;
found = true;
break;
}
if(!found)
throw new SSHException(String.Format(Strings.GetString("ServerNotSupportedX"), "Blowfish/TripleDES"));
if(_param.AuthenticationType==AuthenticationType.Password && (supported_authentications_mask & (1 << (int)AuthenticationType.Password))==0)
throw new SSHException(String.Format(Strings.GetString("ServerNotSupportedPassword")), SSH1Packet.Data);
if(_param.AuthenticationType==AuthenticationType.PublicKey && (supported_authentications_mask & (1 << (int)AuthenticationType.PublicKey))==0)
throw new SSHException(String.Format(Strings.GetString("ServerNotSupportedRSA")), SSH1Packet.Data);
}
private byte[] GenerateSessionKey() {
//session key(256bits)
byte[] session_key = new byte[32];
_param.Random.NextBytes(session_key);
//for(int i=0; i<32; i++) Debug.Write(String.Format("0x{0:x}, ", session_key[i]));
return session_key;
}
private void SendSessionKey(byte[] session_key) {
try
{
//step1 XOR with session_id
byte[] working_data = new byte[session_key.Length];
byte[] session_id = CalcSessionID();
Array.Copy(session_key, 0, working_data, 0, session_key.Length);
for(int i=0; i<session_id.Length; i++) working_data[i] ^= session_id[i];
//step2 decrypts with RSA
RSAPublicKey first_encryption;
RSAPublicKey second_encryption;
SSHServerInfo si = _cInfo._serverinfo;
int first_key_bytelen, second_key_bytelen;
if(si.server_key_bits < si.host_key_bits)
{
first_encryption = new RSAPublicKey(si.server_key_public_exponent, si.server_key_public_modulus);
second_encryption = new RSAPublicKey(si.host_key_public_exponent, si.host_key_public_modulus);
first_key_bytelen = (si.server_key_bits+7)/8;
second_key_bytelen = (si.host_key_bits+7)/8;
}
else
{
first_encryption = new RSAPublicKey(si.host_key_public_exponent, si.host_key_public_modulus);
second_encryption = new RSAPublicKey(si.server_key_public_exponent, si.server_key_public_modulus);
first_key_bytelen = (si.host_key_bits+7)/8;
second_key_bytelen = (si.server_key_bits+7)/8;
}
BigInteger first_result = RSAUtil.PKCS1PadType2(new BigInteger(working_data), first_key_bytelen, _param.Random).modPow(first_encryption.Exponent, first_encryption.Modulus);
BigInteger second_result = RSAUtil.PKCS1PadType2(first_result, second_key_bytelen, _param.Random).modPow(second_encryption.Exponent, second_encryption.Modulus);
//output
SSH1DataWriter writer = new SSH1DataWriter();
writer.Write((byte)_cInfo._algorithmForTransmittion);
writer.Write(si.anti_spoofing_cookie);
writer.Write(second_result);
writer.Write(0); //protocol flags
//send
SSH1Packet SSH1Packet = SSH1Packet.FromPlainPayload(PacketType.SSH_CMSG_SESSION_KEY, writer.ToByteArray());
SSH1Packet.WriteTo(_stream);
_sessionID = session_id;
}
catch(Exception e)
{
if(e is IOException)
throw (IOException)e;
else
{
string t = e.StackTrace;
throw new SSHException(e.Message); //IOException埲奜偼傒側SSHException偵偟偰偟傑偆
}
}
}
private void ReceiveKeyConfirmation() {
SSH1Packet SSH1Packet = ReceivePacket();
if(SSH1Packet.Type!=PacketType.SSH_SMSG_SUCCESS)
throw new SSHException("unexpected packet type [" + SSH1Packet.Type +"] at ReceiveKeyConfirmation()", SSH1Packet.Data);
}
private int ReceiveAuthenticationRequirement() {
SSH1Packet SSH1Packet = ReceivePacket();
if(SSH1Packet.Type==PacketType.SSH_SMSG_SUCCESS)
return AUTH_NOT_REQUIRED;
else if(SSH1Packet.Type==PacketType.SSH_SMSG_FAILURE)
return AUTH_REQUIRED;
else
throw new SSHException("type " + SSH1Packet.Type, SSH1Packet.Data);
}
private void SendUserName(string username) {
SSH1DataWriter writer = new SSH1DataWriter();
writer.Write(username);
SSH1Packet SSH1Packet = SSH1Packet.FromPlainPayload(PacketType.SSH_CMSG_USER, writer.ToByteArray());
SSH1Packet.WriteTo(_stream, _tCipher);
}
private void SendPlainPassword() {
SSH1DataWriter writer = new SSH1DataWriter();
writer.Write(_param.Password);
SSH1Packet SSH1Packet = SSH1Packet.FromPlainPayload(PacketType.SSH_CMSG_AUTH_PASSWORD, writer.ToByteArray());
SSH1Packet.WriteTo(_stream, _tCipher);
}
//RSA authentication
private void DoRSAChallengeResponse() {
//read key
SSH1UserAuthKey key = new SSH1UserAuthKey(_param.IdentityFile, _param.Password);
SSH1DataWriter w = new SSH1DataWriter();
w.Write(key.PublicModulus);
SSH1Packet p = SSH1Packet.FromPlainPayload(PacketType.SSH_CMSG_AUTH_RSA, w.ToByteArray());
p.WriteTo(_stream, _tCipher);
p = ReceivePacket();
if(p.Type==PacketType.SSH_SMSG_FAILURE)
throw new SSHException(Strings.GetString("ServerRefusedRSA"));
else if(p.Type!=PacketType.SSH_SMSG_AUTH_RSA_CHALLENGE)
throw new SSHException(String.Format(Strings.GetString("UnexpectedResponse"), p.Type));
//creating challenge
SSH1DataReader r = new SSH1DataReader(p.Data);
BigInteger challenge = key.decryptChallenge(r.ReadMPInt());
byte[] rawchallenge = RSAUtil.StripPKCS1Pad(challenge, 2).getBytes();
//building response
MemoryStream bos = new MemoryStream();
bos.Write(rawchallenge, 0, rawchallenge.Length); //!!mindterm偱偼摢偑侽偐偳偆偐偱曄側僴儞僪儕儞僌偑偁偭偨
bos.Write(_sessionID, 0, _sessionID.Length);
byte[] response = new MD5CryptoServiceProvider().ComputeHash(bos.ToArray());
w = new SSH1DataWriter();
w.Write(response);
p = SSH1Packet.FromPlainPayload(PacketType.SSH_CMSG_AUTH_RSA_RESPONSE, w.ToByteArray());
p.WriteTo(_stream, _tCipher);
}
private bool ReceiveAuthenticationResult() {
SSH1Packet SSH1Packet = ReceivePacket();
PacketType type = SSH1Packet.Type;
if(type==PacketType.SSH_MSG_DEBUG) {
SSH1DataReader r = new SSH1DataReader(SSH1Packet.Data);
//Debug.WriteLine("receivedd debug message:"+Encoding.ASCII.GetString(r.ReadString()));
return ReceiveAuthenticationResult();
}
else if(type==PacketType.SSH_SMSG_SUCCESS)
return true;
else if(type==PacketType.SSH_SMSG_FAILURE)
return false;
else
throw new SSHException("type: " + type, SSH1Packet.Data);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -