使用自签名证书

8

我正在尝试理解SSL。

我在本地主机上设置了一个Jetty服务器,并使用Keytool生成了自己的证书。

现在当我访问https://localhost:8443/时,会出现无法信任此证书的错误。

我使用以下命令:

keytool -export -alias pongus -keystore keystore -file certfile.cer

创建证书,我认为这就是客户端需要用来验证服务器的证书。(这可能是我非常错误的地方!)

我有以下Ruby代码:

require 'net/https'
require 'openssl'

require 'open-uri'

puts 'yay' if File.exists?('certfile.cer')

uri = URI.parse("https://localhost:8443/")
http_session = Net::HTTP.new(uri.host, uri.port)
http_session.use_ssl = true
http_session.verify_mode = OpenSSL::SSL::VERIFY_PEER
http_session.ca_file = 'certfile.cer'
res = http_session.start do |http|
  # do some requests here
  http.get('/')
end

这会打印出“yay”,所以certfile.cer文件确实存在。

但是我遇到了错误

/Applications/NetBeans/NetBeans 6.8.app/Contents/Resources/NetBeans/ruby2/jruby-1.4.0/lib/ruby/1.8/net/http.rb:586 warning: can't set verify locations
/Applications/NetBeans/NetBeans 6.8.app/Contents/Resources/NetBeans/ruby2/jruby-1.4.0/lib/ruby/1.8/net/http.rb:586:in `connect': certificate verify failed (OpenSSL::SSL::SSLError)

你有什么想法我做错了什么吗?

编辑

我希望能够确保我连接到正确的服务器,并且服务器可以保证我连接到它,没有任何篡改。我正在开发服务器和客户端。

3个回答

7
您的客户需要访问它的私钥。
对于服务器证书验证,您不需要私钥。您只需要包含公钥的证书本身。只有服务器拥有私钥。 在这里http://www.helpbytes.co.uk/https.php和这里http://www.verisign.com/ssl/ssl-information-center/how-ssl-security-works/中有很好的说明。
我的建议很简单。检查您的证书是否正确。
openssl x509 -text -in mycert.crt

另外,如果您可以访问服务器,则可以明确验证证书和密钥(用于httpd配置)是否正确匹配:http://kb.wisc.edu/middleware/page.php?id=4064 请注意,这是在服务器上明确运行的检查。永远不要泄露私钥。只有管理员才能执行此检查,以验证httpd未被错误配置。

(openssl x509 -noout -modulus -in server.pem | openssl md5 ;\
   openssl rsa -noout -modulus -in server.key | openssl md5) | uniq

您也可以使用标准的openssl命令来调试SSL证书通信。输入此命令,等待几秒钟,然后键入QUIT并按回车键。您将看到服务器发送的证书。
openssl s_client -connect your.server.com:443

尝试导入证书到您的浏览器并访问URL资源。点击https(Firefox和Chrome)后,浏览器可以进行验证。然后您将看到证书本身和有效性信息。上述所有内容都是关于服务器证书的。这只是问题的一部分。“我正在连接正确的服务器,并且服务器可以保证是我在连接它。”实际上,在您上面的代码中,您只检查了服务器证书。现在,如果您想要客户端证书(您声明的第二部分),则需要在Ruby中使用以下内容:
File.open( "client_certificate.pem", 'rb' ) { |f| cert = f.read }
File.open( "client_key.pem", 'rb' ) { |f| key = f.read }
http_session.cert = OpenSSL::X509::Certificate.new(cert)
http_session.key = OpenSSL::PKey::RSA.new(key, nil)

这是如何在Ruby中使用客户端证书。如果您的私钥受密码保护,请在RSA构造函数的第二个参数中传递它,而不是nil。
我强烈建议先让服务器证书正常工作(您的代码),然后再开始使用客户端证书。请注意,您需要保留当前的代码(ca_cert、validation常量),并将上述四行代码添加到其中。
希望这可以帮助您。

不,Greg,你是正确的。他是在询问SSL客户端认证,而是遇到了SSL服务器证书验证问题。这是不同的。属性http_session.ca_file和http_session.verify_mode 用于SSL客户端证书。 - lzap
@Izap:你需要读一下他写的话:“……所以我保证我正在连接正确的服务器,而服务器也可以保证是我在连接它……”。这就是所谓的客户端认证。 - President James K. Polk
@GregS:他所说的和他的本意可能有所不同。 - Jumbogram
@Jumbogram:同意,这在这里经常是个问题。有时候你必须要读懂字里行间的意思。 - President James K. Polk
是的,我错过了“服务器可以保证连接到它的是我”的部分。我写的一切都还好,除了“This is not true”这句话。他需要两个。所以在A中,他需要服务器证书而不包括私钥,在B中,他需要客户端证书,包括他的私钥(当然)。对于混淆,我很抱歉,但我非常确定他只需要服务器证书(根据他的源代码)。 - lzap
显示剩余4条评论

0

您的客户需要访问其私钥。私钥不在证书中,证书仅包含公钥。很抱歉,我不了解Ruby,但常见的技术是将私钥和证书捆绑在单个PKCS#12文件(也称为p12文件),并将其提供给加密库。


好的,这是有用的信息让我有了方向!我会回到keytool文档。谢谢。 - Mongus Pong
Ruby可以接受PEM格式的证书。完全不需要p12和私钥。将PK分发出去是一个安全漏洞 :-) - lzap
1
@Izap:没有私钥,有两个:一个是客户端的,一个是服务器的。这就是为什么我说客户端需要访问它自己的私钥,而不是服务器的私钥。 - President James K. Polk
Greg,你正在试图混合两件事情。那个人要求针对CA证书进行SSL服务器证书验证。而你描述的是SSL用户认证授权。这是两件不同的事情。它们可以共存。 - lzap
抱歉,他确实需要一个私钥,但仅用于客户端证书。对于服务器证书,私钥存储在服务器上。请参阅我上面的帖子。 - lzap

-6

更改

http_session.verify_mode = OpenSSL::SSL::VERIFY_PEER

http_session.verify_mode = OpenSSL::SSL::VERIFY_NONE

一旦你这样做了,SSL 就会正常工作。我在我的开发环境中多次使用过这个方法,总是无故障地运行。


1
谢谢,但我不想绕过验证。;-) - Mongus Pong
很抱歉回复这个问题花费了这么长时间。由于VERIFY_PEER尝试将证书与已知的SSL颁发机构进行验证,因此您必须使用VERIFY_NONE来绕过此检查。它并不是在验证证书本身,而是在验证证书的颁发机构。由于自签名证书没有已知的颁发机构,因此无法通过此检查。 - Eugene
2
这真的非常危险。如果你不知道自己在做什么,就不要这样做。 - lzap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接