Apache 不会请求我的 SSL 客户端证书

Posted

技术标签:

【中文标题】Apache 不会请求我的 SSL 客户端证书【英文标题】:Apache Won't Request My SSL Client Certificate 【发布时间】:2012-05-02 09:47:15 【问题描述】:

首先,请注意我是配置 SSL 的新手。过去,我一直很幸运有一个 IT 部门提前为我设置。因此,请做好准备,我可能需要对您的一些答案进行澄清。 =)

我要做什么

我正在为员工建立一个公司内部网站。例如,将有一个浏览器起始页,显示为每个员工定制的内容。因此,我需要能够在不需要任何用户名/密码或其他提示的情况下识别所述员工(虽然一次性设置是可以的;我只是不希望他们每次都被提示)。当然,SSL 似乎是最好的方法。

我将设置一个 mysql 数据库,以将“用户”帐户与 SSL_CLIENT_M_SERIAL 和 SSL_CLIENT_I_DN 相关联,我假设每个客户端证书都是唯一的(?)。我从这篇文章中得到了这个想法:http://cweiske.de/tagebuch/ssl-client-certificates.htm

用户第一次访问内部网站时,他们将没有证书(我不想为客户手动生成它们!),在这种情况下 $_SERVER["SSL_CLIENT_VERIFY"] == "NONE ”。如果发生这种情况,它将转到用户帐户设置页面,其中包括 php 生成 SSL 客户端证书并将其发送到浏览器以供用户安装的步骤。很好很简单。然后用户安装证书,建立关联,并在重新启动浏览器后(为了好的措施),用户返回内部网站。

此时,Apache 应该请求客户端证书,然后浏览器发送该证书。然后 PHP 脚本解析必要的 $_SERVER 变量,与 MySQL 数据库进行比较,所有人都度过了美好的时光。再一次,又好又简单。

目前的工作情况

我已安装服务器端证书。是的,它们是自签名的(原因很明显)。 Apache 安装了 mod_ssl,一切似乎都运行良好。我创建了一个 PHP 脚本,它只转储 $_SERVER 数组,并且所有 SSL_SERVER_* 键值都与我为它创建的证书匹配。

问题

我无法让客户端证书正常工作!在同一个 PHP 脚本中,无论我做什么,都缺少 SSL_CLIENT_VERIFY == "NONE" 和其他 SSL_CLIENT_* 键。如果我在 ssl.conf 中将 SSLVerifyClient 设置为可选,就会发生这种情况。根据我读过的每个教程,他们都说网络服务器应该向浏览器请求客户端证书。问题是,我不能让它这样做!它直接进入 PHP 脚本并假设我根本没有客户端证书。这发生在 Firefox、Chrome 和 IE 中。

所以我尝试将 SSLVerifyClient 设置为必需并重新启动网络服务器。有了这个选项,我什至无法建立 SSL 连接。 Firefox 只是说连接已被重置(其他浏览器也会显示他们自己的错误版本)。奇怪的是日志没有显示这些连接尝试的任何活动! IE。 access_log、error_log、ssl_access_log、ssl_error_log 和 ssl_request_log 都不显示任何东西;就好像这种尝试从未发生过。这令人沮丧,因为这意味着我什至没有错误消息可供使用。只是一个网络服务器被动地告诉我下地狱。

我尝试使用 PHP 的 OpenSSL 扩展手动生成/安装我自己的客户端证书。证书安装得很好,虽然我找不到任何关于如何将该证书与服务器相关联的信息(假设我什至需要?)。此外,这似乎并不重要,因为如果设置了 optional,Apache 甚至不会请求客户端证书。如果设置了require,它只会在没有解释的情况下爆炸。无论如何,我都需要将其设置为可选,以便此架构正常工作。

环境

操作系统:CentOS 5.7 64 位(虚拟机)

阿帕奇:2.2.3

PHP:5.3.10

我猜您可能需要更多信息来帮助我,所以请询问!你需要什么我都会给你。

