SSL/TLS 高强度加密: 常见问题解答

优质
小牛编辑
134浏览
2023-12-01

智者并不给出正确的答案,而是提出正确的问题。

-- Claude Levi-Strauss

本文收集了一些USENET上常见的问题及其解答。这些问题主要来自comp.infosystems.www.servers.unix新闻组和mod_ssl的邮件列表modssl-users@modssl.org,我们把这些经常被问到的问题集中在这里是为了避免一次又一次不停的回答同样的问题。

在安装mod_ssl之前,强烈建议你首先阅读一下本文。此外,如果你有任何问题打算提交,请务必首先在这里找找是否已经有了解答。

关于这个模块

  • mod_ssl 简史
  • mod_ssl会受到Wassenaar Arrangement(瓦森纳协议)的影响吗?

mod_ssl 简史

mod_ssl v1 最早在1998年4月由Ralf S. Engelschall基于Ben LaurieApache-SSL 1.17 源代码补丁(Apache 1.2.6 to Apache 1.3b6)开发。因为和Ben Laurie的开发周期冲突,所以就在合并了旧有代码之后,为Apache 1.3.0重新开发。从此之后,mod_ssl就按照自己的生命周期独立开发了,并在1998年8月10号发布了mod_ssl 2.0.0版本。

在美国加密软件出口限制放松之后,mod_ssl成为了Apache HTTP Server 2.0的一个官方模块。

mod_ssl会受到Wassenaar Arrangement(瓦森纳协议)的影响吗?

首先让我们解释一下什么是Wassenaar(瓦森纳)以及对常规/军用双用途的货物及技术的出口控制协议:这是一项1995年达成的、在自愿基础上的集团性出口控制机制,其根本目的在于提高常规武器和双用途物品及技术转让的透明度,以达到对常规武器和双用途物品及相关技术转让的监督和控制。它是原CoCom(巴统)制度的替代品。更多细节请查看http://www.wassenaar.org/

简而言之,因为mod_ssl和OpenSSL都是位于公共域(public domain)中的软件,所以它们都不受Wassenaar Arrangement(瓦森纳协议)的影响。

安装

  • 为什么我在启动Apache时会收到与SSLMutex相关的权限错误?
  • 为什么启动Apache时,mod_ssl会在遇到"Failed to generate temporary 512 bit RSA private key"错误后停止?

为什么我在启动Apache时会收到与SSLMutex相关的权限错误?

诸如"mod_ssl: Child could not open SSLMutex lockfile /opt/apache/logs/ssl_mutex.18332 (System error follows) [...] System: Permission denied (errno: 13)"这样的错误通常是由于对目录的权限限制过分苛刻所致。请确保Apache子进程的UID对所有父目录(本例中是/opt,/opt/apache,/opt/apache/logs)都具有执行权限(参见User指令)。

为什么启动Apache时,mod_ssl会在遇到"Failed to generate temporary 512 bit RSA private key"错误后停止?

加密软件需要一个不可预知的随机数源才能正常工作。许多开源操作系统都提供了一个"随机数设备"(通常是/dev/random)。而在其他操作系统上,应用程序必须在生成密钥(key)或者执行公钥加密时使用OpenSSL伪随机数生成器(PRNG)手动生成一个随机数。从OpenSSL-0.9.5版起,如果PRNG未能生成128位以上的随机数,需要使用随机数的OpenSSL函数就会报告上述错误。

要阻止这种错误的出现,mod_ssl必须提供足够的熵(entropy)给PRNG用于生成足够长的随机数。这可以通过SSLRandomSeed指令实现。

配置

  • 可以使用同一个服务器同时提供HTTP和HTTPS服务吗?
  • HTTPS究竟使用的是什么端口?
  • 出于测试目的,我如何手动使用HTTPS进行会话?
  • 为什么当我连接到配置了SSL支持的Apache服务器时会出现连接挂起的情况?
  • 为什么我在通过HTTPS访问新安装的Apache+mod_ssl服务器时会遭遇"Connection Refused"(连接被拒绝)的错误?
  • 为什么SSL_XXX变量在我的CGI或SSI脚本中不可用?
  • 如何在使用相对路径的超链接之间切换HTTP/HTTPS协议?

可以使用同一个服务器同时提供HTTP和HTTPS服务吗?

