博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java SSL Server
阅读量:2244 次
发布时间:2019-05-09

本文共 13446 字,大约阅读时间需要 44 分钟。

NIO中有socketChannel但是没有sslSocketChannel,据文档中说,如果要实现ssl的socketChannel会牵涉很多代码的实现,增加api的复杂程度;ssl的实现也不应该依赖于是NIO还是传统的基于stream的IO,应该给程序员自己进行组合的自由,因此就没有在标准中提供相关的api,而留给程序员实现。为了支持nio实现ssl通讯,从java1.5开始增加了javax.ssl.SSLEngine用于支持nio的ssl,这个SSLEngine的作用就像一个状态机,维护着ssl通讯中各个状态以及下一个状态。

 

为了了解SSL通讯可以参考文档中的SSL通讯过程,主要包括握手,对话,关闭对话,三个步骤。其中握手部分的主要内容有协商协议,相互验证,生成并交换对称密钥,其中相互验证和对称密钥的交换是由非对称加密来完成的。在对话过程中,实际的明文是由之前生成的对称密钥来加密的。当对话结束后,互相发送结束信号结束通讯。

 

在上述过程中不仅仅是通讯双方简单的交换数据,更重要的是要根据SSL协议的要求,在特定的状态下发送或接受特定的数据,并且这些数据是经过处理的数据,也就是在tcp头和通讯正文之间还要包括一些ssl的信息,并且正文是由特定形式加密的。SSLEngine正是完成了管理状态,封装应用程序数据发往网络,解析网络数据并传递给应用程序的角色。

 

Sequence of messages exchanged in SSL handshake.

先看看SSLEngine有哪些状态,有哪些工作要做,假设从一个ssl server看。首先在握手阶段,需要和client程序多次握手,进行身份验证,对称密钥生成等工作,在这段时间里,并没有实际的应用层数据交换,而只有SSL协议数据的交换。且不看实际传输的内容和意义,在握手过程中,也就是上图中step1 到step13。Server的SSLEngine初始化后总是等待client的请求(等待接收数据),此时它的状态是NEED_UNWRAP,unwrap是解包的意思,这意味着,SSLEngine等待解析一个SSL的数据包,当server收到数据包后,在nio中数据包总是放在一个buffer里而不再是读stream,我们把这个buffer交给SSLEngine,调用它的unwrap方法,SSLEngine会解析这个数据包,把其中关于SSL握手的信息提取出来,并改变自己的状态,此处它将变成NEED_WRAP状态,意味着打包,它需要把对应的SSL回复内容写到数据包中返回到客户端,也就是step2-6中所作的事情。以此类推,SSLEngine多数时间总是在解包和打包两个状态间切换,尤其是在实际通讯时,注意到在unwrap和wrap函数中都有一个源buffer和一个目的buffer,因为SSLEngine不仅提取SSL协议相关的内容还要解密网络数据并把明文传递给应用程序,这其实才是这两个函数名字的来源,只不过在握手过程中,并没有实际的数据,而只有SSL协议信息,所以那个目的buffer总是没有东西。可以把SSL通讯看做交换礼物,SSLEngine把包裹拆了把礼物给你,或者他把礼物包起来送走,只是在SSL握手时,那个包裹里没有礼物,SSLEngine只是拆了个空包裹或是寄了个空包裹。那么还有没有其它状态,有一个FINISHED 状态那是在server端处于step13时所处的状态,表示这次handshake完成了;而当进入实际交换数据的时候,也就是step14的状态,这个状态是NOT_HANDSHAKE,表示当前不在握手,一般这个时候只需要在socket可读时,调用unwrap函数解密来自网络的SSL数据包,在socket可写的时候调用wrap函数把明文数据加密发送出去。还有一个状态NEED_TASK,首先要知道一点SSLEngine是异步的,wrap和unwrap函数调用都会立刻返回,比如在server收到client第一次请求后,会调用unwrap,但实际上SSLEngine还会做很多工作,比如访问Keystore文件,这些操作是费时的,但是实际上函数却立刻返回了,这时候SSLEngine会进入NEED_TASK状态,而不是立刻进入NEED_WRAP状态,所以必须让SSLEngine完成手头的工作,才能进入下一步工作,这时可以调用SSLEngine的getDelegatedTask()方法获得那个尚未完成的工作,它是一个Runnable的对象,可以调用它的run方法等待他完成,如果你是个高并发的server,也可以在这个时候做其他事情,等待这个工作完成,再接下去做wrap工作。另外还有一个非常容易出错的地方,一个NEED_UNWRAP状态的下一个状态然有可能是NEED_UNWRAP,并且一次调用unwrap方法并不一定把buffer中的所有内容都解包出来,可能还有内容需要在一次unwrap才能把所有东西都解析完,我遇到的这种情况发生在用nio的server和老的SSLSocket通讯时,在step7-11的过程中,client只向server一次性发送了这些数据,而server端需要连续两次unwrap才能把client的数据完整处理掉。

 

