数字证书学习笔记

说得直白点,数字证书(Digital Certificate)即通过对通信双方(比如浏览器和服务器)之间传输的数据进行加密以达到安全通信的目的。除了加密功能外,数字证书还用于身份认证,即数字证书就像是居民身份证一样,能够向别人证明“你确是你”。

一点点理论基础

我们都知道,HTTP协议对数据采用明文传输,浏览器和服务器之间的所有数据传输都赤裸裸地暴露在互联网上,包括你在登陆某个网站时输入的密码。为了解决这个问题,前人发明了对数据进行加密传输的HTTPS协议,该协议又建立在SSL/TLS协议之上。SSL/TLS位于HTTPS与TCP协议之间,是实现数据加密的核心协议,其本质上讲是对TCP协议的一层封装。由此可见,SSL/TLS其实不止可以用于HTTP,任何采用TCP协议的数据通信都可以采用SSL/TLS进行传输,比如FTP。只是就当前而言,SSL/TLS最为广泛的应用在于HTTPS。

在计算机密码学里,存在着对称加密和非对称加密两种加密方式,对称加密即通信双方均使用相同的密钥,如果Alice要将密文发给Bob,那么她首先需要将密钥发给Bob,那么问题来了——密钥又通过什么方式能够安全地发给Bob?为了解决这个问题,出现了非对称加密算法,即加密采用一个密钥,解密采用另一个密钥,两个密钥组成一个密钥对,由一个密钥加密的数据只能由密钥对中另一个密钥才能解密,最典型的非对称加密算法非RSA莫属。RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥(Public Key),而两个大素数组合成私钥(Private Key)。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。SSL/TLS便使用了非对称加密。

对于网站服务器来说,我们可以为其生成一个密钥对,其中公钥可以被所有浏览器任意下载,但是私钥只能又对服务器可见。当浏览器通过HTTPS访问网站时,浏览器首先下载到服务器的公钥,然后再通过公钥对所发送的数据进行加密,再传给服务器,服务器收到加密数据后,通过对应的私钥进行解密。服务器在返回数据时,使用私钥进行加密,浏览器取到数据后再通过公钥进行解密。由此一来,即保证了通信双方数据的加密传输,又解决了密钥的分发问题。整个SSL/TLS的工作原理比上述要复杂很多,感兴趣的读者可以参考SSL/TLS

对称加密与非对称加密

但是,这样依然会存在问题——浏览器如何知道所下载的公钥的确是来自于其要访问的服务器的呢?恶意攻击者完全可以在他们之间冒充服务器向浏览器发送一个冒牌的公钥,你在完全没有感知的情况下相信了该公钥,这个冒牌货便肆无忌惮地盗取你的密码,偷走你银行卡里的存款(此所谓中间人攻击)。如果有一种方式能够让浏览器事先验证所下载公钥的确来自于其正在访问的网站就好了——即本文一开始提到的证明“你的确是你”。

答案是肯定的,就是我们今天要讲的主角——数字证书。

还是拿居民身份证为例,身份证之所以能够证明你的身份,是因为它是由公安局颁发的,任何人需要使用身份证来验证你的身份时,比如火车票自动售卖机或者银行柜台的业务办理员,他们都需要无条件的相信公安局的权威作用。也就是说,如果能够找到一个权威的第三方同时被当事双方所信任,那么问题就解决了。类比起来,数字证书便充当了居民身份证的作用,它由一个第三方的权威机构颁发——证书权威机构(Certificate Authority,CA)。数字证书里面包含有网站的身份信息以及用于网站的公钥,即既能对网站的真实性提供证明,又能提供对数据的加密传输。

在有数字证书的参与下,整个通信过程大致如下:网站首先向CA机构购买一个数字证书,该证书里有网站的公钥、网站的域名和CA机构的数字签名。浏览器不再直接下载网站的公钥,而是下载得到网站的数字证书。浏览器需要做的第一件事便是验证数字证书的真实性,操作系统或者浏览器在安装时便安装有常见CA机构自己的证书,比如Verisign、Symantic等,这些证书是浏览器无条件信任的,他们里面的公钥便用于对网站证书的验证——通过CA证书里面的公钥计算网站证书的数字签名,当计算出的数字签名与网站证书里提供的数字签名相同时,表示该网站证书的确为CA机构所颁发,也即此时所访问的网站的确不是冒牌的。