当然可以!HTTP和HTTPS使用的是不同的服务端口(HTTP位于80端口,而HTTPS位于443端口),所以它们之间并没有什么直接的冲突。你既可以在这两个端口上分别运行两个Apache实例,也可以在同一个Apache实例上配置两个虚拟主机:一个在80端口应答HTTP请求,另一个在443端口应答HTTPS请求。

HTTPS究竟使用的是什么端口?

事实上,你可以在任何端口运行HTTPS服务,但是默认的标准端口是443。443端口也是浏览器默认连接的HTTPS端口。当然,你也可以在URL中强制浏览器使用非默认的端口,比如使用666端口的话,你可以这样:https://secure.server.dom:666/

出于测试目的,我如何手动使用HTTPS进行会话?

通常情况下,你会这样使用HTTP与服务器进行会话:

$ telnet localhost 80
GET / HTTP/1.0

但是,使用HTTPS就不那么简单了,因为在TCP和HTTP之间多了一层SSL协议的保护。但使用OpenSSL提供的s_client命令行工具,你同样可以像以前一样简单的使用HTTPS和服务器进行会话:

$ openssl s_client -connect localhost:443 -state -debug
GET / HTTP/1.0

在真正的HTTP应答到来之前,你会首先看到SSL握手的相关信息。cURL是一个更通用的命令行客户端,它能直接理解HTTP/HTTPS协议、执行GET/POST操作、使用代理等等。你可以像下面这样使用这个工具来检查Apache是否能够在80和443作出正确的应答:

$ curl http://localhost/
$ curl https://localhost/

为什么当我连接到配置了SSL支持的Apache服务器时会出现连接挂起的情况?

因为你使用了HTTP协议去连接了HTTPS端口,也就是你使用了"http://"而不是"https://"这样的URL;或者相反,你使用了HTTPS协议去连接了HTTP端口,也就是你使用了"https://"去连接一个并不支持SSL的端口。你必须确保的确连接到了一个支持SSL的虚拟主机,它可能是关联到你的主机名的IP地址,而不是"localhost"(127.0.0.1)。

为什么我在通过HTTPS访问新安装的Apache+mod_ssl服务器时会遭遇"Connection Refused"(连接被拒绝)的错误?

可能有多个原因。最常见的原因是启动Apache时使用的是apachectl start(或httpd)命令而不是apachectl startssl(或httpd -DSSL);还有一个原因是你的配置不正确。你必须保证Listen指令和<VirtualHost>相匹配。如果仍然失败,那么可以再使用mod_ssl提供的默认配置重启服务器试一试。

为什么SSL_XXX变量在我的CGI或SSI脚本中不可用?

请确保为使用这些变量的CGI/SSI脚本设置了"SSLOptions +StdEnvVars"。

如何在使用相对路径的超链接之间切换HTTP/HTTPS协议?

通常,必须使用全限定超链接(因为你改变了URL模式)才能在HTTP和HTTPS之间切换,但是mod_rewrite可以协助你在使用相对路径的超链接之间切换HTTP/HTTPS协议:

RewriteEngine on
RewriteRule ^/(.*):SSL$ https://%{SERVER_NAME}/$1 [R,L]
RewriteRule ^/(.*):NOSSL$ http://%{SERVER_NAME}/$1 [R,L]

这个重写规则可以将类似<a href="document">这样的超链接切换到HTTPS连接。

证书

  • 什么是RSA私钥(RSA Private Key)、CSR(证书签发请求)、证书(Certificates)?
  • 配置了SSL支持的Apache和普通的Apache在启动时有什么不同吗?
  • 如何创建一个自签名的SSL证书用于测试?
  • 如何创建一个真正的SSL证书?
  • 如何创建一个我自己使用的认证中心(CA,Certificate Authority)?
  • 如何更改私钥文件的密语(pass-phrase)?
  • 如何去除Apache启动时要求输入密语(pass-phrase)的提示信息?
  • 如何验证一个私钥文件是否与一个证书相匹配?
  • 为什么连接会出现类似"alert bad certificate"这样的错误信息?
  • 为什么我的2048位私钥不能正常使用?
  • 为什么在将SSLeay从0.8版本升级到0.9之后客户端认证会失败?
  • 如何将PEM格式的证书转换成DER格式?
  • 为什么我在安装Verisign签发的证书时找不到Verisign提及的getcagetverisign程序?
  • mod_ssl可以配合SGC(Server Gated Cryptography)技术(也就是Verisign Global ID)一起使用吗?
  • 为什么浏览器会抱怨说无法校验我的Verisign Global ID服务器证书?

