2

通常 2 路 ssl aka 相互身份验证包括生成服务器 ca 密钥和证书等。然后客户端生成一个 csr,将其提供给您,然后您签署他们的 csr 并为他们提供客户端证书。

然而,

我遇到过客户要求我通过相互交换 x509 公共证书来实现“相互身份验证”的情况。这是听说过的吗?也许称为“2 路 SSL”或“相互验证”以外的其他名称。

我正在努力使用 openssl 查找有关此的任何文档或信息。

4

3 回答 3

4

我遇到过客户要求我通过相互交换 x509 公共证书来实现“相互身份验证”的情况。这是听说过的吗?

我相信它仍然被称为相互认证。

通常,基于证书的相互认证属于两种模型之一。第一个是具有 CA 层次结构的企业模型,组织的 CA 签署客户端和服务器证书。

第二种模式是客户端在所谓的Origin Bound Certificates中使用自签名证书。它被称为起源绑定,因为每个需要证书的站点(起源)都会让客户端提供一个。来源绑定证书是 IETF令牌绑定协议的基础。

我不清楚令牌绑定是否具有与原始绑定证书相同的安全属性。你看,我们可以通过将身份验证作为通道设置的一部分来使用原始绑定证书来阻止中间人攻击。令牌绑定将绑定解耦并将其在堆栈中向上移动。我相信它允许 MitM 充当中间人。


企业证书

在企业模型中,您可以使用 OpenSSL 执行以下操作。

在服务器上执行以下操作。服务器将处理客户端证书验证:

  • SSL_CTX_set_verifySSL_VERIFY_PEER和调用SSL_VERIFY_FAIL_IF_NO_PEER_CERT
  • 调用CTX_set_client_CA_list以设置服务器将接受的颁发者 CA 列表。这会导致将适当的 SSL/TLS 消息发送到提示输入证书的客户端。这只会向客户端发送一个名称列表;他们仍然必须在服务器上被信任
  • SSL_CTX_load_verify_locations与服务器接受的发行者通话。这增加了服务器端的信任。

在客户端,执行以下操作:

  • 调用SSL_CTX_use_certificate_file加载客户端证书
  • SSL_CTX_use_certificate_chain_file根据需要调用
  • 调用SSL_CTX_use_PrivateKey加载私钥

原产地绑定证书

原始绑定证书和自签名证书略有不同,因为没有 CA 层次结构。

在服务器上执行以下操作。服务器调用SSL_CTX_load_verify_locationsCTX_set_client_CA_list因为它是自签名证书。

  • SSL_CTX_set_verifySSL_VERIFY_PEER和调用SSL_VERIFY_FAIL_IF_NO_PEER_CERT
  • SSL_get_peer_certificate密钥交换后调用。验证提交给服务器的客户端证书。

在客户端,执行以下操作。它与企业模型相同。

  • 调用SSL_CTX_use_certificate_file加载客户端证书
  • SSL_CTX_use_certificate_chain_file根据需要调用
  • 调用SSL_CTX_use_PrivateKey加载私钥

缺少企业 CA 意味着客户端的自签名证书需要带外传送到服务器。然后,服务器需要保留某种目录,用于根据可分辨名称和序列号查找客户端证书。您在这里真正关心的是客户端的公钥。在这种情况下,X509 证书是一个演示详细信息。它只是打包,因为它通常通过权威的签名将身份绑定到公钥(但不在此模型中)。

这个模型中的攻击——房间里的 500 磅大猩猩——是坏人通过使用相同的专有名称和序列号来冒充用户,因为没有注册机构 (RA)。您将需要采取一些措施来确保您不被欺骗,例如向用户发送电子邮件以确认他们的公钥更改是预期的。

攻击意味着当你注册一个用户时,你需要三四件事:

  • 唯一的 {Distinguished Name, Serial Number}
  • 公钥(X509 证书的一部分)
  • 确认/恢复电子邮件地址

为了进一步混淆水域,用户可能有 3 或 4 台设备,因此他们为每个设备创建一个新的起源绑定证书。您还需要优雅地处理该注册。

总的来说,真正重要的是电子邮件地址以及与电子邮件地址关联的不同公钥/身份。身份存储在具有唯一{Distinguished Name, Serial Number}对的 X509 证书中。您可能希望它们唯一用于审计目的,但可能会在设备之间进行一些复制/粘贴。


我正在努力使用 openssl 查找有关此的任何文档或信息。

您可能很难找到信息,因为这是一个更高级别的安全架构和设计问题。要获得一些参考资料,请先阅读 Dietz、Czeskis、Balfanz 和 Wallach 的Origin-Bound Certificates: A Fresh Approach to Strong Client Authentication for Web。另请访问 Peter Gutmann 的工程安全和罗斯安德森的安全工程

Origin Bound Certificates 可用于替换几乎所有基于密码的身份验证系统。它适用于几乎所有系统,从基于用户名/密码的身份验证到 Web 服务中使用的 API 密钥。密码仍然用于保护本地私钥,但密码没有放在网络上。