公钥基础设施(PKI)

当然,真实的数字证书的认证过程比上述要复杂得多,比如,除了验证证书的数字签名外,还需检查网站证书里的网站域名是否与当前正在访问的域名一致。另外,CA机构是分层级的,包括根证书(Root Certificate)、中间证书(Intermediate Certificate)以及最底层的网站证书。其中根证书签署中间证书,中间证书在签署最底层的网站证书,由此组成一个证书链(Certificate Chain)。整个对证书的颁发、管理、认证组成了一整套基础设施——PKI

获取证书

获取证书的常规方式是向CA权威机构(公司)购买,这些证书一般来讲价格都不便宜,当然,一些公司为了广告宣传会提供免费的证书,比如阿里云和腾讯云都有免费的证书。另外,有一个叫Let's Encrypt的组织完全免费地向网站提供数组证书。再者,作为开发者,你完全可以自己生成一个自签名(Self-Signed)的证书用于开发过程,当然,这种证书并不是由CA机构颁发的,因此不能用于生成环境。

使用Java Keytool生成自签名证书

Java提供一个命令行工具keytool用于创建和管理密钥和数字证书。通过keytool生成自签名证书如下:

keytool -genkey -keyalg RSA -alias self-signed -keystore keystore.jks -storepass password -validity 360 -keysize 2048

该命令表示采用RSA算法生成一个2048位的密钥对,然后由为密钥对中的公钥生成数字证书,证书的有效期为360天。最后将密钥对和证书作为一条记录(Entry)保存到keystore.jks文件中,文件访问密码设为password,为该条记录取个别名为self-signed以便后续定位用。

该命令会让我们回答一系列问题:

What is your first and last name?
  [Unknown]:  www.mydomain.com
What is the name of your organizational unit?
  [Unknown]:  it department
What is the name of your organization?
  [Unknown]:  my company
What is the name of your City or Locality?
  [Unknown]:  Chengdu
What is the name of your State or Province?
  [Unknown]:  Sichuan
What is the two-letter country code for this unit?
  [Unknown]:  CN
Is CN=www.mydomain.com, OU=it department, O=my company, L=Chengdu, ST=Sichuan, C=CN correct?
  [no]:  yes

Enter key password for <self-signed>
	(RETURN if same as keystore password):

这里第一个问题What is your first and last name?最为重要,如果你要将此时生成的密钥用于向CA机构购买证书(参考下文的CSR),那么该问题应该回答你自己的网站域名。

如何购买证书

由于证书包含了你自己的公钥,所以在购买之前你需要自己生成一个密钥对,然后将密钥对中的公钥提取出来,再和一些你网站的基本信息(比如域名等)一并组装成一个证书签署请求(Certificate Signing Request,CSR),将该请求(文件)发给CA机构,CA机构将根据CSR中的信息为你生成一个数字证书。

在java中,我们依然可以通过keytool命令为既有的密钥记录生成CSR,比如要为上文中别名为self-signed的记录生成CSR命令如下:

keytool -certreq -alias self-signed -keyalg RSA -file yourdomain.csr -keystore keystore.jks

当从CA机构购买得到证书之后,我们可以通过keytool命令将证书导入到原来的自签名记录中,原来的自签名证书将被覆盖掉。假设购买得到的证书文件为certificate.cer,导入命令如下:

keytool -import -alias self-signed -file certificate.cer -keystore keystore.jks

在导入了购买所得的证书之后,别名self-signed便不是那么合适了,此时我们可以将其改名:

keytool -changealias -keystore keystore.jks -alias self-signed -destalias new-alias

上述创建密钥对以及CSR的过程对于很多人来说可能并不是一件简单的事情,因此有些公司(比如阿里云)将整个过程简化了——由公司负责帮我们生成密钥对,然后依然由公司帮我们生成CSR,最后将所生成的数字证书、私钥一并返回,而我们只需要提供诸如网站域名这样的一些基本信息即可。

证书格式