什么是RSA私钥(RSA Private Key)、CSR(证书签发请求)、证书(Certificates)?

RSA私钥(RSA Private Key)文件是一个数字文件,它用于解密你所接收到的信息。此私钥有一个对应的、被封装在你发布出去的证书文件中的公钥,而给你发送信息的人正是用这个公钥对发送的信息进行加密的。

一个证书签发请求(CSR,Certificate Signing Request)是一个数字文件,它包含了你的公钥和你的名字。你把这个CSR发送给认证中心(CA)后,CA将会对其进行签名(译者注:使用CA自己的私钥对你的公钥和名字进行加密),并将其转化为一个真正的证书。

证书(Certificate)包含了你的RSA公钥、你的名字、CA的名字,并且经过了CA的数字签名。能够识别这个CA的浏览器可以校验这个证书上的签名,从而就可以获取你的RSA公钥。这样浏览器就可以发送只有你能够解密的信息。

参见SSL简介以获取对SSL协议的更详细介绍。

配置了SSL支持的Apache和普通的Apache在启动时有什么不同吗?

一般说来没什么不同,但是如果你使用的SSL私钥文件带有密语(passphrase)保护,那么Apache将会在启动时要求你输入密语。

必须在Apache启动时输入密语可能会造成一些麻烦:比如你无法在系统的启动脚本中启动Apache服务。当然,你也可以使用下面的办法去除私钥文件上的密语。

如何创建一个自签名的SSL证书用于测试?

  1. 首先确认OpenSSL已经被正确安装,并且openssl可执行文件位于PATH环境变量所包含的目录中。
  2. 运行下面的命令来创建server.keyserver.crt文件:
    $ openssl req -new -x509 -nodes -out server.crt -keyout server.key
    然后在httpd.conf中这样使用它们:
         SSLCertificateFile    /path/to/this/server.crt
         SSLCertificateKeyFile /path/to/this/server.key
    	
  3. 注意:你必须确保server.key文件没有被加上密语保护。如果你确实想对上面得到的私钥加上密语进行保护,可以使用下面的命令,并按屏幕提示连续输入两次相同的字符串作为密语。

    $ openssl rsa -des3 -in server.key -out server.key.new
    $ mv server.key.new server.key

    请务必将你输入的密语和server.key文件备份到一个安全的地方。

如何创建一个真正的SSL证书?

下面是一个按部就班的过程描述:

  1. 首先确认OpenSSL已经被正确安装,并且openssl可执行文件位于PATH环境变量所包含的目录中。
  2. 创建一个RSA私钥文件(这里的例子使用3DES加密并封装为PEM格式):
    $ openssl genrsa -des3 -out server.key 1024
    请务必将你输入的密语和server.key文件备份到一个安全的地方。你可以使用下面的命令查看这个RSA私钥的详细信息:
    $ openssl rsa -noout -text -in server.key
    如果确有必要,你也可以为这个RSA私钥创建一个已解密的PEM版本(不推荐):
    $ openssl rsa -in server.key -out server.key.unsecure
  3. 使用这个RSA私钥创建一个证书签发请求(CSR)(这里的例子将封装为PEM格式):
    $ openssl req -new -key server.key -out server.csr
    请务必确信在OpenSSL提示你输入"CommonName"时,你输入的是正确的全限定域名(FQDN,Fully Qualified Domain Name)。举例来说,如果你想为一个将来要通过https://www.foo.dom/访问的站点生成CSR,那么你就必须在这里输入"www.foo.dom"。你可以使用下面的命令来查看这个CSR的详细信息:
    $ openssl req -noout -text -in server.csr
  4. 现在,你必须将你的证书签发请求(CSR)发送给一个认证中心(CA)等待被签名。一旦CSR被CA签名之后,你将收到一个真正的证书(Certificate),这个证书就可以被Apache使用了。当然,你可以将CSR发送给一个专门提供证书服务的商业CA,也可以创建一个你自己的CA来对CSR进行签名。
    专门提供证书服务的商业CA通常要求你通过web表格提交CSR,然后付费,最后才给你签发证书。收到的证书就可以存储为server.crt文件,供Apache使用了。
    可以通过下列网站了解更多商业CA的信息,这些都是它们的官网:
    1. Verisign
      http://digitalid.verisign.com/server/apacheNotice.htm
    2. Thawte
      http://www.thawte.com/
    3. CertiSign Certificadora Digital Ltda.
      http://www.certisign.com.br
    4. IKS GmbH
      http://www.iks-jena.de/leistungen/ca/
    5. Uptime Commerce Ltd.
      http://www.uptimecommerce.com
    6. BelSign NV/SA
      http://www.belsign.be
    要想了解如何创建你自己的CA,以及如何用它对CSR进行签名,请看后面的内容。
    一旦你的CSR被签了名,就可以使用下面的命令查看证书的详情:
    $ openssl x509 -noout -text -in server.crt
  5. 你现在应该有了server.keyserver.crt两个文件,现在可以在httpd.conf中这样使用它们:
        SSLCertificateFile    /path/to/this/server.crt
        SSLCertificateKeyFile /path/to/this/server.key
           
    server.csr文件已经没用了,你可以删除它。

