使用Libssh2 C++进行SSH公钥认证

Boa*_*rdy 17 c++ ssh public-key libssh2 private-key

我正在开发一个项目,我正在使用C++中的libssh2将端口转发到MySQL.我已经使用它进行用户名/密码验证,但我现在想要使用公钥/私钥验证.libssh2的文档很差,所以我很难解决我需要做的事情.

我想要做的是让一个Android应用程序将数据发布到我的C++应用程序,其中C++将获得SSH详细信息以及auth密钥,并创建SSH隧道.C++正在获得关键,然后我正在执行以下操作来进行公钥认证.

else if (this->getAuthMethod() == SupportedAuthMethods::AUTH_PUBLICKEY)
    {

        string test = this->getSSHPrivateKey();

        boost::replace_all(test, "\n", "");

        unsigned char * key = (unsigned char *)test.c_str();

        size_t sizeofkey = strlen((char*)key);
        cout << key << endl;
        stringstream logstream;
        logstream << "Using public key authentication for SSH Host: " << this->getSSHHostnameOrIPAddress();
        this->bitsLibrary->writeToLog(logstream.str(), "SSHTunnelForwarder", "authenticateSSHServer");
        if (chosenAuthMethod & SupportedAuthMethods::AUTH_PUBLICKEY)
        {
            //int result = 0;
            int result = libssh2_userauth_publickey(this->session, this->getUsername().c_str(), key, sizeofkey, SSHTunnelForwarder::publicKeyAuthComplete, 0);
            if (result != 0)
            {
                char * error = NULL;
                int len = 0;
                int errbuf = 0;
                libssh2_session_last_error(this->session, &error, &len, errbuf);
                this->bitsLibrary->writeToLog(std::string(error), "SSHTunnelForwarder", "auth");
                JSONResponseGenerator jsonResponse;
                jsonResponse.generateJSONResponse(API_AUTH_FAILURE, "InvalidPublicKey");
                return jsonResponse.getJSONString();

            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我在某个地方读到它不应该有新行,这就是为什么我要替换\n为空白字符但是有或没有它它没有什么区别.

最基本的文档提到其中一个参数libssh2_userauth_publickey是回调,如下所示:

int name(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract);

但我找不到任何关于这个回调是什么或它应该包含什么的信息.在我的函数调用libssh2_userauth_publickey我传入SSHTunnelForwarder::publicKeyAuthComplete,目前这个函数只有以下内容:

int SSHTunnelForwarder::publicKeyAuthComplete(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
    const unsigned char *data, size_t data_len, void **abstract)
{
    cout << "In SSH Auth Callback" << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

上面的这个方法没有被调用,虽然我期望这甚至不是正确的.

当我运行上面的代码时,libssh2_userauth_publickey的结果为19,并且从get_last_message()方法返回的错误是invalid public key.我知道公钥文件很好,因为我可以在Android上的SSH应用程序中使用它,并且可以使用我的服务器成功进行身份验证.

更新1

我已经设法取得了一些进展,我发现有一个函数叫:

libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session, const char *username, size_t username_len, const char *publickeyfiledata, size_t publickeyfiledata_len, const char *privatekeyfiledata, size_t privatekeyfiledata_len, const char *passphrase);
Run Code Online (Sandbox Code Playgroud)

我的libssh2版本来自NuGet包管理器,发现其中的最新版本实际上已经很老了,所以我在Visual Studio中从GitHub构建了最新的1.7.0版本,并将我的项目重新链接到这个新版本.

我现在已经用这个版本替换了原来的publickey auth函数,所以我的代码现在看起来像这样:

int result = libssh2_userauth_publickey_frommemory(this->session, username.c_str(), username.length(), nullptr, 0, test.c_str(), sizeofkey, nullptr);
Run Code Online (Sandbox Code Playgroud)

这个函数让我有点困惑,它想要公钥和私钥,因为我所知道的关于SSH的一切(公认的不是很大)公钥留在服务器上,想要登录的用户只能访问私有键.

我发现其他人问这个并且已经完成了一个补丁以允许公钥的空指针,所以我已经为公钥传递了nullptr,为公钥长度传递了0.我还为证书密码短语设置了一个nullptr,因为尚未设置.

当我现在运行此代码时,我现在得到错误-18 invalid username/public key combination.但是,我知道我使用的用户名和私钥文件是正确的,因为我可以在Android上的SSH客户端应用程序中使用它来连接到我的SSH服务器.

Boa*_*rdy 6

我找到了解决方案.我在更新1中所做的是实际的修复,但我的服务器密钥由于某种原因而不知道为什么会发生变化,所以当它说它无法将用户名与密钥链接时它是正确的.

完整的修复方法如下:

不要使用NuGet的libssh2它的旧版本.我从https://github.com/libssh2/libssh2/releases下载了GitHub的最新源代码,然后完成了在Visual Studio中构建libssh2的后续步骤.

  • 将libssh2/src中的所有.c文件推送到空的Win32 C++(DLL或静态库)项目中,除了libgcrypt.c/openssl.c,您只选择适合您的加密库的文件.
  • 将OpenSSL或libgcrypt include目录添加到项目包含路径中
  • 将libssh2/include添加到项目包含路径中
  • 将libssh2/win32添加到项目包含路径
  • 将适当的加密库添加到项目Additonal Libraries列表中
  • 建立
  • 任务完成

以上步骤取自https://www.libssh2.org/mail/libssh2-devel-archive-2012-09/0029.shtml

libssh2在编译期间链接到openssl.

将您的项目链接到您刚为libssh2完成的最新构建并使用该方法

libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session, const char *username, size_t username_len, const char *publickeyfiledata, size_t publickeyfiledata_len, const char *privatekeyfiledata, size_t privatekeyfiledata_len, const char *passphrase);
Run Code Online (Sandbox Code Playgroud)

就是这样.