使用 Libssh2 C++ 的 SSH 公钥认证

Posted

技术标签:

【中文标题】使用 Libssh2 C++ 的 SSH 公钥认证【英文标题】:SSH Public Key Authentication with Libssh2 C++ 【发布时间】:2017-07-06 05:27:31 【问题描述】:

我正在做一个项目,我在 C++ 中使用 libssh2 将端口转发到 mysql。我已经让它用于用户名/密码身份验证,但我现在想使用公钥/私钥身份验证。 libssh2 的文档很差,所以我很难弄清楚我需要做什么。

我要做的是让一个 android 应用程序将数据发布到我的 C++ 应用程序,其中 C++ 将获得 SSH 详细信息以及身份验证密钥,并创建 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();

            
        
    

我在某处读到它不应该有新行,这就是为什么我将 \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;

上面的这个方法没有被调用,虽然我预计这甚至不接近正确。

当我在上面运行我的代码时,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);

我拥有的 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);

这个函数让我有点困惑,它需要公钥和私钥,因为我所知道的关于 SSH 的一切(当然不是很多)公钥都保留在服务器上,并且想要登录的用户只能访问到私钥。

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

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

【问题讨论】:

【参考方案1】:

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

完整修复如下:

不要使用 NuGet 中的 libssh2,它已经很老了。我从 GitHub https://github.com/libssh2/libssh2/releases 下载了最新的源代码,然后完成了在 Visual Studio 中构建 libssh2 的以下步骤。

将 libssh2/src 中的所有 .c 文件放入一个空的 Win32 C++(DLL 或静态库)项目,除了你的 libgcrypt.c/openssl.c 只选择适合您的加密库的那个。 将您的 OpenSSL 或 libgcrypt 包含目录添加到项目包含路径中 将 libssh2/include 添加到项目包含路径 将libssh2/win32添加到项目包含路径 将适当的加密库添加到项目附加库列表中 构建 工作完成

以上步骤取自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);

就是这样。

【讨论】:

以上是关于使用 Libssh2 C++ 的 SSH 公钥认证的主要内容,如果未能解决你的问题,请参考以下文章

ssh 服务器之间公钥认证方式的配置

Xshell配置ssh免密码登录-密钥公钥(Public key)

ssh公钥认证不改变系统文件

Xshell配置ssh免密码登录-密钥公钥(Public key)与私钥(Private Key)登录

如何设置公钥认证? [关闭]

Linux的SSH免密登录认证过程研究