如何创建一个我自己使用的认证中心(CA,Certificate Authority)?

简单的答案是使用OpenSSL提供的CA.shCA.pl脚本。除非你有更好的理由,否则这两个脚本是首选。如果你不会或不能使用这两个脚本,那么还可以按照下面的步骤创建一个自签名的证书:

  1. 首先确认OpenSSL已经被正确安装,并且openssl可执行文件位于PATH环境变量所包含的目录中。
  2. 创建一个RSA私钥文件(这里的例子使用3DES加密并封装为PEM格式):
    $ openssl genrsa -des3 -out server.key 1024
    请务必将你输入的密语和server.key文件备份到一个安全的地方。你可以使用下面的命令查看这个RSA私钥的详细信息:
    $ openssl rsa -noout -text -in server.key
    如果确有必要,你也可以为这个RSA私钥创建一个已解密的PEM版本(不推荐):
    $ openssl rsa -in server.key -out server.key.unsecure
  3. 使用你刚才创建的RSA私钥创建一个自签名的X509证书(PEM格式):
    $ openssl req -new -x509 -nodes -sha1 -days 365 -key server.key -out server.crt
    这将对服务器的CSR进行签名,并将生成一个server.crt文件。
    可以使用下面的命令查看这个证书的详细信息:
    $ openssl x509 -noout -text -in server.crt

如何更改私钥文件的密语(pass-phrase)?

你必须使用老的密语读取私钥文件,然后再指定新的密语写入。可以用下面的命令完成这个操作:

$ openssl rsa -des3 -in server.key -out server.key.new
$ mv server.key.new server.key

这个命令执行过程中,会首先向你询问旧的PEM密语,输入正确之后,会再向你询问新的密语,你应该输入两次新密语,并确保两次输入都准确无误。

如何去除Apache启动时要求输入密语(pass-phrase)的提示信息?

要求输入密语的原因是server.key文件中的RSA私钥是出于安全考虑而被加密存储的,而密语就是用来解密私钥的。解除密语的保护就剥去了一层安全保护,所以做这个操作的时候请三思而后行!

  1. 首先备份原始RSA私钥文件,然后再去除RSA私钥文件上的密语保护:
    $ cp server.key server.key.org
    $ openssl rsa -in server.key.org -out server.key
  2. 确保server.key只能被root读取:
    $ chmod 400 server.key

现在,server.key中就包含了一份未加密的私钥。如果你让Apache使用这个文件,那么就不会在启动的时候提示输入密语了。但是如果有任何其他人获得了这个未加密的私钥文件,那么他就可以冒充你的身份。所以你必须确保只有root用户可以读取它,然后以root启动Apache并以其他用户身份运行Apache的子进程。

另一种选择是使用"SSLPassPhraseDialog exec:/path/to/program"功能,但是需要切记的是这种做法并不能获得更高的安全性(当然也不更低)。

如何验证一个私钥文件是否与一个证书相匹配?

每个私钥文件都包含一连串数字,其中有两串数字来源于对应的公钥。当你生成CSR的时候,这两串数字将被包含在其中。自然,当CSR被签署成证书后,这两串数字仍然被包含在证书中。

