我正在尝试使用 OpenSSL 和 TLS 来提供类似 SSH 的信任模型。这意味着两个对等点都存储了一个 RSA 密钥对,没有证书。他们还在 TLS 会话建立之前交换了他们的公钥。为了实现这一点,我使用了一些 OpenSSL 的未记录函数,所以我仍然有疑问,这里是如何:
在初始化期间,对等点生成内存中的临时 x509 证书。它包含我可以逃脱的最小虚拟数据,即虚拟 CN、发行人以及到期前的许多年。这些无论如何都不会被检查。SSL 上下文设置为双向交换两个证书。
重要的一步是证书包含主机的公钥,并使用私钥签名:
EVP_PKEY *pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, PRIVKEY); X509_set_pubkey(x509, pkey); X509_sign(x509, pkey, EVP_sha384());
此证书分配给
SSL_CTX
并用于整个应用程序生命周期。
我最担心的是建立TLS会话时的验证过程。我正在禁用 OpenSSL 执行的所有验证,SSL_CTX_set_cert_verify_callback(always_true_func)
并像这样滚动我自己的:
X509 *received_cert = SSL_get_peer_certificate(conn_info->ssl);
EVP_PKEY *received_pubkey = X509_get_pubkey(received_cert);
if (EVP_PKEY_type(received_pubkey->type) != EVP_PKEY_RSA)
error();
ret = X509_verify(received_cert, received_pubkey);
if (ret <= 0)
error("trust_failed");
// Compare received public key with expected one
RSA *expected_rsa_key = read_RSA_key_from_disk();
EVP_PKEY expected_pubkey = { 0 };
EVP_PKEY_assign_RSA(&expected_pubkey, expected_rsa_key);
EVP_PKEY_cmp(received_pubkey, &expected_pubkey);
if (ret == 1)
return true; // identity verified!
else
return false;
问题:这是对 OpenSSL API 的正确使用吗?您是否看到任何安全漏洞,特别是在最后一部分,验证收到的密钥?有更好的方法来达到相同的结果吗?
编辑:答案:要验证在 TLS 握手期间收到的自签名证书是否与存储的 RSA 密钥匹配,不需要检查证书的签名,即不需要X509_verify
. 将收到的公钥与预期的公钥进行比较就足够了。
原因是(引用 OpenSSL 项目核心开发人员 Stephen Henson 博士的话)“取决于密码套件,服务器执行 RSA 解密操作或 RSA 签名操作。因此,如果握手成功完成,您可以确保相同的密钥用作证书中存在的那个”。