总而言之,在上述条件下,我需要知道如何让 Apache 请求 SSL 客户端证书。此外,如果必须执行任何特殊的签名/等操作以使客户端证书与服务器证书“兼容”(同样,无需通过 shell 手动为每个客户端证书执行此操作!),我需要知道这一点好吧。

到目前为止,我 100% 坚持这一点。在 Google 上什至找不到任何有帮助的东西。非常感谢您对此提供的任何帮助!谢谢! =)

【问题讨论】:

生成的每个客户端证书都是自签名的吗?或者您是否创建了自己的 CA 来签署生成的请求?如果您安装了一个由受信任的机构(由服务器的信任库确定)签名的证书,客户端只会提示您提供证书。如果每个客户端证书都是自签名的,则您需要将这些生成的每个证书都放在服务器的信任库中,这是不可行的。 我尝试使用 openssl 在服务器上创建客户端证书,导出为 p12,然后在 Firefox 中安装它,但现在我从 Apache 收到“无法获取本地颁发者证书”错误。我使用服务器证书签署了客户端证书,为什么它不起作用?我尝试取消注释 ssl.conf 中的 SSLCACertificateFile 行,但这只是让我回到第一个问题,即页面加载正常但没有识别客户端证书。所以我又一次回到了原点...... 【参考方案1】:

我有类似的问题:

CentOS 6.3 Apache 2.2.15

经过一些尝试,我认识到了我的问题。

如果我设置了SSLVerifyClient optionalSSLVerifyClient optional_no_ca 并且我还指定了SSLCACertificateFileSSLCACertificatePath,则只有当客户端证书从配置中指定的CA 参考文件/路径中的CA 释放时,Apache 才会获取客户端证书。

【讨论】:

【参考方案2】:

首先,您需要配置 Apache Httpd 以请求客户端证书。为此,您至少需要在要使用此方法进行身份验证的位置/目录上使用SSLVerifyClient optional

其次,客户端发送的证书也需要被 Apache Httpd 信任。 (您原则上可以使用SSLVerifyClient optional_no_ca,让任何客户端证书通过 Apache Httpd SSL/TLS 堆栈,然后才在 PHP 中验证证书,但这是一项相当多的工作,您需要更加小心因为这不一定是简单的代码;更重要的是,在这种情况下这将毫无用处,因为您处于控制 CA 的场景中。)

据我了解,SSL_CLIENT_VERIFY(我自己并没有经常使用的变量)似乎只对SSLVerifyClient optional_no_ca 真正有用。它可能适用于SSLVerifyClient optional,但我对此表示怀疑。 SSLVerifyClient require 将拒绝使用不受信任的客户端证书(由 SSLCACertificateFile/SSLCACertificatePath 中的 CA 之一)或没有证书的连接。据我所知,SSLVerifyClient optional 会让客户端在没有证书或受信任证书的情况下通过,但如果证书不受信任,也会拒绝连接。

这里,通过拒绝连接,我的意思是突然关闭 SSL/TLS 连接并发出警报。没有机会产生 HTTP(S) 错误页面。所有你会得到标准浏览器错误,类似于ssl_error_unknown_certificate_...。 (您应该从可用性的角度考虑这一点。)

从那时起,您需要建立自己的 CA,可能是基于 Web 的、带有浏览器内密钥生成功能的 CA,并且在同一网站内。你不会想要SSLVerifyClient require,因为你需要让还没有证书的用户进入(改用optional)。话虽如此,这些指令不必适用于整个主机,但可以特定于某些位置/目录。

如果您不熟悉这一切,那么集成您自己的基于 Web 的 CA(或更一般地说,创建您自己的 CA)并不一定容易。存在现成的工具(例如OpenCA),或者您可以使用javascript/ActiveX 的各种位构建自己的工具,并且您需要服务器端代码来处理SPKAC 或PKCS#10 请求(并发出实际的证书)。 (为了使这样的 CA 有用,您希望申请新证书的用户在申请时提供一些身份证明,也许是密码。)