因此要验证一个私钥文件是否与一个证书相匹配,只要检查证书中的这两串数字是否与私钥中的相同即可。可以使用下面这两个命令查看:

$ openssl x509 -noout -text -in server.crt
$ openssl rsa -noout -text -in server.key

私钥和证书输出的"modulus"和"public exponent"两个部分必须互相匹配。"public exponent"的值通常都是"65537",但是"modulus"的值却是一串很长很长数字,检查起来非常吃力。所以,你也可以使用下面的命令,通过检查其MD5值是否一致来判断:

$ openssl x509 -noout -modulus -in server.crt | openssl md5
$ openssl rsa -noout -modulus -in server.key | openssl md5

这个方法允许你通过比较两串较短的MD5值来判断两个较长的"modulus"值是否相等。理论上,可能会出现两个"modulus"值不等,但MD5值相等的可能。但在实践中,这种可能性基本为零。

如果你想检查一个CSR与一个私钥或证书是否匹配,也可以用相同的办法:

$ openssl req -noout -modulus -in server.csr | openssl md5

为什么连接会出现类似"alert bad certificate"这样的错误信息?

SSL日志中诸如"OpenSSL: error:14094412: SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate"这样的错误,通常是由于不能处理服务器证书/私钥的浏览器导致的。比如古董级别的Netscape Navigator 3.x就不能处理长度不等于1024的RSA密钥。[译者注]:IE直到7.0都不支持大于1024位RSA密钥。

为什么我的2048位私钥不能正常使用?

为了兼容某些特别的浏览器,建议你只使用1024位RSA密钥,否则这些浏览器将不能正常工作。[译者注]:IE直到7.0都不支持大于1024位RSA密钥,但Firefox2/Opera9甚至可以支持到4096位(也许更长)的RSA密钥。

为什么在将SSLeay从0.8版本升级到0.9之后客户端认证会失败?

放在SSLCACertificatePath指令目录下的CA证书会被SSLeay通过"openssl x509 -noout -hash"命令得到的哈希值进行索引。但是SSLeay 0.8与0.9版本之间的哈希算法并不相同,因此你必须在升级之后使用mod_ssl提供的Makefile更新哈希值。

如何将PEM格式的证书转换成DER格式?

SSLeay/OpenSSL默认的证书格式是PEM,它是经过Base64编码的DER再加上页眉页脚线组成的。而某些应用程序(比如Microsoft Internet Explorer)却只能识别原始的DER格式。你可以使用下面的命令将PEM格式的cert.pem证书转换成DER格式的cert.der证书:
$ openssl x509 -in cert.pem -out cert.der -outform DER

为什么我在安装Verisign签发的证书时找不到Verisign提及的getcagetverisign程序?

Verisign从未将相应的详细规范提供给Apache+mod_ssl,因此Apache并不提供这两个程序。而C2Net的Stronghold(一个商业版本的、带有SSL支持的Apache)提供这两个程序。

为了安装证书,你要做的所有事情就是将证书保存在一个文件里面,并将SSLCertificateFile指令指向这个文件。当然,你还需要将对应的私钥文件保存到SSLCertificateKeyFile指令指定的地方。

mod_ssl可以配合SGC(Server Gated Cryptography)技术(也就是Verisign Global ID)一起使用吗?

当然可以!mod_ssl从2.1版本起就支持SGC技术了,并且不需要任何特殊的配置——只要使用Global ID作为你的服务器证书即可。客户端浏览器的提升工作会由mod_ssl在运行时自动处理。

为什么浏览器会抱怨说无法校验我的Verisign Global ID服务器证书?

Verisign在根CA证书(已安装在浏览器里面)和服务器证书(安装在服务器上)之间使用了一个中间CA证书。正常情况下,你应当从Verisign收到这个额外的CA证书。如果没有,浏览器就会发出这个抱怨了。这时,你需要正确设置SSLCertificateChainFile指令,这样才能确保将这个中间CA证书发送给浏览器,从而将证书链填充完整。

