TLS:如何验证用RSA密钥自签名的证书

3

我正在尝试使用OpenSSL和TLS提供类似SSH的信任模型。这意味着两个对等方都存储有RSA密钥对,而没有证书。在TLS会话建立之前,他们也交换了公钥。为了实现这一点,我使用了一些未经文档化的OpenSSL函数,因此我仍然有疑问,以下是具体步骤:

  • During initialisation peers generate an in-memory, temporary x509 certificate. It contains the minimal dummy data I could get away with, i.e. a dummy CN, issuer, and many years before expiring. These are never checked anyway. SSL context is setup to exchange both certificates both ways.

  • The important step is that the certificate contains the host's public key and it is signed using the private key:

    EVP_PKEY *pkey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(pkey, PRIVKEY);
    X509_set_pubkey(x509, pkey);
    X509_sign(x509, pkey, EVP_sha384());
    
  • This certificate is assigned to SSL_CTX and used for the whole application lifetime.

我最担心的是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项目核心开发人员Dr Stephen Henson的话):“根据密码套件,服务器执行RSA解密操作或RSA签名操作。因此,如果握手成功完成,则可以确定使用与证书中存在的密钥相同的密钥。”。
1个回答

2
证书将公钥和其他信息(如标识符和其他属性)绑定在一起。使公钥与其他信息之间的关联成为证书的原因是它被签署。
X.509证书被签署并由CA颁发的原因是CA断言公钥与证书的其余内容(特别是其主题)之间的绑定。这样做的目的是让知道CA但不一定知道证书颁发给哪个实体的一方验证证书内容的真实性,特别是公钥属于证书的主题。
由于您的身份验证方案依赖于预先建立的对公钥所有者的了解,并且由于您将忽略证书的内容(除了公钥),因此没有必要验证公钥与证书其余部分之间的关联是否正确(无论是自签名还是非自签名)。
您必须验证的是您接收到的公钥是否与您已知的公钥之一匹配。
任何人都可以向您发送带有主机公钥的证书,但只有具有与该公钥匹配的私钥的实体才能协商主密钥。如何完成这项工作取决于密码套件,但只有拥有私钥的服务器才能继续进行TLS连接。
客户端至少始终会知道它已经与具有服务器证书中公钥的私钥的实体建立了TLS连接(与知道该证书属于谁无关)。
这与用于验证服务器身份的机制完全独立(传统上是PKI +主机名验证)。
如果您确信知道主机的公钥(例如通过在您已知的列表中查找它),则验证证书签名是无关紧要的(无论是自签名还是由您不知道的一方签署)。
客户端证书也适用:如果发送其证书的客户端无法使用与其发送的客户端证书匹配的私钥发送正确的签名(在Certificate Verify消息中),则握手将无法完成。

从我的角度来看,我必须验证两件事:第一是证书包含我知道的相同公钥。但第二是实际验证自签名证书是否有效签名,因为任何人都可以向我发送主机的公钥(它是公共的...)。我使用1.EVP_PKEY_cmp()和2.X509_verify()来执行这两个操作。 - jimis
感谢您深入了解TLS协议,特别是关于CertificateVerify消息的提示。我回到这里并试图从维基百科上理解它。对于CertificateVerify消息,它说:“可以使用客户端证书的公钥验证此签名。这使服务器知道客户端可以访问证书的私钥,因此拥有该证书。”但是,我已经通过覆盖verify_callback禁用了OpenSSL中的所有验证,那么什么时候检查该签名呢? - jimis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接