SSL/TLS 单双向认证代码示例

来源:互联网 发布:windows自带编程软件 编辑:程序博客网 时间:2024/06/13 03:44

采用SSL/TLS形式的安全链接,能防止数据传输过程中被截获修改等问题。

SSL算法简短说明:

    对称加密算法:DES 3DES AES 客户端和服务端使用同一个密钥对消息进行加密解密。
    非对称加密算法:RSA, 生成公钥和私钥,采用公钥加密,私钥解密。客户端和服务器端各自持有自己的私钥证书,相互信任对方的证书(证书都导入到了对方的私钥仓库)
    速度上,对称加密算法比非对称加密算法要快。


下面使用jdk工具keytool生成服务端和客户端的密钥和证书,采用的是RSA的非对称加密形式。

生成服务器端证书仓库

keytool -genkey -alias serverks -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass serverpwd -storepass serverpwd -keystore D:/serverks.jks

生成服务器端证书

keytool -export -alias servercert -keystore D:/serverks.jks -storepass serverpwd -file D:/servercert.cer

生成客户端证书仓库

keytool -genkey -alias clientks -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass clientpwd -storepass clientpwd -keystore D:/clientks.jks

生成客户端证书

keytool -export -alias clientcert -keystore D:/clientks.jks -storepass clientpwd -file D:/clientcert.cer

上面四个命令就可以生成客户端和服务器端的证书和仓库了。

对于单项认证,客户端验证服务器端,服务器端不做验证情况,只需要将服务器端证书导入到客户端证书仓库。

对于双向认证,除了上面的导入操作外,还需要将客户端证书导入到服务器端证书仓库。

命令如下:

服务器端证书导入客户端证书仓库

keytool -import -trustcacerts -alias servercert2clientks -file D:/servercert.cer -storepass clientpwd -keystore D:/clientks.jks

客户端证书导入到服务器端证书仓库

keytool -import -trustcacerts -alias clientcert2serverks -file D:/clientcert.cer -storepass serverks -keystore D:/serverks.jks

上述命令解释:

keytool    //java jdk自带工具

-genkey  //生成key的命令参数

-alias //生成key别名

-keysize // key大小,2048

-validity //有效期,单位天

-keyalg //key的算法,本文采用RSA

-dname //key的信息串,CN=这里是域名,此外还有OU / O / L / S / C=CH 代表拥有者/归属/地区/城市/国家

-keypass // 生成key的密码,

-storepass //存储的密码

-keystore //生成key输出的路径和文件名

-export //导出证书

-import //导入证书

-file //导出的文件


下面是Java中SSLContext的生成过程:

单向SSL认证服务端代码(客户端对服务端证书进行验证,服务器端接受所有证书)

//获取X509算法的key管理工厂类KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");//获取keystore类型实例,jks类型KeyStore ks = KeyStore.getInstance("JKS");//读取服务器端的证书库InputStream in = ClassLoader.getSystemResourceAsStream("serverks.jks");//加载到keystore,第二个参数密码ks.load(in, "serverpwd".toCharArray());in.close();//初始化keymanager通过keystore和密码kmf.init(ks, "serverpwd".toCharArray());//获取sslcontext,SSL/TLSv1.xSSLContext serverContext = SSLContext.getInstance("TLSv1.2");//单项认证,客户端认证服务器,trustmanager为空,就是服务器端信任所有客户端证书serverContext.init(kmf.getKeyManagers(), null, null);

双向认证相比上面,只是让sslcontext的第二个信任证书参数传递不为空即可,代表服务器端新人的证书对象。

//获取X509算法的key管理工厂类KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");//获取keystore类型实例,jks类型KeyStore ks = KeyStore.getInstance("JKS");//读取服务器端的证书库InputStream in = ClassLoader.getSystemResourceAsStream("serverks.jks");//加载到keystore,第二个参数密码ks.load(in, "serverpwd".toCharArray());in.close();//初始化keymanager通过keystore和密码kmf.init(ks, "serverpwd".toCharArray());//获取sslcontext,SSL/TLSv1.xSSLContext serverContext = SSLContext.getInstance("TLSv1.2");//获取x509信任证书工厂,并根据keystore初始化, 服务器端验证客户端证书是否有效TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");tmf.init(ks);//双向认证,此处多trustManagers参数serverContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

对于TrustManager可以自己实现,但是一般很少自己实现,实现方式如下:

//获取X509算法的key管理工厂类KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");//获取keystore类型实例,jks类型KeyStore ks = KeyStore.getInstance("JKS");//读取服务器端的证书库InputStream in = ClassLoader.getSystemResourceAsStream("serverks.jks");//加载到keystore,第二个参数密码ks.load(in, "serverpwd".toCharArray());in.close();//初始化keymanager通过keystore和密码kmf.init(ks, "serverpwd".toCharArray());//获取sslcontext,SSL/TLSv1.xSSLContext serverContext = SSLContext.getInstance("TLSv1.2");//自定义实现证书验证X509TrustManager x509 = new X509TrustManager() {public X509Certificate[] getAcceptedIssuers() {return null;}/** * 服务器端检查 */public void checkServerTrusted(X509Certificate[] chain, String authType)throws java.security.cert.CertificateException {if (chain == null || chain.length == 0) {throw new IllegalArgumentException("X509Certificate chain cannot be null");}if (authType == null || authType.equals("")) {throw new IllegalArgumentException("X509Certificate authType cannot be null");}boolean isSucc = false;Principal p = null;for (X509Certificate x : chain) {p = x.getSubjectDN();//Principal getName 为keytool 中的-dname参数对应的值if (p != null && p.getName().contains("localhost")) {//这里只验证了-dName那个参数是否能识别isSucc = true;return ;}}if (!isSucc) {throw new  java.security.cert.CertificateException("no authorizication");}}/** * 客户端检查 */public void checkClientTrusted(X509Certificate[] chain, String authType)throws java.security.cert.CertificateException {if (chain == null || chain.length == 0) {throw new IllegalArgumentException("X509Certificate chain cannot be null");}if (authType == null || authType.equals("")) {throw new IllegalArgumentException("X509Certificate authType cannot be null");}boolean isSucc = false;Principal p = null;for (X509Certificate x : chain) {p = x.getSubjectDN();if (p != null && p.getName().contains("localhost")) {isSucc = true;return ;}}if (!isSucc) {throw new  java.security.cert.CertificateException("no authorizication");}}};//双向认证,自定义x509验证实现serverContext.init(kmf.getKeyManagers(), new TrustManager[]{x509}, null);