采用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:/server-keystore.jks
生成服务器端证书(alias与上面生成时指定的要相同)
keytool -export -alias serverks -keystore D:/server-keystore.jks -storepass serverpwd -file D:/server-cert.cer
生成客户端证书仓库
keytool -genkey -alias clientks -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass clientpwd -storepass clientpwd -keystore D:/client-keystore.jks
生成客户端证书(alias与上面生成时指定的要相同)
keytool -export -alias clientks -keystore D:/client-keystore.jks -storepass clientpwd -file D:/client-cert.cer
上面四个命令就可以生成客户端和服务器端的证书和仓库了。
对于单项认证,客户端验证服务器端,服务器端不做验证情况,只需要将服务器端证书导入到客户端证书仓库。
对于双向认证,除了上面的导入操作外,还需要将客户端证书导入到服务器端证书仓库。
命令如下:
服务器端证书导入客户端证书仓库
keytool -import -trustcacerts -alias servercert2clientks -file D:/server-cert.cer -storepass clientpwd -keystore D:/client-keystore.jks
客户端证书导入到服务器端证书仓库
keytool -import -trustcacerts -alias clientcert2serverks -file D:/client-cert.cer -storepass serverks -keystore D:/server-keystore.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("server-keystore.jks"); //加载到keystore,第二个参数密码 ks.load(in, "serverpwd".toCharArray()); in.close(); //初始化keymanager通过keystore和密码 kmf.init(ks, "serverpwd".toCharArray()); //获取sslcontext,SSL/TLSv1.x SSLContext 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("server-keystore.jks"); //加载到keystore,第二个参数密码 ks.load(in, "serverpwd".toCharArray()); in.close(); //初始化keymanager通过keystore和密码 kmf.init(ks, "serverpwd".toCharArray()); //获取sslcontext,SSL/TLSv1.x SSLContext 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("server-keystore.jks"); //加载到keystore,第二个参数密码 ks.load(in, "serverpwd".toCharArray()); in.close(); //初始化keymanager通过keystore和密码 kmf.init(ks, "serverpwd".toCharArray()); //获取sslcontext,SSL/TLSv1.x SSLContext 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);
SSL/TLS 单双向认证代码示例
最新推荐文章于 2024-08-02 14:16:37 发布