SSL 协议

  • 为什么在服务器负载非常重的时候会出现随机性的SSL协议错误?
  • 为什么服务器在处理SSL加密连接的时候负载会很重?
  • 为什么在使用HTTPS协议的情况下,有时候需要花上30秒才能建立一个连接?
  • mod_ssl支持哪些SSL加密算法?
  • 为什么使用匿名Diffie-Hellman(ADH)算法时会收到"no shared cipher"错误?
  • 为什么连接到新安装的Apache-SSL服务器时会收到"no shared ciphers"错误?
  • 为什么不能在基于域名的虚拟主机上使用SSL?
  • 为什么不可以使用基于域名的虚拟主机来标识不同的SSL虚拟主机?
  • 如何启用SSL压缩?
  • 当我通过HTTPS使用基本认证的时候,Netscape浏览器状态栏上的锁图标处于打开状态,这是否意味着我的用户名和密码是明文传递的?
  • 为什么使用Microsoft Internet Explorer通过HTTPS去连接Apache+mod_ssl服务器时会出现I/O错误?
  • 为什么使用Netscape Navigator通过HTTPS去连接Apache+mod_ssl服务器时会出现I/O错误或"Netscape has encountered bad data from the server"错误?

为什么在服务器负载非常重的时候会出现随机性的SSL协议错误?

可能有多种原因,但是最可能的原因是由于将SSLSessionCache指定为使用DBM会话缓存所致。因此,使用SHM会话缓存或者根本不使用SSL会话缓存有助于解决这个问题。

为什么服务器在处理SSL加密连接的时候负载会很重?

SSL使用的强加密算法需要消耗大量CPU资源进行大数运算。当你通过HTTPS访问一个页面时,每一个元素(包括图片和多媒体资源)都会被加密传输。所以,HTTPS流量越大,负载就会越重。

为什么在使用HTTPS协议的情况下,有时候需要花上30秒才能建立一个连接?

这通常是由于在使用read(2)读取SSLRandomSeed指定的/dev/random设备时,设备暂时无法提供足够多的随机位,操作被一直阻塞所致。更多细节可以参考手册中对SSLRandomSeed指令的详细说明。

mod_ssl支持哪些SSL加密算法?

通常,所有OpenSSL支持的SSL加密算法mod_ssl都支持,具体取决于你的OpenSSL是怎样编译的。可以使用下面的命令列出实际究竟可以使用哪些加密算法:

$ openssl ciphers -v

为什么使用匿名Diffie-Hellman(ADH)算法时会收到"no shared cipher"错误?

默认情况下,出于安全原因,OpenSSL并启用ADH算法。仅在你确实明白了这个算法的副作用时,你才可以启用此算法。

为了使用匿名Diffie-Hellman(ADH)算法,你必须在编译OpenSSL时使用"-DSSL_ALLOW_ADH"配置选项,并在SSLCipherSuite指令中添加"ADH"。

为什么连接到新安装的Apache-SSL服务器时会收到"no shared ciphers"错误?

或者是SSLCipherSuite指令配置错误(你可以和预配置的httpd.conf-dist比对一下);或者是你在生成私钥的时候使用了DSA/DH算法而不是RSA,并且忽略了警告信息。如果使用了DSA/DH的话,那么你的服务器就不能使用基于RSA的SSL加密算法进行通信(至少直到你配置了一个额外的RSA证书/密钥对为止)。现在的浏览器,比如NS或IE只能和使用RSA加密算法的SSL服务器通信,这也是导致"no shared ciphers"错误原因之一。解决这个错误的最佳途径就是使用RSA算法重新生成服务器证书和密钥对。

为什么不能在基于域名的虚拟主机上使用SSL?

原因很技术化,有点类似于"鸡和蛋"的问题。SSL协议层位于HTTP协议层之下,HTTP协议是被封装在SSL协议中的,当一个SSL连接(HTTPS)请求到来的时候,Apache/mod_ssl必须和客户端协商SSL协议参数(握手),所以,mod_ssl必须查看虚拟主机的配置信息(例如允许使用哪些加密算法、服务器证书是哪个等等),然后才能完成SSL会话通道的建立;但另一方面,为了确切知道究竟应该查看哪个虚拟主机,Apache又必须知道HTTP请求头的Host字段的内容,而这在完成SSL握手之前是不可能知道的。

为什么不可以使用基于域名的虚拟主机来标识不同的SSL虚拟主机?

基于域名的虚拟主机是用来标识不同虚拟主机的流行方法。这种方法允许多个不同的站点使用同一个IP地址和端口对。当人们将站点转移到SSL时,很自然的就认为可以使同相同的办法在同一台机器上运行多个SSL虚拟主机。