当数据敏感度级别需要更强大的安全控制时,客户端证书是我们首先要阻止密码处理不当以及验证和授权控制不当的事情之一。不要相信 Apple、Microsoft、Google(等)使用和处理密码。多年来一直有缺陷。它只是让用户轻松获得业务的公司。

于 2016-05-05T07:16:24.710 回答
1

传统的客户端证书方法利用 CA 和数字签名来验证证书的真实性。

在您的情况下,您似乎想要事先在受信任的渠道中交换证书。在这种情况下,您需要做的是存储此证书的指纹,并在收到请求时验证签名是否正确。

这是 Node.JS 中的一个示例:

const https = require('https');
const fs = require('fs');

const options = {
    key: fs.readFileSync('/tmp/server.key'),
    cert: fs.readFileSync('/tmp/server.crt'),
    requestCert: true,
    rejectUnauthorized: false
};

https.createServer(options, (req, res) => {
    // We use snake oil certificates and no CA, so we will have an unauthorized cert.
    console.log(req.socket.authorized);
    var cert = req.connection.getPeerCertificate();
    // Here's the cert fingerprint, validate that it's the one you expected
    console.log(cert.fingerprint);

    res.writeHead(200);
    res.end('hello world\n');
}).listen(8000);

用于生成证书和密钥的 ruby​​ 脚本:

require "openssl"

keypair = OpenSSL::PKey::RSA.new(2048)
cert = OpenSSL::X509::Certificate.new
cert.not_before = Time.now
cert.subject = OpenSSL::X509::Name.new([
    ["C", "NO"],
    ["ST", "Oslo"],
    ["L", "Oslo"],
    ["CN", "Root CA"]
                                               ])
cert.issuer = cert.subject
cert.not_after = Time.now + 1000000000 # 40 or so years
cert.public_key = keypair.public_key
cert.sign(keypair, OpenSSL::Digest::SHA256.new)

File.open("/tmp/client.key", "w+") do |f|
  f << keypair.to_pem
end

File.open("/tmp/client.crt", "w+") do |f|
  f << cert.to_pem
end


snakeoil_keypair = OpenSSL::PKey::RSA.new(2048)
snakeoil_cert = OpenSSL::X509::Certificate.new
snakeoil_cert.not_before = Time.now
snakeoil_cert.subject = OpenSSL::X509::Name.new([
    ["C", "NO"],
    ["ST", "Oslo"],
    ["L", "Oslo"],
    ["CN", "Root CA"]
                                               ])
snakeoil_cert.issuer = snakeoil_cert.subject
snakeoil_cert.not_after = Time.now + 1000000000 # 40 or so years
snakeoil_cert.public_key = snakeoil_keypair.public_key
snakeoil_cert.sign(snakeoil_keypair, OpenSSL::Digest::SHA256.new)

File.open("/tmp/server.key", "w+") do |f|
  f << snakeoil_keypair.to_pem
end

File.open("/tmp/server.crt", "w+") do |f|
  f << snakeoil_cert.to_pem
end

用 curl 测试:

curl --insecure --cert /tmp/client.crt --key /tmp/client.key https://localhost:8000

请注意,重要的安全层被遗漏了 - 任何拥有该指纹的人都将成为有效用户,您不会获得 CA 设置的良好加密检查。

于 2016-05-04T08:17:40.180 回答
1

TLS(或 SSL)握手中的相互身份验证需要交换双方的证书。

客户端通过验证服务器证书(颁发它的证书链)来验证它连接到的服务器。

例如,当您浏览到任何 http网站时,就会这样做您的浏览器将检查收到的证书是否受信任(通过查找其证书存储),并且还将检查网站的 dns 名称与证书的通用名称是否匹配等属性。

在 TLS 相互身份验证中,服务器还将通过验证客户端证书来检查客户端是否受信任。这种不太常见的身份验证类型需要以下条件:

  1. 客户端在 TLS 握手中提出需要证书身份验证的合适密码
  2. 服务器根据其密码配置选择客户端密码之一
  3. 服务器在其证书存储中至少具有客户端根 CA(也可能是中间客户端 CA)
  4. 客户端还需要服务器根 CA(可能还有中间 CA)

为了满足第一个条件,客户端的 openssl 密码列表必须包括例如使用基于 RSA 的密码(如RSA-AES128-SHA256. 您可以通过查看client hello消息来使用wireshark 查看客户端密码列表。要定义(或限制)密码的数量,您可以使用openssl ciphers命令,并使用密码字符串。

在服务器端,还必须配置和选择密码。此选择取决于实现。通常,服务器会选择第一个合适的密码client hello(即使 RFC 说服务器可以从列表中选择任何密码)。因此,在某些情况下,将所需密码放在客户端密码列表的顶部会有所帮助。

关于证书,必须分别在客户端和服务器证书存储中提供服务器和客户端根 CA。现在,如果任何一方有证书链,有时还需要将中间 CA 放入证书存储中。

于 2016-05-05T10:38:08.770 回答