客户端为您提供公共证书时的相互身份验证

时间:2016-05-04 01:06:48

标签: openssl ssl-certificate mutual-authentication

通常2路ssl又名相互认证包括生成服务器密钥&然后,客户端生成一个csr,将它提供给您并签署他们的csr并为他们提供客户端证书。

然而,

我遇到过客户要求我实施"相互认证"通过交换彼此x509公共证书。这听说过吗?也许称为" 2路SSL"或"相互认证"。

我很难通过openssl找到有关此问题的任何文档或信息。

3 个答案:

答案 0 :(得分:3)

  

我遇到过客户要求我实施"相互认证"通过交换彼此x509公共证书。这听说过吗?

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

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

第二个模型是在所谓的Origin Bound Certificates中使用自签名证书的客户端。它被称为Origin Bound,因为每个需要证书的站点(起源)都会让客户端提供证书。原始绑定证书是IETF Token Binding protocol的基础。

我不清楚令牌绑定是否具有与源绑定证书相同的安全属性。您可以看到,我们可以使用原始绑定证书来阻止中间人攻击,方法是将身份验证作为通道设置的一部分。令牌绑定解耦绑定并将其在堆栈中向上移动。我相信它允许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) 。您需要采取一些措施来确保不被欺骗,例如向用户发送电子邮件以确认他们的公钥更改是否正常。

攻击意味着当您注册用户时,您需要三到四件事:

  • 唯一的 {专有名称,序列号}
  • 公钥(X509证书的一部分)
  • 确认/恢复电子邮件地址

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

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

  

我很难通过openssl找到有关此问题的任何文档或信息。

您可能无法找到信息,因为这是一个更高级别的安全架构和设计问题。为了帮助一些参考文献,首先阅读Dietz,Czeskis,Balfanz和Wallach的Origin-Bound Certificates: A Fresh Approach to Strong Client Authentication for the Web。另请访问Peter Gutmann的Engineering Security和Ross Anderson的Security Engineering

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

当数据敏感度级别需要更强的安全控制时,客户端证书是我们首先要停止错误处理密码和拙劣的身份验证和授权控制的事情之一。不要购买Apple,Microsoft,Google(等)使用和处理密码。它多年来一直有缺陷。它简单的公司使用户可以轻松捕捉业务。

答案 1 :(得分: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 --insecure --cert /tmp/client.crt --key /tmp/client.key https://localhost:8000

请注意,遗漏了一个重要的安全层 - 拥有该指纹的任何人都是有效用户,您无法获得CA设置的漂亮加密检查。

答案 2 :(得分:0)

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

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

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

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

  1. 客户端在TLS握手中提出需要证书身份验证的合适密码
  2. 服务器根据其密码配置选择一个客户端密码
  3. 服务器在其证书存储区中至少有客户端根CA(可能还有中间客户端CA)
  4. 客户端还需要服务器根CA(可能还有中间CA)
  5. 为了满足第一个条件,客户端的openssl密码列表必须包括使用基于RSA的密码(例如RSA-AES128-SHA256)进行身份验证。您可以通过查看client hello消息,使用wireshark查看客户端密码列表。要定义(或限制)密码的数量,可以使用openssl ciphers命令,并使用密码字符串。

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

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