但事实是:没门!

原因在于SSL协议层位于HTTP协议层之下,HTTP协议是被封装在SSL协议中的,所以SSL会话必须在HTTP会话之前建立。因为在建立SSL会话的最初握手阶段,服务器无法知道HTTP请求头的Host字段的内容,也就无法确定究竟使用哪个虚拟主机的配置(例如允许使用哪些加密算法、服务器证书是哪个等等),于是Apache就会使用匹配这个IP地址端口对的第一个主机的SSL配置。

当然,你也可以在同一个IP地址上使用基于域名的虚拟主机来标识多个非SSL虚拟主机(例如全运行在80端口),同时再运行一个SSL虚拟主机(例如在443端口)。不过如果你确实这么做了的话,一定要确保在NameVirtualHost指令里明确指定端口号,像这样:

NameVirtualHost 192.168.1.1:80

另外一个解决方案:为不同的SSL虚拟主机使用不同的IP地址端口对。

如何启用SSL压缩?

虽然SSLv2和TLS规范定义了SSL压缩协商,但是直到2004年5月,RFC 3749才将DEFLATE定义为可协商的压缩方法。

从0.9.8版本开始,如果编译时使用了zlib选项,那么OpenSSL将默认支持SSL压缩。如果客户端和服务器同时支持压缩,那么压缩将被启用。但是,当前的大多数客户端仍然会在建立SSL会话的最初尝试使用SSLv2,而SSLv2在握手阶段并不协商压缩算法,所以对于使用SSLv2的客户端,实际上是无法使用压缩的。如果客户端使用SSLv3或TLS建立连接,那么就可以启用压缩。你可以使用%{SSL_COMPRESS_METHOD}x变量在日志中记录客户端是否利用了压缩功能。

当我通过HTTPS使用基本认证的时候,Netscape浏览器状态栏上的锁图标处于打开状态,这是否意味着我的用户名和密码是明文传递的?

非也!你的用户名和密码依然是加密传输的。浏览器上的图标事实上并没有和SSL/TLS同步,它仅在开始实际传输web页面的时候才锁上,这会给用户带来一些困惑。基本认证是HTTP层的特性,而HTTP层位于SSL层之上(所以被称为HTTPS),所以任何HTTP会话开始之前,SSL层已经完成握手并建立了加密信道。所以不要被这个图标所迷惑。

为什么使用Microsoft Internet Explorer通过HTTPS去连接Apache+mod_ssl服务器时会出现I/O错误?

第一个原因是某些版本MSIE的SSL实现存在与HTTP/1.1的keep-alive特性相关的BUG。可以通过在SSL虚拟主机的配置段中设置下列指令来解决这个问题:

SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0

此外,某些版本MSIE的加密算法实现也存在问题,并且没有办法通过SetEnvIf专门针对MSIE进行调整,唯一的解决办法就是修改全局设置。但是在你决定这么做的时候,最好先确保客户端确实出现了I/O错误,否则所有客户端都将受到影响。

第二个原因是仅支持56位加密的MSIE 5.x的SSLv3实现存在BUG。解决方案之一是将OpenSSL降到0.9.4版本以下(不推荐)。另一个解决方案是完全禁止使用SSLv3协议:

SSLProtocol all -SSLv3

而最佳解决方案则是只对那些仅支持56位加密浏览器禁止使用SSLv3协议:

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP

这样那些带有BUG的MSIE依然可以正常工作,只是移除了新的56位TLS加密算法。

另外一个MSIE 5.x的问题是:如果服务器使用了SGC(Server Gated Cryptography)技术,那么它会拒绝连接到类似https://12.34.56.78/(使用IP地址而不是主机名)这样的URL。解决这个问题只有一种方法:只在URL中使用全限定域名。

最后,某些版本的MSIE要求SSL会话可以被重用(当然,这一定是个完全不符合标准的行为)。对于这种版本的MSIE,唯一的解决之道就是使用SSLSessionCache指令开启SSL会话缓存。

为什么使用Netscape Navigator通过HTTPS去连接Apache+mod_ssl服务器时会出现I/O错误或"Netscape has encountered bad data from the server"错误?

