KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > maverick > ssl > SSLTransportImpl


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.maverick.ssl;
21
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.DataInputStream JavaDoc;
24 import java.io.DataOutputStream JavaDoc;
25 import java.io.EOFException JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.InterruptedIOException JavaDoc;
29 import java.io.OutputStream JavaDoc;
30 import java.text.MessageFormat JavaDoc;
31
32 import com.maverick.crypto.asn1.x509.X509Certificate;
33
34 /**
35  *
36  * @author Lee David Painter <a HREF="mailto:lee@3sp.com">&lt;lee@3sp.com&gt;</a>
37  */

38 public class SSLTransportImpl implements SSLTransport {
39
40     public static final int VERSION_MAJOR = 3;
41     public static final int VERSION_MINOR = 0;
42
43     static final int WARNING_ALERT = 1;
44     static final int FATAL_ALERT = 2;
45
46     final static int CHANGE_CIPHER_SPEC_MSG = 20;
47     final static int ALERT_PROTOCOL = 21;
48     final static int APPLICATION_DATA = 23;
49
50     SSLInputStream sslIn = new SSLInputStream();
51     SSLOutputStream sslOut = new SSLOutputStream();
52
53     SSLHandshakeProtocol handshake = null;
54     SSLContext context;
55
56     public String JavaDoc debug = "Standard transport"; //$NON-NLS-1$
57

58     DataInputStream JavaDoc rawIn;
59     DataOutputStream JavaDoc rawOut;
60
61     long incomingSequence = 0;
62     long outgoingSequence = 0;
63
64     SSLCipherSuite writeCipherSuite = null;
65     SSLCipherSuite readCipherSuite = null;
66
67     // #ifdef DEBUG
68
org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(SSLSocket.class);
69
70     // #endif
71

72     public SSLTransportImpl() {
73     }
74
75     public SSLContext getContext() {
76         return context;
77     }
78
79     /* (non-Javadoc)
80      * @see com.maverick.ssl.SSLTransport#getCertificate()
81      */

82     public X509Certificate getCertificate(){
83         return handshake.getCertificate();
84     }
85     
86     /* (non-Javadoc)
87      * @see com.maverick.ssl.SSLTransport#initialize(java.io.InputStream, java.io.OutputStream)
88      */

89     public void initialize(InputStream JavaDoc in, OutputStream JavaDoc out) throws IOException JavaDoc, SSLException {
90         initialize(in, out, null);
91     }
92
93     public void initialize(InputStream JavaDoc in, OutputStream JavaDoc out, SSLContext context) throws IOException JavaDoc, SSLException {
94
95         this.rawIn = new DataInputStream JavaDoc(in);
96         this.rawOut = new DataOutputStream JavaDoc(out);
97         writeCipherSuite = readCipherSuite = new SSL_NULL_WITH_NULL_NULL();
98
99         // #ifdef DEBUG
100
log.debug(Messages.getString("SSLTransport.initialising")); //$NON-NLS-1$
101
// #endif
102

103         if (context == null)
104             context = new SSLContext();
105
106         handshake = new SSLHandshakeProtocol(this, context);
107
108         // Start the handshake
109
handshake.startHandshake();
110
111         // While the handshake is not complete process all the messages
112
while (!handshake.isComplete()) {
113             processMessages();
114
115             // #ifdef DEBUG
116
log.debug(Messages.getString("SSLTransport.initCompleteStartingAppProtocol")); //$NON-NLS-1$
117
// #endif
118
}
119     }
120
121     void processMessages() throws SSLException, EOFException JavaDoc {
122
123         int type = 0;
124         byte[] fragment = null;
125         try {
126             type = rawIn.read();
127             int major = rawIn.read();
128             int minor = rawIn.read();
129
130             int length = rawIn.readShort();
131
132             fragment = new byte[length];
133
134             rawIn.readFully(fragment);
135
136             readCipherSuite.decrypt(fragment, 0, fragment.length);
137
138             if (readCipherSuite.getMACLength() > 0) {
139                 if (!readCipherSuite.verifyMAC(fragment,
140                     0,
141                     fragment.length - readCipherSuite.getMACLength(),
142                     type,
143                     incomingSequence,
144                     fragment,
145                     fragment.length - readCipherSuite.getMACLength(),
146                     readCipherSuite.getMACLength())) {
147                     throw new SSLException(SSLException.PROTOCOL_VIOLATION, Messages.getString("SSLTransport.invalidMAC")); //$NON-NLS-1$
148
}
149
150             }
151         } catch (EOFException JavaDoc ex) {
152             throw ex;
153         } catch (InterruptedIOException JavaDoc ex) {
154             throw new SSLException(SSLException.READ_TIMEOUT);
155         } catch (IOException JavaDoc ex) {
156             throw new SSLException(SSLException.UNEXPECTED_TERMINATION, ex.getMessage() == null ? ex.getClass().getName()
157                 : ex.getMessage());
158         }
159
160         incomingSequence++;
161
162         // #ifdef DEBUG
163
log.debug(MessageFormat.format(Messages.getString("SSLTransport.processingFragmentOfType"), new Object JavaDoc[] { new Integer JavaDoc(type) })); //$NON-NLS-1$
164
// #endif
165

166         switch (type) {
167             case SSLHandshakeProtocol.HANDSHAKE_PROTOCOL_MSG:
168                 handshake.processMessage(fragment, 0, fragment.length - readCipherSuite.getMACLength());
169                 break;
170             case CHANGE_CIPHER_SPEC_MSG:
171
172                 // #ifdef DEBUG
173

174                 log.debug(Messages.getString("SSLTransport.changingInputCipherSpec")); //$NON-NLS-1$
175
// #endif
176

177                 readCipherSuite = handshake.getPendingCipherSuite();
178                 incomingSequence = 0;
179                 break;
180             case ALERT_PROTOCOL:
181                 switch (fragment[0]) {
182                     case FATAL_ALERT:
183                         throw new SSLException(((int) (fragment[1] & 0xFF)));
184                     case WARNING_ALERT:
185                         switch (fragment[1]) {
186                             case SSLException.CLOSE_NOTIFY:
187
188                                 // #ifdef DEBUG
189
log.debug(Messages.getString("SSLTransport.remoteSideClosing")); //$NON-NLS-1$
190
// #endif
191

192                                 sendMessage(ALERT_PROTOCOL, new byte[] { (byte) WARNING_ALERT, (byte) SSLException.CLOSE_NOTIFY });
193                                 // close();
194
// Let the InputStream know that we're at EOF
195
throw new EOFException JavaDoc();
196
197                             default:
198
199                                 // #ifdef DEBUG
200
log.warn(SSLException.getDescription(fragment[1]));
201                                 // #endif
202

203                                 break;
204                         }
205
206                         break;
207                     default:
208
209                         // #ifdef DEBUG
210
log.debug(MessageFormat.format(Messages.getString("SSLTransport.unexpectedAlert"), new Object JavaDoc[] { new Integer JavaDoc(fragment[0]), new Integer JavaDoc(fragment[1]) })); //$NON-NLS-1$
211
// #endif
212

213                         break;
214                 }
215             case APPLICATION_DATA:
216                 sslIn.write(fragment, 0, fragment.length - readCipherSuite.getMACLength());
217                 break;
218             default:
219                 throw new SSLException(SSLException.PROTOCOL_VIOLATION,
220                     Messages.getString("SSLTransport.unexpecedSSLProtocolType") + type); //$NON-NLS-1$
221

222         }
223
224     }
225
226     /* (non-Javadoc)
227      * @see com.maverick.ssl.SSLTransport#close()
228      */

229     public void close() throws SSLException {
230         sendMessage(ALERT_PROTOCOL, new byte[] { WARNING_ALERT, SSLException.CLOSE_NOTIFY });
231     }
232
233     void sendMessage(int type, byte[] fragment) throws SSLException {
234         sendMessage(type, fragment, 0, fragment.length);
235     }
236
237     void sendMessage(int type, byte[] fragment, int off, int len) throws SSLException {
238
239         // Compress the record?? - since were not using compression the
240
// record remains the same
241
byte[] encrypted;
242         // Calculate the mac
243
if (writeCipherSuite.getMACLength() > 0) {
244             byte[] mac = writeCipherSuite.generateMAC(fragment, off, len, type, outgoingSequence);
245             // Create the final encrypted packet
246
encrypted = new byte[len + mac.length];
247             System.arraycopy(fragment, off, encrypted, 0, len);
248             System.arraycopy(mac, 0, encrypted, len, mac.length);
249
250             // Encrypt the packet
251
writeCipherSuite.encrypt(encrypted, 0, encrypted.length);
252         } else {
253             if (off > 0 || fragment.length != len) {
254                 encrypted = new byte[len];
255                 System.arraycopy(fragment, off, encrypted, 0, len);
256             } else {
257                 encrypted = fragment;
258             }
259         }
260
261         // Create a record for sending
262
ByteArrayOutputStream JavaDoc record = new ByteArrayOutputStream JavaDoc();
263
264         try {
265             record.write(type);
266             record.write(SSLTransportImpl.VERSION_MAJOR);
267             record.write(SSLTransportImpl.VERSION_MINOR);
268             record.write((encrypted.length >> 8) & 0xFF);
269             record.write(encrypted.length);
270             record.write(encrypted);
271         } catch (IOException JavaDoc ex) {
272             throw new SSLException(SSLException.INTERNAL_ERROR, ex.getMessage() == null ? ex.getClass().getName() : ex.getMessage());
273         }
274
275         try {
276             // Send the record
277
rawOut.write(record.toByteArray());
278         } catch (IOException JavaDoc ex) {
279             throw new SSLException(SSLException.UNEXPECTED_TERMINATION, ex.getMessage() == null ? ex.getClass().getName()
280                 : ex.getMessage());
281         }
282
283         // TODO: check the limit and reset to zero if required
284
outgoingSequence++;
285     }
286
287     void sendCipherChangeSpec(SSLCipherSuite pendingCipherSuite) throws SSLException {
288
289         // #ifdef DEBUG
290
log.debug(Messages.getString("SSLTransport.changingOutputCipherSpec")); //$NON-NLS-1$
291
// #endif
292

293         sendMessage(CHANGE_CIPHER_SPEC_MSG, new byte[] { 1 });
294         writeCipherSuite = pendingCipherSuite;
295         outgoingSequence = 0;
296     }
297
298     /* (non-Javadoc)
299      * @see com.maverick.ssl.SSLTransport#getInputStream()
300      */

301     public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
302         return sslIn;
303     }
304
305     /* (non-Javadoc)
306      * @see com.maverick.ssl.SSLTransport#getOutputStream()
307      */

