我正在尝试使用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签名操作。因此,如果握手成功完成,则可以确定使用与证书中存在的密钥相同的密钥。”。