KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > xsocket > stream > io > impl > SSLProcessor


1 // $Id: IoSSLHandler.java 1268 2007-05-23 06:26:50Z grro $
2
/*
3  * Copyright (c) xsocket.org, 2006 - 2007. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
20  * The latest copy of this software may be found on http://www.xsocket.org/
21  */

22 package org.xsocket.stream.io.impl;
23
24 import java.io.IOException JavaDoc;
25 import java.nio.ByteBuffer JavaDoc;
26 import java.util.LinkedList JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.logging.Level JavaDoc;
29 import java.util.logging.Logger JavaDoc;
30
31 import javax.net.ssl.SSLContext;
32 import javax.net.ssl.SSLEngine;
33 import javax.net.ssl.SSLEngineResult;
34 import javax.net.ssl.SSLException;
35 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
36
37
38 import org.xsocket.ClosedConnectionException;
39 import org.xsocket.DataConverter;
40
41
42
43 /**
44  * ssl processor
45  *
46  * @author grro@xsocket.org
47  */

48 final class SSLProcessor {
49     
50     private static final Logger JavaDoc LOG = Logger.getLogger(SSLProcessor.class.getName());
51     
52
53     private SSLEngine sslEngine = null;
54     
55     private boolean isClientMode = false;
56     private IMemoryManager memoryManager = null;
57     private EventHandler eventHandler = null;
58
59     // buffer size
60
private int minNetBufferSize = 0;
61     private int minAppBufferSize = 0;
62
63     
64     /**
65      * constructor
66      *
67      * @param sslContext the ssl context
68      * @param isClientMode true, is ssl processor runs in client mode
69      */

70     SSLProcessor(SSLContext sslContext, boolean isClientMode, IMemoryManager memoryManager, EventHandler eventHandler) {
71         this.isClientMode = isClientMode;
72         this.memoryManager = memoryManager;
73         this.eventHandler = eventHandler;
74         
75         sslEngine = sslContext.createSSLEngine();
76         minAppBufferSize = sslEngine.getSession().getApplicationBufferSize();
77         minNetBufferSize = sslEngine.getSession().getPacketBufferSize();
78                 
79         sslEngine.setUseClientMode(isClientMode);
80     }
81     
82     
83     
84     /**
85      * start ssl processing
86      *
87      * @param inNetData already received net data
88      * @throws IOException If some other I/O error occurs
89      */

90     synchronized void start() throws IOException JavaDoc {
91         try {
92             sslEngine.beginHandshake();
93         } catch (SSLException sslEx) {
94             throw new RuntimeException JavaDoc(sslEx);
95         }
96
97         
98         if (isClientMode) {
99             if (LOG.isLoggable(Level.FINE)) {
100                 LOG.fine("initate ssl handshake");
101             }
102             processOutAppData();
103         }
104     }
105
106
107     /**
108      * destroy this instance
109      *
110      */

111     void destroy() {
112         sslEngine.closeOutbound();
113     }
114     
115
116     /**
117      * process incoming encrypted net data
118      *
119      * @param inNetBufferList the data
120      * @throws IOException if an io exction occurs
121      * @throws ClosedConnectionException if the connection is already closed
122      */

123     synchronized void processInNetData(LinkedList JavaDoc<ByteBuffer JavaDoc> inNetBufferList) throws IOException JavaDoc ,ClosedConnectionException {
124
125         LinkedList JavaDoc<ByteBuffer JavaDoc> inAppDataList = new LinkedList JavaDoc<ByteBuffer JavaDoc>();
126         
127         try {
128             int size = 0;
129             for (ByteBuffer JavaDoc buffer : inNetBufferList) {
130                 size += buffer.remaining();
131             }
132
133             for (ByteBuffer JavaDoc inNetBuffer : inNetBufferList) {
134                 List JavaDoc<ByteBuffer JavaDoc> inAppData = unwrap(inNetBuffer);
135                 inAppDataList.addAll(inAppData);
136                 
137                 
138                 if (LOG.isLoggable(Level.FINE)) {
139                     int appDataSize = 0;
140                     ByteBuffer JavaDoc[] inAppDataListCopy = new ByteBuffer JavaDoc[inAppDataList.size()];
141                     for (int i = 0; i < inAppDataList.size(); i++) {
142                         appDataSize += inAppDataList.get(i).remaining();
143                         inAppDataListCopy[i] = inAppDataList.get(i).duplicate();
144                     }
145                     
146                     if ((size - appDataSize) > 0) {
147                         LOG.fine((size - appDataSize) + " ssl system packet data ");
148                     }
149                     
150                     if (appDataSize > 0) {
151                         LOG.fine(appDataSize + " app data extracted: " + DataConverter.toTextOrHexString(inAppDataListCopy, "UTF-8", 500));
152                     }
153                 }
154             }
155             
156             if (!inAppDataList.isEmpty()) {
157                 eventHandler.onInAppDataReceived(inAppDataList);
158             }
159         } catch (SSLException sslEx) {
160             eventHandler.onSSLProcessorClosed();
161         }
162         
163                     
164         if (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
165             processOutAppData();
166         }
167     }
168
169     
170     /**
171      * return true, if the connection is handshaking
172      *
173      * @return true, if the connection is handshaking
174      */

175     synchronized boolean isHandshaking() {
176         HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
177         return !(handshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
178                 || (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED);
179     }
180     
181     
182     /**
183      * process outgoing plain app data
184      *
185      * @param outAppDataList the app data
186      * @throws IOException if an io exction occurs
187      * @throws ClosedConnectionException if the connection is already closed
188      */

189     void processOutAppData(LinkedList JavaDoc<ByteBuffer JavaDoc> outAppDataList) throws ClosedConnectionException, IOException JavaDoc{
190         for (ByteBuffer JavaDoc outAppData : outAppDataList) {
191             wrap(outAppData);
192         }
193     }
194
195     /**
196      * process outgoing plain app data
197      *
198      * @throws IOException if an io exction occurs
199      * @throws ClosedConnectionException if the connection is already closed
200      */

201     void processOutAppData() throws ClosedConnectionException, IOException JavaDoc{
202         ByteBuffer JavaDoc outAppData = ByteBuffer.allocate(0);
203         wrap(outAppData);
204     }
205
206     
207     private synchronized LinkedList JavaDoc<ByteBuffer JavaDoc> unwrap(ByteBuffer JavaDoc inNetData) throws SSLException, ClosedConnectionException, IOException JavaDoc {
208         LinkedList JavaDoc<ByteBuffer JavaDoc> inAppDataList = new LinkedList JavaDoc<ByteBuffer JavaDoc>();
209         int minAppSize = minAppBufferSize;
210             
211         try {
212             do {
213
214                 // perform unwrap
215
ByteBuffer JavaDoc inAppData = memoryManager.acquireMemory(minAppSize);
216                 SSLEngineResult engineResult = sslEngine.unwrap(inNetData, inAppData);
217
218
219                 // handle result
220
if ((engineResult.getStatus() == SSLEngineResult.Status.OK)
221                      || (engineResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW)) {
222                     
223                     // exctract remaining encrypted inNetData
224
if (inNetData.position() < inNetData.limit()) {
225                         inNetData = inNetData.slice();
226                     }
227                     
228                     // extract unwrapped app data
229
inAppData.flip();
230                     inAppData = extractAndRecycleMemory(inAppData, minAppSize);
231                     if (inAppData.remaining() > 0) {
232                         inAppDataList.add(inAppData);
233                     }
234                     
235                     
236                 } else if (engineResult.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
237                     // hmmm? shouldn't occure, but handle it by expanding the min buffer size
238
memoryManager.recycleMemory(inAppData, minAppSize);
239                     minAppSize += minAppSize;
240                     continue;
241                 
242                     
243                 } else if (engineResult.getStatus() == SSLEngineResult.Status.CLOSED) {
244                     memoryManager.recycleMemory(inAppData, minAppSize);
245                     throw new ClosedConnectionException("Couldn't unwrap, because connection is closed");
246                 }
247                 
248
249                                 
250                 // need task?
251
if (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
252                     Runnable JavaDoc task = null;
253                     while ((task = sslEngine.getDelegatedTask()) != null) {
254                         task.run();
255                     }
256                 }
257                 
258
259                 // finished ?
260
if (engineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
261                     notifyHandshakeFinished();
262                 }
263                 
264                 
265             } while (inNetData.hasRemaining());
266             
267         } catch (SSLException ssles) {
268             throw ssles;
269         }
270
271         return inAppDataList;
272     }
273     
274     
275     
276     private synchronized void wrap(ByteBuffer JavaDoc outAppData) throws SSLException, ClosedConnectionException, IOException JavaDoc {
277         int minNetSize = minNetBufferSize;
278
279         do {
280
281             // perform wrap
282
ByteBuffer JavaDoc outNetData = memoryManager.acquireMemory(minNetSize);
283             SSLEngineResult engineResult = sslEngine.wrap(outAppData, outNetData);
284             
285             
286             // handle the engine result
287
if ((engineResult.getStatus() == SSLEngineResult.Status.OK)
288                 || (engineResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW)) {
289
290                 // extract remaining outAppData
291
if (outAppData.position() < outAppData.limit()) {
292                     outAppData = outAppData.slice();
293                 }
294
295                 // extract encrypted net data
296
outNetData.flip();
297                 outNetData = extractAndRecycleMemory(outNetData, minNetSize);
298                 if (outNetData.hasRemaining()) {
299                     eventHandler.onOutNetDataToWrite(outNetData);
300                 }
301
302                 
303             } else if (engineResult.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
304                 // hmmm? shouldn't occure, but handle it by expanding the min buffer size
305
memoryManager.recycleMemory(outNetData, minNetSize);
306                 minNetSize += minNetSize;
307                 continue;
308
309                 
310             } else if (engineResult.getStatus() == SSLEngineResult.Status.CLOSED) {
311                 memoryManager.recycleMemory(outNetData, minNetSize);
312                 throw new ClosedConnectionException("Couldn't wrap, because connection is closed");
313             }
314             
315             
316             // need task?
317
if (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
318                 Runnable JavaDoc task = null;
319                 while ((task = sslEngine.getDelegatedTask()) != null) {
320                     task.run();
321                 }
322             }
323
324             
325             // finished ?
326
if (engineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
327                 notifyHandshakeFinished();
328                 break;
329             }
330         } while (outAppData.hasRemaining() || (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP));
331         
332     }
333     
334
335     private void notifyHandshakeFinished() throws IOException JavaDoc {
336         if (LOG.isLoggable(Level.FINE)) {
337             if (isClientMode) {
338                 LOG.fine("handshake has been finished (clientMode)");
339             } else {
340                 LOG.fine("handshake has been finished (serverMode)");
341             }
342         }
343         
344         processOutAppData();
345         eventHandler.onHandshakeFinished();
346     }
347     
348     
349     private ByteBuffer JavaDoc extractAndRecycleMemory(ByteBuffer JavaDoc buffer, int minSize) {
350         // all bytes used?
351
if (buffer.limit() == buffer.capacity()) {
352             return buffer;
353
354         // not all bytes used -> slice used part
355
} else {
356             int savedLimit = buffer.limit();
357             ByteBuffer JavaDoc slicedPart = buffer.slice();
358
359             // .. and return the remaining buffer for reuse
360
buffer.position(savedLimit);
361             buffer.limit(buffer.capacity());
362             ByteBuffer JavaDoc unused = buffer.slice();
363             memoryManager.recycleMemory(unused, minSize);
364
365             return slicedPart;
366         }
367     }
368     
369     
370     /**
371      * SSLProcessor call back interface
372      *
373      * @author grro
374      */

375     static interface EventHandler {
376
377         /**
378          * signals that the handshake has been finished
379          *
380          * @throws IOException if an io exception occurs
381          */

382         public void onHandshakeFinished() throws IOException JavaDoc;
383         
384         
385         /**
386          * singals available app data
387          *
388          * @param appDataList the app data
389          */

390         public void onInAppDataReceived(LinkedList JavaDoc<ByteBuffer JavaDoc> appDataList);
391         
392         
393         /**
394          * signals that there is net data to write
395          *
396          * @param netData the data to write
397          * @throws IOException if an io exception occurs
398          */

399         public void onOutNetDataToWrite(ByteBuffer JavaDoc netData) throws IOException JavaDoc ;
400         
401         
402         /**
403          * signals, that the SSLProcessor has been closed
404          *
405          * @throws IOException if an io exception occurs
406          */

407         public void onSSLProcessorClosed() throws IOException JavaDoc;
408     }
409 }
410
Popular Tags