308     public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc {
309         return sslOut;
310     }
311
312     class SSLOutputStream extends OutputStream JavaDoc {
313         public synchronized void write(int b) throws IOException JavaDoc {
314             // #ifdef DEBUG
315
log.debug(Messages.getString("SSLTransport.sending1Byte")); //$NON-NLS-1$
316
// #endif
317
try {
318                 sendMessage(APPLICATION_DATA, new byte[] { (byte) b });
319             } catch (SSLException ex) {
320                 throw new SSLIOException(ex);
321             }
322         }
323
324         public synchronized void write(byte[] b, int off, int len) throws IOException JavaDoc {
325             // #ifdef DEBUG
326
log.debug(MessageFormat.format(Messages.getString("SSLTransport.bytesToSend"), new Object JavaDoc[] { new Integer JavaDoc(len) })); //$NON-NLS-1$
327
// #endif
328

329             try {
330                 int pos = 0;
331                 while (pos < len) {
332                     // #ifdef DEBUG
333
log.debug(MessageFormat.format(Messages.getString("SSLTransport.sendingBlock"), new Object JavaDoc[] { new Integer JavaDoc((len - pos) < 16384 ? len - pos : 16384) })); //$NON-NLS-1$
334
// #endif
335
sendMessage(APPLICATION_DATA, b, off + pos, ((len - pos) < 16384 ? len - pos : 16384));
336                     pos += (len < 16384 ? len : 16384);
337                 }
338             } catch (SSLException ex) {
339                 throw new SSLIOException(ex);
340             }
341         }
342     }
343
344     class SSLInputStream extends InputStream JavaDoc {
345
346         byte[] buffer;
347         int unread = 0;
348         int position = 0;
349         int base = 0;
350         boolean isEOF = false;
351
352         SSLInputStream() {
353             buffer = new byte[16384];
354         }
355
356         public int available() {
357             return unread;
358         }
359
360         public int read() throws IOException JavaDoc {
361             byte[] b = new byte[1];
362             int ret = read(b, 0, 1);
363             if (ret > 0) {
364                 return b[0] & 0xFF;
365             } else {
366                 return -1;
367             }
368         }
369
370         long transfered = 0;
371
372         public int read(byte[] buf, int offset, int len) throws IOException JavaDoc {
373
374             try {
375
376                 while (unread <= 0 && !isEOF) {
377                     processMessages();
378                 }
379
380                 int count = unread < len ? unread : len;
381                 int index = base;
382                 base = (base + count) % buffer.length;
383                 if (buffer.length - index > count) {
384                     System.arraycopy(buffer, index, buf, offset, count);
385                 } else {
386                     int remaining = buffer.length - index;
387                     System.arraycopy(buffer, index, buf, offset, remaining);
388                     System.arraycopy(buffer, 0, buf, offset + remaining, count - remaining);
389                 }
390
391                 unread -= count;
392
393                 return count;
394             } catch (EOFException JavaDoc ex) {
395                 return -1;
396             } catch (SSLException ex) {
397                 throw new SSLIOException(ex);
398             }
399
400         }
401
402         void write(byte[] buf, int offset, int len) {
403
404             for (int i = offset; i < offset + len; i++) {
405                 // puts a value from the circular buffer
406
int index = (base + unread) % buffer.length;
407                 buffer[index] = buf[i];
408                 unread++;
409             }
410
411         }
412     }
413
414 }
415
Popular Tags