除了上述4个状态描述了SSLEngine的状态,还有4个状态用于描述每次调用wrap和unwrap后的结果状态。它们分别是BUFFER_OVERFLOW表示目标buffer没有足够的空间来存放解包的内容,这往往是因为你的目的buffer太小,或者在buffer在写入前没有clear;BUFFER_UNDERFLOW表示源buffer没足够内容让SSLEngine来解包,这往往是因为,可能还有数据尚未到达,或者在buffer读取前没有flip;CLOSED表示通讯的某一段正试图结束这个SSL通讯;OK,你懂的。

 

了解了SSLEngine的状态以及wrap和unwrap的原理,完成一个基于NIO的SSLsocket也就不会没想法了。

首先NIO的socket基本都通过Selector来实现,把socket 的accept,read,write事件都注册到selector上,不断的循环select()就可以,只是对于一个SSL Server Socket而言,它只是个普通的ServerSocket,首先只关心accept事件,所以首先这在selector上注册一个事件。

当serversocket接收到一个SSL client的请求后,就要开始进行握手,这个过程是同步的,所以先不要吧read和write事件也注册到selector上,当完成握手后,才注册这两个事件,并把socket设置成非阻塞。当select到socket可读时先调用unwrap方法,可写时先调用wrap方法。

每个socket都有两组buffer,分别是appIn,netIn和appOut,netOut,其中netXX都代表从socket中读取或写入的东西,他们都是加了密的,而appXX代表应用程序可理解的数据内容,它们都通过SSLEngine的wrap和unwrap方法才能与netXX相互转换。

 

粘代码

