打开了之前看到的两个CREATOR_SSH_DEBUG调试
先看下state状态
// NOTE: When you add stuff here, don't forget to update m_packetHandlers.
enum SshStateInternal {
SocketUnconnected, // initial and after disconnect
SocketConnecting, // After connectToHost()
SocketConnected, // After socket's connected() signal
UserAuthServiceRequested,
UserAuthRequested,
ConnectionEstablished // After service has been started
// ...
};
2是SocketConnected,是前面3个包。
3是UserAuthServiceRequested,第四个包,说明第3个包处理之后就是认证服务请求了。
4是UserAuthRequested,第五个包。
5,服务已经启动了,说明五个包之后服务就起来了。
这个很重要,需要结合这个来分析抓到的包。差点忘了,这些是incoming接受到server的第几个包,差点惯性认为是交互的第几个包了。一开始写的时候就想错了,现在回过来说明下。这里的包状态说明使用(1)(2)(3)(4)(5)...来说明
登陆成功后显示信息
上面整个过程抓到的
第一个包,分析过了,连接成功后状态就是SocketConnected,然后发送ClientId
第二个包,状态为SocketConnected,第(1)个包,server返回ServerId
第三个包,状态为SocketConnected,之前就分析到第二个包,现在知道了收到ServerId后要发送Key Exchange Init,现在我依然没有去看SSH协议介绍,只说我的理解。这个应该是告诉Server,client这边key交换已经初始化好了
第四个包,状态为SocketConnected,第(2)个包,Server发送Key Exchange Init,同样应该是告诉client,server这边key交换已经初始化好了
第五个包,状态为SocketConnected,client发送Diffle-Hellman Key Exchange Init,具体是什么意思,后面看SSH协议时再去理解。这里分析完后,在本文文末还会拿点SSH协议内容来,来验证分析是否正确。
第六个包,状态为SocketConnected,第(3)个包,server发送了Diffle-Hellman Key Exchange reply,并有New Keys。既然是exchange,那么这里应该是server的new keys信息吧,不是让client发送new keys的意思吧?看后面协议验证。
第七个包,状态可能为SocketConnected,也可能为UserAuthServiceRequested,待确定。client 发送自己的new keys。UserAuthServiceRequested之后还有个UserAuthRequested。
第七个包之后,都是加密数据包了。第(4)个包及之后。UserAuthRequested?
哎,那用户名和密码是在什么时候发送过去的?哦,我忽略了一个重要信息,就是字节数。
第八个包,len=52,第九个包,len=36。而第(4)个包len=52,第(5)个包len=36。那说明,这里才是用户名和密码的验证及后续信息传输的开始。也对,用户名和密码是登陆shell的,算shell信息,而不是ssh信息。
Diffie-Hellman:一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一个组成部分。Whitefield与Martin Hellman在1976年提出了一个奇妙的密钥交换协议,称为Diffie-Hellman密钥交换协议/算法(Diffie-Hellman Key Exchange/Agreement Algorithm).这个机制的巧妙在于需要安全通信的双方可以用这个方法确定对称密钥。然后可以用这个密钥进行加密和解密。但是注意,这个密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实际加密和解密消息。
http://blog.csdn.net/gsnumen/article/details/7293266《ssh详细登录过程》
我配置sshd时使用密码登录,没有开启密钥认证。
“
这个似乎没有,第三个包是key exchange,可以理解为密钥协商。客户端没有再回复一个决定使用版本号的包啊?我是觉得没有回复。
就到这里吧,再去看下代码。
看了一下,发现之前看的
// RFC 4253, 4.2.
void SshConnectionPrivate::handleServerId()
{
#ifndef CREATOR_SSH_DEBUG
//#ifdef CREATOR_SSH_DEBUG
qDebug("%s: incoming data size = %d, incoming data = '%s'",
Q_FUNC_INFO, m_incomingData.count(), m_incomingData.data());
#endif
const int newLinePos = m_incomingData.indexOf('\n');
if (newLinePos == -1)
return; // Not enough data yet.
// Lines not starting with "SSH-" are ignored.
if (!m_incomingData.startsWith("SSH-")) {
m_incomingData.remove(0, newLinePos + 1);
m_serverHasSentDataBeforeId = true;
return;
}
if (newLinePos > 255 - 1) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
"Identification string too long.",
tr("Server identification string is %n characters long, but the maximum "
"allowed length is 255.", 0, newLinePos + 1));
}
const bool hasCarriageReturn = m_incomingData.at(newLinePos - 1) == '\r';
m_serverId = m_incomingData.left(newLinePos);
if (hasCarriageReturn)
m_serverId.chop(1);
m_incomingData.remove(0, newLinePos + 1);
if (m_serverId.contains('\0')) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
"Identification string contains illegal NUL character.",
tr("Server identification string contains illegal NUL character."));
}
// "printable US-ASCII characters, with the exception of whitespace characters
// and the minus sign"
QString legalString = QLatin1String("[]!\"#$!&'()*+,./0-9:;<=>?@A-Z[\\\\^_`a-z{|}~]+");
const QRegExp versionIdpattern(QString::fromLatin1("SSH-(%1)-%1(?: .+)?").arg(legalString));
if (!versionIdpattern.exactMatch(QString::fromLatin1(m_serverId))) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
"Identification string is invalid.",
tr("Server Identification string \"%1\" is invalid.")
.arg(QString::fromLatin1(m_serverId)));
}
const QString serverProtoVersion = versionIdpattern.cap(1);
if (serverProtoVersion != QLatin1String("2.0") && serverProtoVersion != QLatin1String("1.99")) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
"Invalid protocol version.",
tr("Server protocol version is \"%1\", but needs to be 2.0 or 1.99.")
.arg(serverProtoVersion));
}
if (m_connParams.options & SshEnableStrictConformanceChecks) {
if (serverProtoVersion == QLatin1String("2.0") && !hasCarriageReturn) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
"Identification string is invalid.",
tr("Server identification string is invalid (missing carriage return)."));
}
if (serverProtoVersion == QLatin1String("1.99") && m_serverHasSentDataBeforeId) {
throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
"No extra data preceding identification string allowed for 1.99.",
tr("Server reports protocol version 1.99, but sends data "
"before the identification string, which is not allowed."));
}
}
m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
m_keyExchange->sendKexInitPacket(m_serverId);
m_keyExchangeState = KexInitSent;
}
提示中就说了need ssh2.0或者ssh1.99,所以对于QSsh确实没有回复的。这些都处理好后,就直接sendKeyInitPacket()了,也就是Key Exchange Init。