SSH Public 使用 Libssh2 C++ 的密钥验证

SSH Public Key Authentication with Libssh2 C++

我正在做一个项目,我在 C++ 中使用 libssh2 将端口转发到 MySQL。我已经为 username/password 身份验证工作,但我现在想使用 public/private 密钥身份验证来完成它。 libssh2 的文档很差,所以我很难弄清楚我需要做什么。

我想做的是将 Android 应用程序 post 数据发送到我的 C++ 应用程序,其中 C++ 将获得 SSH 详细信息以及授权密钥,并创建 SSH 隧道. C++ 正在获取密钥,然后我正在执行以下操作来进行 public 密钥身份验证。

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。我知道 public 密钥文件很好,因为我可以在 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 Package manager,发现里面的最新版本其实很旧,所以我从 GitHub 里面构建了最新的 1.7.0 版本 Visual Studio 并将我的项目重新链接到这个新版本。

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

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

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

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

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

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

完整修复如下:

不要使用 NuGet 中的 libssh2,它太旧了。我在 https://github.com/libssh2/libssh2/releases 从 GitHub 下载了最新的源代码,然后完成了以下步骤以在 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 在编译期间 linked 到 openssl。

Link 您的项目到您刚刚为 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);

就是这样。