这通常是因为你更改了服务器证书,并且客户端永久保存了老证书所致。只要客户端清除老证书,并接受新证书即可解决问题。Netscape的SSL实现是完全正确的,所以只有在更换服务器证书的时候才会遇见这种错误。

mod_ssl 支持

  • 当我遇到mod_ssl相关的问题时,有哪些文档资源可供参考?
  • 当我遇到mod_ssl故障时,可以到哪里寻求支援?
  • 在提交BUG报告时需要提供哪些信息?
  • 我有一个"core dump",你能帮我吗?
  • 如何得到backtrace以帮助发现"core dump"的原因?

当我遇到mod_ssl相关的问题时,有哪些文档资源可供参考?

当你遇到mod_ssl相关的问题时,你应该首先查询下面这些文档资源:

Apache手册中的"常见问题解答"(就是本文啦)
http://httpd.apache.org/docs/2.2/ssl/ssl_faq"http://www.modssl.org/support/">http://www.modssl.org/support/
在邮件列表归档中搜索你的问题,因为你的问题很可能在你之前已经有别的人先遇到过了。

当我遇到mod_ssl故障时,可以到哪里寻求支援?

下面按照优先级递减顺序列出了可以寻求支援的途径。请务必按顺序尝试,而不要按你的喜好随意选择一个。

  1. 向modssl用户支持邮件列表发邮件
    modssl-users@modssl.org
    这是寻求帮助的首选途径,因为通过这个途径其他人也能看到这个问题,并且可以从解答中学习新知识。当然,你必须首先订阅这个邮件列表。通过这个列表,你可以和整个mod_ssl社区(包括开发者)交流。
  2. 向Apache httpd用户支持邮件列表发邮件
    users@httpd.apache.org
    这是寻求帮助的次选途径。同样,你也必须首先订阅这个邮件列表。通过这个列表,你可以和整个Apache httpd用户社区交流。
  3. 向Bug数据库提交问题报告
    http://httpd.apache.org/bug_report"httpd -v"命令查看,OpenSSL版本可以通过"openssl version"命令查看。此外,如果你安装了Lynx的话,还可以通过"lynx -mime_header http://localhost/ | grep Server"命令同时查看这两个信息。
    编译和安装Apache+mod_ssl+OpenSSL的详细步骤
    你可以将整个编译和安装过程中的屏幕输出记录到一个日志文件。如果做不到,也至少需要你提供完整的configure命令行。
    在遇到"core dump"的时候,请包含一个相应的Backtrace
    如果你的Apache+mod_ssl+OpenSSL生成了一个"core dump"请务必附上相应的堆栈结构"backtrace"(参见下面的说明)。如果没有这个信息,就没办法找到"core dump"的原因。
    对你所遇到问题的尽可能详细的描述
    不管你是否相信,许多BUG报告确实根本没说清楚究竟遇到了什么问题。不说清楚你究竟遇到了什么问题,神仙也没办法帮你。所以你有义务尽可能详细的包含每一个细节,当然,前面说的也包括在内。

    我有一个"core dump",你能帮我吗?

    一般说来,不能。至少在你提供相应的backtrace之前不能(参见下一个问题)。没有backtrace很难找到问题出在哪里,也就无法帮你修复它。

    如何得到backtrace以帮助发现"core dump"的原因?

    请按照下面的步骤来获得backtrace:

    1. 确保你至少在Apache上启用了调试符号。在使用GCC/GDB的平台上,你必须在编译Apache+mod_ssl的时候使用"OPTIM="-g -ggdb3""编译器选项。在其他平台上,至少需要使用"OPTIM="-g""编译器选项。
    2. 启动服务器并再次触发core-dump,同时你可能还必须设置类似"CoreDumpDirectory /tmp"这样的指令以确保转储文件能够被正确的写入/tmp/core/tmp/httpd.core文件。如果你得不到转储文件,可以尝试以非root身份运行Apache服务器。因为许多操作系统出于安全考虑,不允许进程在setuid()之后再执行core-dump操作(除非还执行了exec())。如果有必要,还可以使用/path/to/httpd -X来强制禁止Apache执行fork操作。
    3. 运行gdb /path/to/httpd /tmp/httpd.core之类的命令分析这个core-dump。在GDB中,你需要做的所有事情就是输入bt,然后你就可以得到backtrace了。对于其他调试器,建议你查看相应的使用手册来了解如何得到backtrace。