设置完成后,您应该将SSLCACertificateFile(或...Path)配置为指向您的内部CA 的CA 证书(无论它是否是基于Web 的CA,是否在同一站点上)。 (当然,保持 CA 的私钥是私有的,可能在基于 Web 的 CA 应用程序中配置,但 Apache Httpd 本身不需要知道它。)浏览器只会建议由这些 CA 或中间机构颁发的证书(除非您还配置了SSLCADNRequestFile,它将用于发送接受的 CA 列表。

请注意,这两个步骤(设置您的 CA 和设置您的网站以使用客户端证书)实际上是独立的。两者都可以是同一站点的一部分这一事实可能很方便,但不是必需的。您可以在不首先在站点上部署整个 CA 的情况下尝试 Apache Httpd 设置(我建议您这样做,即使它只是为了看看您要进入的内容)。有许多工具可以创建您自己的小型 CA,只需少量证书即可管理:例如 OpenSSL's CA.pl 或 TinyCA。你也可以使用这些test certificates(localhosttestclienttestclient_r如果你想使用CRL,可能一开始不需要):所有密码都是testtest

正如您已经预料到的(使用您的 MySQL 数据库),您需要管理您颁发的证书并将它们映射到用户。不过,SSL_CLIENT_M_SERIALSSL_CLIENT_I_DN 不是正确的变量。 SSL_CLIENT_I_DN 是颁发者 DN(即 CA 的主题 DN)。您要查找的是 SSL_CLIENT_S_DN:客户端证书主题 DN。 SSL_CLIENT_M_SERIAL 是证书序列号:不要使用它,因为它对每个证书都是唯一的:一个用户可以拥有多个具有相同主题 DN 的证书(例如,如果一个证书过期或被吊销)。


尽管如此,我不确定客户证书是否是实现您目标的最佳方式(让您公司的员工无需密码即可登录)。

首先,用户无论如何都应该使用密码保护自己的证书。我猜你真正追求的是某种形式的单点登录 (SSO)。

其次,根据用户的计算机知识程度,证书实际上可能很难管理。

严格来说,“证书”一词根本不包括私钥,但有时暗示私钥的使用可能会让某些人感到困惑。一方面,您有时会听到“将您的证书导入浏览器”和“使用您的证书登录”;另一方面,您也可以听到“将您的证书发送给我”。前者意味着私钥的使用和可用性(“证书”在这些表达式中可能仅表示.p12)。后者绝对不应该涉及私钥。

浏览器用户界面在管理证书或注销方面往往很差或令人困惑。同样,如果证书未被识别,SSL/TLS 连接将无法建立,因此 Web 服务器没有机会显示任何类型的 html 错误页面。

也许您还可以考虑其他形式的 SSO(例如 CAS、基于 SAML 的东西或 Kerberos/SPNEGO。)

【讨论】:

【参考方案3】:

如果尚未完成,您可以查看apache doc。

一般原则是您创建自签名证书并在尝试使用之前对其进行检查。

然后看起来客户端通过http连接到您的Intranet站点。从那里,有许多不同的方法可以使用您的 ssl 证书切换到 https。最简单的方法是使用 apache rewrite 模块。但是在您的情况下,当您进行 php/mysql 检查时,您可能会将客户端从 http 重定向到 https,这不是简单的方法。

在任何一种情况下(通过 mod_rewrite 的 apache 自动重定向,或通过级联测试 (php/javascript/html) 进行重定向),您需要以正确的方式设置 2 个虚拟主机(一个用于 http,一个用于 https),但这假设了一些假设。

例如(debian - apache 2.2),这是一个自动重定向,由 Apache 完成(例如上面描述的第一种情况):

cat /etc/apache2/sites-available/test

# VHOST test

<VirtualHost *:80>

    DocumentRoot /home/www/test
    ServerName www.test.dev

    # ######################
    # Redirect commons
    # ######################
    RewriteEngine on
    # Case of vhosts
    RewriteOptions Inherit

    # ######################
    # Redirect (empty index)
    # ######################

    # Condition 1 to redirect : request matching with the server
    RewriteCond %HTTP_HOST   ^www\.test\.dev [NC]

    # Condition 2 to redirect : non empty HOST
    RewriteCond %HTTP_HOST   !^$

    # Automatic Empty requests Redirect
    RewriteRule ^(.*)/$ /index.php


    # ######################
    # Redirect to SSL
    # ######################
    RewriteCond %HTTP_HOST   ^www\.test\.dev [NC]
    RewriteCond %HTTP_HOST   !^$
    RewriteCond %SERVER_PORT ^80$
    RewriteCond %REQUEST_URI /

    # Redirection
    RewriteRule ^/(.*)$ https://%SERVER_NAME%REQUEST_URI [L,R]

</VirtualHost>

SSL 的第二个虚拟主机: cat /etc/apache2/sites-available/test-ssl

# VHOST for ssl

DocumentRoot "/home/www/test"
ServerName www.test.dev

    # SSL
    SSLEngine on
    SSLCACertificateFile /etc/apache2/ssl/cur_cert/ca.pem
    SSLCertificateFile /etc/apache2/ssl/cur_cert/serveur.pem
    SSLCertificateKeyFile /etc/apache2/ssl/cur_cert/serveur.key


    <Directory "/home/www/test">
        Options FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from 127.0.0.1 192.168.0.0/16
    </Directory>


    <Directory "/home/www/test/cgi-bin">
        Options FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        Allow from 127.0.0.1 192.168.0.0/16
        Options +ExecCGI
        AddHandler cgi-script .cgi
    </Directory>

</VirtualHost>

您的情况可能与此稍有不同,例如,您不会在第一个 vhost 中拥有重定向部分,而只有一个简单的 vhost 和第二个用于 https (ssl) 的 vhost。完成 mysql 检查后,重定向将由 php/javascript 完成。

这是一个来自 php 类的示例摘要,用于使用 php,然后是 javascript,然后是 html 级联从 http 到 https 的切换:

public function Redirect($url)

    if (TRUE !== Validator::isValidURL($url))
        die ("FATAL ERR: url not valid");

    // PHP ABSOLUTE URL REDIRECT (HTTP1.1)
    if (!headers_sent()) 

        header("Status: 200");
        header("Cache-Control: no-cache, must-revalidate"); // required for HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // past Date
        header("Pragma: no-cache");
        header('Location: '.$url); // note: 302 code return by default with "Location"
        flush();
        exit();

        // if headers are already sent... do javascript redirect... if javascript is disabled, do html redirect.
     else 

        // Js redirect
        echo '<script type="text/javascript">';
        //echo "<!--";
        echo 'document.location="'. $url .'";';
        //echo "//-->";
        echo '</script>';

        // HTML redirect if js disabled
        echo '<noscript>';
        echo '<meta http-equiv="refresh" content="0;url="'.$url.'" />';
        echo '</noscript>';

        exit();
    

    return FALSE;

 /* end of method (redirect) */

希望它能帮助您更好地了解如何继续并根据您的具体情况调整这种方法。

【讨论】:

但我遇到的问题与重定向无关。我正在通过 https 直接访问该页面。 IE。在浏览器中,我使用的是 https://。所以 80 端口不是问题。 我尝试使用 openssl 在服务器上创建客户端证书,导出为 p12,然后在 Firefox 中安装它,但现在我从 Apache 收到“无法获取本地颁发者证书”错误。我使用服务器证书签署了客户端证书,为什么它不起作用?我尝试在 ssl.conf 中取消注释 SSLCACertificateFile 行,但这只是让我回到第一个问题,即页面加载正常但没有识别客户端证书。 对不起,我现在更好地理解了你的观点,除了在登录后完成 mysql 检查后连接 http 和切换 https 之外,没有其他简单的解决方案,这对我来说看起来是一种更安全的方法,但对我来说是一个相当大的代码消费者凭经验。 这与问题无关。

以上是关于Apache 不会请求我的 SSL 客户端证书的主要内容,如果未能解决你的问题,请参考以下文章

Apache 2、SSL 和客户端证书

使用ssl和客户端证书的Wcf:请求svc succes wcf调用返回403.16

Apache Http 客户端 SSL 证书错误

ssl原理

Linux apache自建证书搭建https

禁用SSL证书检查双绞线代理