zoukankan      html  css  js  c++  java
  • 安全SOCKET

    导语

    要使用安全Socket需要对密码学有一定的了解。在阅读本文之前最好能阅读一下下面几个网站的内容
    http://kb.cnblogs.com/page/194742/
    http://kb.cnblogs.com/page/162080/
    http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
    http://geek.csdn.net/news/detail/188003

    创建安全客户端Socket

    创建一个安全Socket很简单,首先从javax.net.ssl.SSLSocketFactory获取一个默认的工厂对象。然后通过该工厂的createSocket()方法得到一个具体的Socket对象。下面是一个简单的例子

    SocketFactory factory = SSLSocketFactory.getDefault();
    Socket socket = factory.createSocket("www.xdysite.cn", 7000);
    

    代码中返回的Socket实际上是javax.net.ssl.SSLSocket,这是java.net.Socket的一个子类。不过,我们不需要了解这些细节。我们就把他当成普通的socket来使用即可,即通过其getInputStream(),getOutputStream()和其它方法加以使用。

    一个简单的小案例

    假设有一个接受订单的服务器在www.xdysite.cn的7000端口监听连接。每个订单使用一个TCP连接以ASCII字符串的形式发送。服务器接受订单并关闭这个连接(这里我们省略大量细节)。客户端发送的订单形式如下:

    Name: liming
    Product: 67x-89
    Address: 1280 XiDian University
    Card number: 4000-1234-5678-901
    Expires: 2016/12/29

    这个消息中包含了大量的信息,足以让某个监听数据包的人恶意地使用liming的信用卡号码。因此,在发送这个订单之前,应当对它加密。下面的代码将通过安全Socket发送订单:

    SocketFactory factory = SSLSocketFactory.getDefault();
    		try {
    			Socket socket = factory.createSocket("www.xdysite.cn", 7000);
    			Writer out = new OutputStreamWriter(socket.getOutputStream(), "ASCII");
    			out.write("Name: liming
    ");
    			out.write("Product: 67x-89
    ");
    			out.write("Address: 1280 XiDian University
    ");
    			out.write("Card number: 4000-1234-5678-901
    ");
    			out.write("Expires: 2016/12/29
    ");
    			out.flush();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    

    下面是一个HTTPS客户端的示例代码

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    
    import javax.net.SocketFactory;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    
    public class HTTPSClient {
    	
    	public static void main(String[] args) {
    		if (args.length == 0) {
    			System.out.println("Usage: java HTTPSClient host");
    			return;
    		}
    		int port = 443;
    		String host = args[0];
    		SocketFactory factory =  SSLSocketFactory.getDefault();
    		SSLSocket socket = null;
    		try {
    			socket = (SSLSocket) factory.createSocket(host, port);
    			
    			//启用所有密码组
    			String[] supported = socket.getSupportedCipherSuites();
    			socket.setEnabledCipherSuites(supported);
    			
    			Writer out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
    			
    			//https在GET行中需要完全URL
    			out.write("GET / HTTP/1.1
    ");
    			out.write("Host: " + host + "
    
    ");
    			out.flush();
    			
    			//读取响应
    			BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    			
    			//读取首部
    			String s;
    			while (!(s = in.readLine()).equals("")) {
    				System.out.println(s);
    			}
    			
    			System.out.println();
    			
    			//读取长度
    			String contentLength = in.readLine();
    			int length = Integer.MAX_VALUE;
    			try {
    				length = Integer.parseInt(contentLength.trim(), 16);
    			} catch (NumberFormatException e) {
    				//这个服务器在响应体的第一行
    				//没有发送content-length
    			}
    			System.out.println(contentLength);
    					
    			int c;
    			int i = 0;
    			while ((c = in.read()) != -1 && i++ < length) {
    				System.out.write(c);;
    			}
    			System.out.println();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				socket.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    通过上面的程序,我们可以访问类似https://www.baidu.com网站的内容。

    HTTPS服务的实现

    HTTPS服务器实现起来比较复杂,要创建一个安全服务器Socket,必须要完成下面的步骤:

    • 使用keytool工具生成公钥和证书
    • 花钱请可信的第三方(权威CA)认证证书
    • 为使用的加密算法创建SSLContext
    • 为使用的证书源创建一个TrustManagerFactory
    • 为使用的密钥类型创建一个KeyManagerFactory
    • 为密钥和证书数据库创建一个KeyStor对象
    • 用密钥和证书填充KeyStore对象
    • 用KeysStore及其口令短语初始化KeyManagerFactory
    • 用KeyManagerFactory中的密钥管理器(必要),TrustManagerFactory中的信任管理器和一个随机源来初始化上下文

    前两步我们要么自己去做,要么找一个CA机构帮我们直接生成。为了完成测试,我们去https://www.pianyissl.com/网站获取一个免费的SSL证书。获取的是一个压缩包,解压后可以从Tomcat目录下找到keystore.jks文件通过JDK中的keytool工具可以查看证书的信息。

    keytool -list -v -keystore /root/javaTest/Tomcat/keystore.jks
    

    图片中显示的是证书的部分信息,那么接下来如何将该证书加载到服务器呢?

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.Socket;
    import java.security.KeyManagementException;
    import java.security.KeyStore;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;
    import java.security.UnrecoverableKeyException;
    import java.security.cert.CertificateException;
    import java.util.Arrays;
    
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLServerSocket;
    import javax.net.ssl.SSLServerSocketFactory;
    
    public class SSLServer {
    	public final static int PORT = 7000;
    	public final static String algorithm ="SSL";
    	
    	public static void main(String[] args) {
    		try {
    			SSLContext context = SSLContext.getInstance("SSL");
    			
    			//设置只支持X.509标准的密钥
    			KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    			
    			//Oracle的默认密钥库类型
    			KeyStore ks = KeyStore.getInstance("JKS");
    			
    			//加载证书,在读取jks类型的证书时需要输入密码(这里是123456)
    			char[] password = "123456".toCharArray();
    			ks.load(new FileInputStream("keystore.jks"), password);
    			kmf.init(ks, password);
    			context.init(kmf.getKeyManagers(), null, null);
    			
    			//删除密码
    			Arrays.fill(password, '0');
    			
    			SSLServerSocketFactory factory = context.getServerSocketFactory();
    			SSLServerSocket server = (SSLServerSocket) factory.createServerSocket(PORT);
    			while(true) {
    				try(
    					Socket client = server.accept();
    					Writer writer = new OutputStreamWriter(client.getOutputStream());
    				){
    					writer.write("HTTP/1.1 200 OK
    ");
    					writer.write("Server: SSlServer
    ");
    					writer.write("Content-lenght: 10
    
    ");
    					writer.write("hello ssl!");
    				}catch(IOException e){} 
    			}
    			
    		} catch (NoSuchAlgorithmException | IOException 
    				| UnrecoverableKeyException | KeyManagementException
    				| CertificateException| KeyStoreException e) {
    			
    		} 
    	}
    }
    

    访问http://www.xdysite.cn:7000端口就可以使用SSL安全链接的HTTP了

  • 相关阅读:
    C++字符串函数之append()、insert()
    492. Construct the Rectangle(LeetCode)
    桶排序
    104. Maximum Depth of Binary Tree (LeetCode)
    557. Reverse Words in a String III(LeetCode )
    基数排序(LSD)
    500. Keyboard Row
    输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
    myeclipse打断点进入后无法查看变量的值的解决方法
    可参考的js代码
  • 原文地址:https://www.cnblogs.com/xidongyu/p/6274199.html
Copyright © 2011-2022 走看看