我正在尝试使用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会话建立时的验证过程。我正在使用SSL_CTX_set_cert_verify_callback(always_true_func)
禁用OpenSSL执行的所有验证,并自行滚动:
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 签名操作由服务器执行。所以如果握手完成了 成功地,您可以确保使用相同的密钥作为存在的密钥 证书“。
答案 0 :(得分:2)
证书将公钥和其他信息(例如标识符和其他属性)绑定在一起。是什么使得公钥和附加信息之间的关联成为签名的事实。
CA签署和颁发X.509证书的原因是因为CA断言公钥与证书的其余内容(特别是其主题)之间的绑定。这样做的目的是让知道CA的一方但不一定知道颁发证书的实体验证证书的内容是否为真,特别是公钥属于证书的主题。 / p>
由于您的身份验证方案依赖于预先知道谁拥有公钥或者拥有公钥的所有知识,并且由于您将忽略证书的内容(除公钥之外),因此无需验证公钥和证书的其余部分是真实的(无论是否自签)。
您必须验证的是,您收到的公钥与您已知的公钥之一匹配。
修改强>
但其次是实际验证自签名证书 有效签名,因为任何人都可以向我发送主持人的公钥(它是 公共...)。
任何人都可以使用主机的公钥向您发送证书,但只有具有与该公钥匹配的私钥的实体才能协商主密钥。如何完成这取决于密码套件,但只有具有私钥的服务器才能继续使用TLS连接。
客户端至少总是至少知道它已经与服务器证书中具有公钥私钥的实体建立了TLS连接(独立于知道该证书属于谁)。
这实际上完全独立于用于验证服务器身份的机制(传统上,PKI +主机名验证)。
如果您确定地知道主持人的公钥(例如,通过在您已知的列表中查找),验证证书签名是无关紧要的(无论是自签名还是由您不知道的一方签名)。
同样适用于客户端证书:如果发送证书的客户端无法使用与其发送的客户端证书匹配的私钥发送正确的签名(在证书验证消息中),则握手将无法完成。 / p>