在上文中,我们接触到了Java的jks文件,jks即Java KeyStore的缩写。在Java中证书通过KeyStore对象表示并存放于keystore文件中。keystore文件主要包含了两类记录,一种是我们上文中所创建的PrivateKeyEntry,一种是TrustedCertificateEntry。PrivateKeyEntry用于存放私钥与证书的组合,而TrustedCertificateEntry只用于存放数字证书(不包含私钥)。虽然两种记录都可以存放证书,但是他们的用途是有区别的,PrivateKeyEntry用于向别人证明你自己的身份,而TrustedCertificateEntry用于验证别人的身份,即如果你是一个网站需要向浏览器证明自己,此时你应该把证书放在PrivateKeyEntry中(就像上文一样);而如果你是一个访问网站的客户端,比如需要通过HTTPS访问网站提供的REST API接口,那么你需要将网站的证书放到TrustedCertificateEntry中。

在Java中,虽然PrivateKeyEntry和TrustedCertificateEntry可以同时放在一个keystore文件中,但是推荐的做法是将他们分开存储。比如JRE默认有一个$JAVA_HOME/jre/lib/security/cacerts的keystore文件,里面便只存放了TrustedCertificateEntry,这些记录为一些常见CA机构的证书。

keystore文件只是Java的东西,除此之外,还有其他文件格式可以表示数字证书,并且在有些情况下他们之间是可以相互转换的。

常见的证书格式文件有:

  • der, cer,二进制文件,只含有证书,不含私钥
  • pem,文本文件,可以同时包含证书和私钥,也可以存放公钥
  • key,文本文件,如果pem中只包含私钥,通常用key后缀替代
  • cert、crt,文本文件,如果pem中只包含了证书,通常用cert或者crt后缀表示
  • pfx、p12,二进制文件,同时包含证书和私钥
  • jks,二进制文件,只用于Java,同时包含证书和私钥

数字证书遵循X.509标准,该标准定义了证书的数据结构,该数据结构通过DER编码便得到了二进制的der文件,由于二进制文件在人机间传输不便,对其进行BASE64编码便得到了pem文本文件,当然pem文件不止用于存放证书,还可以用于存放公钥和私钥,比如由ssh-keygen命令生成的密钥对便采用了pem文件格式。pem文件通常具有以下格式:

—–BEGIN CERTIFICATE—–
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQ
AwRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0
MRwHQYDVQQDDBZBZmZpcm1UcnVz......
—–END CERTIFICATE—–

pfx和p12是RSA公司制定的证书标准。

在Tomcat中配置证书

不同Tomcat版本中配置证书的方式不同,本文采用的是Tomcat 8.5.15版本。
在Tomcat中,主要通过配置jks文件来达到配置HTTPS的目的,当然也可以直接配置pfx文件。

可以通过生成自签名证书的方式得到的jks文件,如果证书是购买的,通常你不会直接拿到jks文件,此时你很有可能得到pfx/p12文件或者是crt文件(通过你自己的CSR获得,密钥在你自己手中或者卖家会一并发给你)。

对于pfx文件,可以通过以下命令转成jks文件:

keytool -importkeystore -srckeystore your.pfx -destkeystore your-name.jks -srcstoretype PKCS12 -deststoretype JKS

对于crt文件,通常来说是你知己手头已经有私钥了,就像在上文中将证书导入到既有的keystore里面一样,此时你只需要将crt证书文件通过keytool命令导入到原来的PrivateKeyEntry即可。

在配置Tomcat时,首先在`$TOMCAT_HOME/conf/server.xml中找到:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            ......
        </SSLHostConfig>
    </Connector>

初始情况下,本段是被注释掉的,解除注释后,将其改为:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="path/to/your.jks"
                         certificateKeystorePassword="your-password"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

其中certificateKeystoreFile为jks文件,certificateKeystorePassword为该keystore文件的访问密码。

如果需要将HTTP自动跳转到HTTPS,需要在项目的web.xml文件中加入:

<security-constraint>
  <web-resource-collection>
    <web-resource-name>Restricted URLs</web-resource-name>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
  </user-data-constraint>
</security-constraint>
posted @ 2017-06-17 17:45  无知者云  阅读(2775)  评论(5编辑  收藏  举报