最近在研究基于ssl的传输加密,涉及到了key和证书相关的话题,走了很多弯路,如今总结一下作个备忘html
很多人可能听过其中的超过3个名词,但它们究竟有什么关联呢?java
证书分为2类:自签名证书和CA证书。通常自签名证书不能用来进行身份认证,若是一个server端使用自签名证书,client端要么被设置为无条件信任任何证书,要么须要将自签名证书的公钥和私钥加入受信任列表。但这样一来就增长了server的私钥泄露风险。node
TLS基于CA的身份认证基本原理是:首先验证方须要信任CA提供方本身的证书(CAcert),好比证书在操做系统的受信任证书列表中,或者用户经过“安装根证书”等方式将 CA的公钥和私钥加入受信任列表;而后CA对被验证方的原始证书进行签名(私钥加密),生成最终的证书;验证方获得最终的证书后,利用CAcert中包含的公钥进行解密,获得被验证方的原始证书。linux
根据RSA的加密原理,若是用CA的公钥解密成功,说明该证书的确是用CA的私钥加密的,能够认为被验证方是可信的。android
下面咱们以客户端单方对服务器端进行身份认证场景,来说解如何作证书的生成。ios
证书的生成能够用linux的OpenSSL工具链。对于一个网站,首先必须有本身的私钥,私钥的生成方式为:nginx
openssl genrsa -out ssl.key 2048
私钥必须妥善保管,既不能丢失,也不能泄露。若是发生丢失和泄露,必须立刻从新生成,以使旧的证书失效。算法
若是要对私钥进行传输/备份,建议先对私钥进行密码加密:tomcat
openssl rsa -in ssl.key -des3 -out encrypted.key
利用私钥就能够生成证书了。OpenSSL使用x509命令生成证书。这里须要区分两个概念:证书(certificate)和证书请求(certificate sign request)安全
x509命令能够将证书和证书请求相互转换,不过咱们这里只用到从证书请求到证书的过程
从私钥或已加密的私钥都可以生成证书请求。生成证书请求的方法为:
openssl req -new -key ssl.key -out ssl.csr
若是私钥已加密,须要输入密码。req命令会经过命令行要求用户输入国家、地区、组织等信息,这些信息会附加在证书中展现给链接方。
接下来用私钥对证书请求进行签名。根据不一样的场景,签名的方式也略有不一样
自签名的原理是用私钥对该私钥生成的证书请求进行签名,生成证书文件。该证书的签发者就是本身,因此验证方必须有该证书的私钥才能对签发信息进行验证,因此要么验证方信任一切证书,面临冒名顶替的风险,要么被验证方的私钥(或加密过的私钥)须要发送到验证方手中,面临私钥泄露的风险。
固然自签名也不是一无用处,好比须要内部通信的两台电脑须要使用加密又不想用第三方证书,能够在两端使用相同的私钥和证书进行验证(固然这样就跟对称加密没有区别了)
自签名的方法为:
openssl x509 -req -in ssl.csr -signkey ssl.key -out ssl.crt
一样若是ssl.key已加密,须要输入密码。
CA证书是一种特殊的自签名证书,能够用来对其它证书进行签名。这样当验证方选择信任了CA证书,被签名的其它证书就被信任了。在验证方进行验证时,CA证书来自操做系统的信任证书库,或者指定的证书列表。
生成自签名证书的方法为:
openssl x509 -req -in sign.csr -extensions v3_ca -signkey sign.key -out sign.crt
使用CA证书对其它证书进行签名的方法为:
openssl x509 -req -in ssl.csr -extensions v3_usr -CA sign.crt -CAkey sign.key -CAcreateserial -out ssl.crt
利用上述方法,受信任的机构就能够用本身的私钥(sign.key)对其余人的证书进行签名。咱们看到,只须要把证书请求(ssl.csr)发给证书机构,证书机构就能够生成出签名过的证书(ssl.crt)。目前购买证书签名服务的价格大约为100-400元/年。
可选的证书机构有:godaddy startssl namecheap
openssl默认使用PEM格式(形如-----BEGIN CERTIFICATE----- ... ... -----END CERTIFICATE---)存放证书和私钥,nginx/node.js可使用该格式启动服务。
但使用tomcat或java客户端/android时,不能使用该格式的证书。jdk中包含了一个keytool命令,能够完成整个的证书生成过程,不过这里咱们只用openssl工具也能够完成java的支持,即便用PKCS12格式对证书进行打包。
PKCS12格式文件,能够包含多个证书/私钥对,指定多个受信任的server(也能够不包含证书),每一个server有一个alias name。咱们来看最简单的只包含一个alias的文件生成:
openssl pkcs12 -export -in sign.crt -inkey sign.key -out sign.p12
上面的命令将CA证书及其私钥导出为sign.p12文件,导出时须要指定一个密码。
java客户端程序中对p12文件进行信任
若是咱们的服务器使用了本身建立的CA证书签名的证书,或者使用了自签名证书,客户端在使用ssl时,须要将导出的证书文件加入到信任列表中。下面是一段导入证书的示例代码:
1 javax.net.SocketFactory initSSLSocketFactory(InputStream keyin, char[] password) throws Exception { 2 java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS12"); 3 keyStore.load(keyin, password); 4 keyin.close(); 5 6 javax.net.ssl.TrustManagerFactory trustManagerFactory = javax.net.ssl.TrustManagerFactory.getInstance(javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm()); 7 trustManagerFactory.init(keyStore); 8 9 javax.net.ssl.SSLContext sslContext = javax.net.ssl.SSLContext.getInstance("SSL"); 10 11 sslContext.init(null, trustManagerFactory.getTrustManagers(), null); 12 13 return sslContext.getSocketFactory(); 14 }
咱们获得了一个SocketFactory,能够用它来初始化ssl/https链接