[java]   
 
  1. package com.red.nio.ssl;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.IOException;  
  6. import java.net.InetSocketAddress;  
  7. import java.nio.ByteBuffer;  
  8. import java.nio.channels.ClosedChannelException;  
  9. import java.nio.channels.SelectableChannel;  
  10. import java.nio.channels.SelectionKey;  
  11. import java.nio.channels.Selector;  
  12. import java.nio.channels.ServerSocketChannel;  
  13. import java.nio.channels.SocketChannel;  
  14. import java.nio.charset.Charset;  
  15. import java.nio.charset.CharsetDecoder;  
  16. import java.nio.charset.CharsetEncoder;  
  17. import java.security.GeneralSecurityException;  
  18. import java.security.KeyStore;  
  19. import java.security.KeyStoreException;  
  20. import java.security.NoSuchAlgorithmException;  
  21. import java.security.UnrecoverableKeyException;  
  22. import java.security.cert.CertificateException;  
  23. import java.util.Iterator;  
  24.   
  25. import javax.net.ssl.KeyManagerFactory;  
  26. import javax.net.ssl.SSLContext;  
  27. import javax.net.ssl.SSLEngine;  
  28. import javax.net.ssl.SSLEngineResult;  
  29. import javax.net.ssl.SSLException;  
  30. import javax.net.ssl.SSLSession;  
  31. import javax.net.ssl.TrustManagerFactory;  
  32. import javax.net.ssl.SSLEngineResult.HandshakeStatus;  
  33.   
  34. public class SSLNewServer {  
  35.   
  36.     private static boolean logging = true;  
  37.       
  38.     private boolean handshakeDone = false;  
  39.       
  40.     private Selector selector;  
  41.     private SSLEngine sslEngine;  
  42.     private SSLContext sslContext;  
  43.       
  44.     private ByteBuffer appOut; // clear text buffer for out  
  45.     private ByteBuffer appIn; // clear text buffer for in  
  46.     private ByteBuffer netOut; // encrypted buffer for out  
  47.     private ByteBuffer netIn; // encrypted buffer for in  
  48.   
  49.     private CharsetEncoder encoder = Charset.forName("UTF8").newEncoder();  
  50.     private CharsetDecoder decoder = Charset.forName("UTF8").newDecoder();  
  51.       
  52.     public SSLNewServer() {  
  53.         try  
  54.         {  
  55.             createServerSocket();  
  56.         } catch (IOException e)  
  57.         {  
  58.             System.out.println("initializing server failed");  
  59.             e.printStackTrace();  
  60.         }  
  61.           
  62.         try  
  63.         {  
  64.             createSSLContext();  
  65.         } catch (GeneralSecurityException e)  
  66.         {  
  67.             System.out.println("initializing SSL context failed");  
  68.             e.printStackTrace();  
  69.         } catch (IOException e)  
  70.         {  
  71.             System.out.println("reading keystore or truststore file failed");  
  72.             e.printStackTrace();  
  73.         }  
  74.           
  75.         createSSLEngines();  
  76.         createBuffers();  
  77.     }  
  78.       
  79.     private void createBuffers()  
  80.     {  
  81.         SSLSession session = sslEngine.getSession();  
  82.         int appBufferMax = session.getApplicationBufferSize();  
  83.         int netBufferMax = session.getPacketBufferSize();  
  84.           
  85.         appOut = ByteBuffer.wrap("This is an SSL Server".getBytes());//server only reply this sentence   
  86.         appIn = ByteBuffer.allocate(appBufferMax + 10);//appIn is bigger than the allowed max application buffer siz  
  87.         netOut = ByteBuffer.allocateDirect(netBufferMax);//direct allocate for better performance  
  88.         netIn = ByteBuffer.allocateDirect(netBufferMax);  
  89.     }  
  90.   
  91.     //the ssl context initialization  
  92.     private void createSSLContext() throws GeneralSecurityException, FileNotFoundException, IOException  
  93.     {  
  94.         KeyStore ks = KeyStore.getInstance("JKS");  
  95.         KeyStore ts = KeyStore.getInstance("JKS");  
  96.   
  97.         char[] passphrase = "123456".toCharArray();  
  98.   
  99.         ks.load(new FileInputStream("ssl/kserver.keystore"), passphrase);  
  100.         ts.load(new FileInputStream("ssl/tserver.keystore"), passphrase);  
  101.   
  102.         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");  
  103.         kmf.init(ks, passphrase);  
  104.   
  105.         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");  
  106.         tmf.init(ts);  
  107.   
  108.         SSLContext sslCtx = SSLContext.getInstance("SSL");  
  109.   
  110.         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);  
  111.   
  112.         sslContext = sslCtx;  
  113.           
  114.     }  
  115.   
  116.     //create the server socket, bind it to port 1234, set unblock and register the "accept" only  
  117.     private void createServerSocket() throws IOException  
  118.     {  
  119.         selector = Selector.open();  
  120.         ServerSocketChannel ssc = ServerSocketChannel.open();  
  121.         ssc.socket().bind(new InetSocketAddress(1234));  
  122.         ssc.configureBlocking(false);  
  123.         ssc.register(selector, SelectionKey.OP_ACCEPT);  
  124.     }  
  125.   
  126.     private void createSSLEngines()   
  127.     {  
  128.         sslEngine = sslContext.createSSLEngine();  
  129.         sslEngine.setUseClientMode(false);//work in a server mode  
  130.         sslEngine.setNeedClientAuth(true);//need client authentication  
  131.     }  
  132.       
  133.     public void selecting() {  
  134.         while (true)  
  135.         {  
  136.             try  
  137.             {  
  138.                 selector.select();  
  139.             } catch (IOException e)  
  140.             {  
  141.                 e.printStackTrace();  
  142.             }  
  143.             Iterator<SelectionKey> iter = selector.selectedKeys().iterator();  
  144.             while (iter.hasNext())  
  145.             {  
  146.                 SelectionKey key = (SelectionKey) iter.next();  
  147.                 iter.remove();  
  148.                 try  
  149.                 {  
  150.                     handle(key);  
  151.                 } catch (SSLException e)  
  152.                 {  
  153.                     // TODO Auto-generated catch block  
  154.                     e.printStackTrace();  
  155.                 } catch (IOException e)  
  156.                 {  
  157.                     // TODO Auto-generated catch block  
  158.                     e.printStackTrace();  
  159.                 }  
  160.             }  
  161.         }  
  162.     }  
  163.   
  164.     private void handle(SelectionKey key) throws IOException  
  165.     {  
  166.         if(key.isAcceptable()) {  
  167.               
  168.             try  
  169.             {  
  170.                 SocketChannel sc = ((ServerSocketChannel)key.channel()).accept();  
  171.                 doHandShake(sc);//if it is an accept event, do the handshake in a blocking mode  
  172.             } catch (ClosedChannelException e)  
  173.             {  
  174.                 // TODO Auto-generated catch block  
  175.                 e.printStackTrace();  
  176.             } catch (IOException e)  
  177.             {  
  178.                 // TODO Auto-generated catch block  
  179.                 e.printStackTrace();  
  180.             }  
  181.         }  
  182.         else if(key.isReadable()) {  
  183.             if (sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING)  
  184.             {  
  185.                 SocketChannel sc = (SocketChannel) key.channel();  
  186.                 sc.read(netIn);  
  187.                 netIn.flip();  
  188.                   
  189.                 SSLEngineResult engineResult = sslEngine.unwrap(netIn, appIn);  
  190.                 log("server unwrap: ", engineResult);  
  191.                 doTask();  
  192.                 //runDelegatedTasks(engineResult, sslEngine);  
  193.                 netIn.compact();  
  194.                 if (engineResult.getStatus() == SSLEngineResult.Status.OK)  
  195.                 {  
  196.                     System.out.println("text recieved");  
  197.                     appIn.flip();// ready for reading  
  198.                     System.out.println(decoder.decode(appIn));  
  199.                     appIn.compact();  
  200.                 }  
  201.                 else if(engineResult.getStatus() == SSLEngineResult.Status.CLOSED) {  
  202.                     doSSLClose(key);  
  203.                 }  
  204.   
  205.             }  
  206.   
  207.         }  
  208.         else if(key.isWritable()) {  
  209.             SocketChannel sc = (SocketChannel) key.channel();  
  210.             //if(!sslEngine.isOutboundDone()) {  
  211.                 //netOut.clear();  
  212.             SSLEngineResult engineResult = sslEngine.wrap(appOut, netOut);  
  213.             log("server wrap: ", engineResult);  
  214.             doTask();  
  215.             //runDelegatedTasks(engineResult, sslEngine);  
  216.             if (engineResult.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING)  
  217.             {  
  218.                 System.out.println("text sent");  
  219.             }  
  220.             netOut.flip();  
  221.             sc.write(netOut);  
  222.             netOut.compact();  
  223.             //}  
  224.         }  
  225.           
  226.     }  
  227.   
  228.     /*public static HandshakeStatus runDelegatedTasks(SSLEngineResult engineResult, SSLEngine sslEngine) 
  229.     { 
  230.         if (engineResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) 
  231.         { 
  232.             Runnable runnable; 
  233.             while ((runnable = sslEngine.getDelegatedTask()) != null) 
  234.             { 
  235.                 System.out.println("\trunning delegated task..."); 
  236.                 runnable.run(); 
  237.             } 
  238.             HandshakeStatus hsStatus = sslEngine.getHandshakeStatus(); 
  239.             if (hsStatus == HandshakeStatus.NEED_TASK) 
  240.             { 
  241.                 //throw new Exception("handshake shouldn't need additional tasks"); 
  242.                 System.out.println("handshake shouldn't need additional tasks"); 
  243.             } 
  244.             System.out.println("\tnew HandshakeStatus: " + hsStatus); 
  245.         } 
  246.         return sslEngine.getHandshakeStatus(); 
  247.          
  248.     }*/  
  249.   
  250.     /* 
  251.      * Logging code 
  252.      */  
  253.     private static boolean resultOnce = true;  
  254.   
  255.     public static void log(String str, SSLEngineResult result)  
  256.     {  
  257.         if (!logging)  
  258.         {  
  259.             return;  
  260.         }  
  261.         if (resultOnce)  
  262.         {  
  263.             resultOnce = false;  
  264.             System.out.println("The format of the SSLEngineResult is: \n"  
  265.                     + "\t\"getStatus() / getHandshakeStatus()\" +\n"  
  266.                     + "\t\"bytesConsumed() / bytesProduced()\"\n");  
  267.         }  
  268.         HandshakeStatus hsStatus = result.getHandshakeStatus();  
  269.         log(str + result.getStatus() + "/" + hsStatus + ", " + result.bytesConsumed() + "/"  
  270.                 + result.bytesProduced() + " bytes");  
  271.         if (hsStatus == HandshakeStatus.FINISHED)  
  272.         {  
  273.             log("\t...ready for application data");  
  274.         }  
  275.     }  
  276.   
  277.     public static void log(String str)  
  278.     {  
  279.         if (logging)  
  280.         {  
  281.             System.out.println(str);  
  282.         }  
  283.     }  
  284.       
  285.       
  286.     private void doHandShake(SocketChannel sc) throws IOException  
  287.     {  
  288.           
  289.         sslEngine.beginHandshake();//explicitly begin the handshake  
  290.         HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();  
  291.         while (!handshakeDone)  
  292.         {  
  293.             switch(hsStatus){  
  294.                 case FINISHED:  
  295.                     //the status become FINISHED only when the ssl handshake is finished  
  296.                     //but we still need to send data, so do nothing here  
  297.                     break;  
  298.                 case NEED_TASK:  
  299.                     //do the delegate task if there is some extra work such as checking the keystore during the handshake  
  300.                     hsStatus = doTask();  
  301.                     break;  
  302.                 case NEED_UNWRAP:  
  303.                     //unwrap means unwrap the ssl packet to get ssl handshake information  
  304.                     sc.read(netIn);  
  305.                     netIn.flip();  
  306.                     hsStatus = doUnwrap();  
  307.                     break;  
  308.                 case NEED_WRAP:  
  309.                     //wrap means wrap the app packet into an ssl packet to add ssl handshake information  
  310.                     hsStatus = doWrap();  
  311.                     sc.write(netOut);  
  312.                     netOut.clear();  
  313.                     break;  
  314.                 case NOT_HANDSHAKING:  
  315.                     //now it is not in a handshake or say byebye status. here it means handshake is over and ready for ssl talk  
  316.                     sc.configureBlocking(false);//set the socket to unblocking mode  
  317.                     sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);//register the read and write event  
  318.                     handshakeDone = true;  
  319.                     break;  
  320.             }  
  321.         }  
  322.           
  323.     }  
  324.       
  325.     private HandshakeStatus doTask() {  
  326.         Runnable runnable;  
  327.         while ((runnable = sslEngine.getDelegatedTask()) != null)  
  328.         {  
  329.             System.out.println("\trunning delegated task...");  
  330.             runnable.run();  
  331.         }  
  332.         HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();  
  333.         if (hsStatus == HandshakeStatus.NEED_TASK)  
  334.         {  
  335.             //throw new Exception("handshake shouldn't need additional tasks");  
  336.             System.out.println("handshake shouldn't need additional tasks");  
  337.         }  
  338.         System.out.println("\tnew HandshakeStatus: " + hsStatus);  
  339.           
  340.         return hsStatus;  
  341.     }  
  342.       
  343.     private HandshakeStatus doUnwrap() throws SSLException{  
  344.         HandshakeStatus hsStatus;  
  345.         do{
    //do unwrap until the state is change to "NEED_WRAP"  
  346.             SSLEngineResult engineResult = sslEngine.unwrap(netIn, appIn);  
  347.             log("server unwrap: ", engineResult);  
  348.             hsStatus = doTask();  
  349.         }while(hsStatus ==  SSLEngineResult.HandshakeStatus.NEED_UNWRAP && netIn.remaining()>0);  
  350.         System.out.println("\tnew HandshakeStatus: " + hsStatus);  
  351.         netIn.clear();  
  352.         return hsStatus;  
  353.     }  
  354.       
  355.     private HandshakeStatus doWrap() throws SSLException{  
  356.         HandshakeStatus hsStatus;  
  357.         SSLEngineResult engineResult = sslEngine.wrap(appOut, netOut);  
  358.         log("server wrap: ", engineResult);  
  359.         hsStatus = doTask();  
  360.         System.out.println("\tnew HandshakeStatus: " + hsStatus);  
  361.         netOut.flip();  
  362.         return hsStatus;  
  363.     }  
  364.       
  365.     //close an ssl talk, similar to the handshake steps  
  366.     private void doSSLClose(SelectionKey key) throws IOException {  
  367.         SocketChannel sc = (SocketChannel) key.channel();  
  368.         key.cancel();  
  369.           
  370.         try  
  371.         {  
  372.             sc.configureBlocking(true);  
  373.         } catch (IOException e)  
  374.         {  
  375.             // TODO Auto-generated catch block  
  376.             e.printStackTrace();  
  377.         }  
  378.         HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();  
  379.         while(handshakeDone) {  
  380.             switch(hsStatus) {  
  381.             case FINISHED:  
  382.                   
  383.                 break;  
  384.             case NEED_TASK:  
  385.                 hsStatus = doTask();  
  386.                 break;  
  387.             case NEED_UNWRAP:  
  388.                 sc.read(netIn);  
  389.                 netIn.flip();  
  390.                 hsStatus = doUnwrap();  
  391.                 break;  
  392.             case NEED_WRAP:  
  393.                 hsStatus = doWrap();  
  394.                 sc.write(netOut);  
  395.                 netOut.clear();  
  396.                 break;  
  397.             case NOT_HANDSHAKING:  
  398.                 handshakeDone = false;  
  399.                 sc.close();  
  400.                 break;  
  401.             }  
  402.         }  
  403.     }  
  404.       
  405.       
  406.       
  407.     public static void main(String[] args) {  
  408.         SSLNewServer sns = new SSLNewServer();  
  409.         sns.selecting();  
  410.     }  
  411. }  

转载于:https://www.cnblogs.com/Dennis-mi/articles/6422752.html

你可能感兴趣的文章
基于范围的for循环(STL)
查看>>
Docker(二十)-Docker容器CPU、memory资源限制
查看>>
win7 下 vmware 虚拟机开后 w字母键失效不能用 解决方案:
查看>>
1000. A+B Problem
查看>>
101.Symmetric Tree
查看>>
mybatits
查看>>
POJ 3104 Drying 二分
查看>>
CSS 简介/特点/优势/给特定浏览器提供不同样
查看>>
鸡蛋的硬度
查看>>
三范式
查看>>
http://www.cnblogs.com/lhb25 漂亮图片资源
查看>>
request请求转发和request域运用
查看>>
ios实用wifi分析仪——AirPort
查看>>
【软工项目组】第十三次会议(样式设计2.0)
查看>>
【django基础】
查看>>
SQL Server:关于Null的一些事
查看>>
StringBuilder与StringBuffer
查看>>
【征文】Hadoop十周年特别策划——我与Hadoop不得不说的故事
查看>>
CPU组成
查看>>
Android 自己定义RecyclerView 实现真正的Gallery效果
查看>>