c - TLS : How to verify a certificate self-signed with an RSA key

标签 c ssl openssl

我正在尝试使用 OpenSSL 和 TLS 来提供类似 SSH 的信任模型。这意味着两个对等点各自存储了一个 RSA key 对,没有证书。他们还在 TLS session 建立之前交换了公钥。为了实现这一点,我使用了一些 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 session 建立时的验证过程。我正在使用 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 的正确使用吗?您是否看到任何安全漏洞,尤其是在最后一部分,即接收 key 的验证?有没有更好的方法来达到相同的结果?

编辑:答案:要验证在 TLS 握手期间收到的自签名证书是否与存储的 RSA key 匹配,无需检查证书的签名,即不需要 X509_verify。将收到的公钥与预期的公钥进行比较就足够了。

原因是(引用 OpenSSL 项目核心开发人员 Stephen Henson 博士的话)“取决于密码套件,RSA 解密操作或 RSA 签名操作由服务器执行。所以如果握手完成 成功地,您可以确定使用了与中存在的 key 相同的 key 证书”。

最佳答案

证书将公钥和附加信息(例如标识符和其他属性)绑定(bind)在一起。使公钥和附加信息之间的这种关联成为证书的原因是它已签名。

X.509 证书由 CA 签署和颁发的原因是因为 CA 断言公钥与证书其余内容(特别是其主题)之间的绑定(bind)。这样做的目的是让知道CA但不一定知道颁发证书的实体的一方验证证书内容的真实性,特别是公钥属于证书的主题。

由于您的身份验证方案依赖于关于谁或什么拥有公钥的预先确定的知识,并且由于您将忽略证书的内容(除了公钥之外),因此没有必要验证两者之间的关联公钥和证书的其余部分都是真实的(无论是否自签名)。

您必须验证的是您收到的公钥是否与您已知的公钥之一匹配。

编辑:

But second is to actually verify that the self-signed certificate is validly signed, since anyone can send me the host's public key (it's public...).

任何人都可以向您发送带有主机公钥的证书,但只有拥有与该公钥匹配的私钥的实体才能协商主 key 。这是如何完成的取决于密码套件,但只有拥有私钥的服务器才能继续 TLS 连接。

客户端至少总是至少知道它已经与在服务器证书中拥有公钥私钥的实体建立了 TLS 连接(独立于知道该证书属于谁)。

这确实完全独立于用于验证服务器身份的机制(传统上,PKI + 主机名验证)。

如果您确定知道主机的公钥(例如,通过在您已知的列表中查找它),验证证书签名是无关紧要的(无论它是自签名的还是由您不认识的一方签名的)。

这同样适用于客户端证书:如果发送其证书的客户端无法发送使用与其发送的客户端证书匹配的私钥生成的正确签名(在证书验证消息中),则握手将无法完成。

关于c - TLS : How to verify a certificate self-signed with an RSA key,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18214950/

相关文章:

ssl - 大摇大摆的 TLS 配置

asp.net-mvc - 在 Asp.net MVC 应用程序中使用 SSL 与验证 key 的区别

单独端口而非 SSL 上的 WCF 元数据

r - 安全 Elasticsearch R 连接错误 - 客户端请求的协议(protocol) TLSv1 未启用或不受支持

node.js - Electron 原生插件在Windows上失败

c++ - 在给定时间内将频率从f1缓慢升高到f2的正弦波

c - 读取 PMG P5 文件时出现错误

c - 在结构错误中指向取消引用(间接需要指针操作数)

C:为什么为 int 参数传递 float/double 文字不会引发警告?

java - 如何使用 openssl 解密 Java-DES 加密的消息?