📄 ssh2connection.cs
字号:
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 + -