001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.ssl;
028:
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.OutputStream;
032: import java.io.DataInputStream;
033: import java.io.DataOutputStream;
034:
035: import javax.microedition.io.Connector;
036: import javax.microedition.io.SecurityInfo;
037: import javax.microedition.io.StreamConnection;
038:
039: import javax.microedition.pki.Certificate;
040:
041: import com.sun.midp.pki.*;
042:
043: /**
044: * The SSLStreamConnection class implements the StreamConnection
045: * interface. Data exchanged through a SSLStreamConnection is
046: * automatically protected by SSL. Currently, only SSL version 3.0
047: * is supported and the list of cipher suites proposed
048: * by the client is hardcoded to {SSL_RSA_WITH_RC4_128_MD5,
049: * SSL_RSA_EXPORT_WITH_RC4_40_MD5}. This version of the implementation
050: * does not support client authentication at the SSL layer -- a feature
051: * that is rarely used.
052: *
053: * Typical usage of this class by an application would be along the
054: * following lines: <BR />
055: *
056: * <PRE>
057: * // create a TCP connection
058: * StreamConnection t = Connector.open("socket://www.server.com:443");
059: *
060: * // Create an SSL connection
061: * SSLStreamConnection s = new SSLStreamConnection("www.server.com", 443,
062: * t.openInputStream(), t.openOutputStream());
063: * t.close();
064: *
065: * // obtain the associated input/output streams
066: * OutputStream sout = s.openOutputStream();
067: * InputStream sin = s.openInputStream();
068: * ...
069: * // send SSL-protected data by writing to sout and
070: * // receive SSL-protected by reading from sin
071: * ...
072: * sin.close();
073: * sout.close();
074: * s.close(); // close the SSL connection when done
075: *
076: * </PRE>
077: *
078: */
079:
080: public class SSLStreamConnection implements StreamConnection {
081: /** Indicates that a is ready to be opened. */
082: static final int READY = 0;
083: /** Indicates that a stream is opened. */
084: static final int OPEN = 1;
085: /** Indicates that a stream is closed. */
086: static final int CLOSED = 2;
087:
088: /** Current record being processed. */
089: private Record rec = null;
090: /** Input stream for buffered records. */
091: private In uin = null;
092: /** Output stream for buffered records. */
093: private Out uout = null;
094: /** Raw encrypted input stream. */
095: private InputStream sin = null;
096: /** Raw encrypted output stream. */
097: private OutputStream sout = null;
098: /** Current host name. */
099: private String host = null;
100: /** Current port number. */
101: private int port = 0;
102: /** Flag indicating the underlying TCP connection is open. */
103: private boolean copen = false;
104: /** Server certificate from a successful handshake. */
105: private X509Certificate serverCert;
106: /** Cipher suite from a successful handshake. */
107: private String cipherSuite;
108:
109: /*
110: * The following are visible within the package so In and Out can
111: * manipulate them directly
112: */
113: /** State of the input stream given out by getInputStream. */
114: int inputStreamState;
115: /** State of the output stream given out by getOutputStream. */
116: int outputStreamState;
117:
118: /**
119: * Establish and SSL session over a reliable stream.
120: * This connection will forward the input and output stream close methods
121: * to the given connection. If the caller wants to have the given
122: * connection closed with this connection, the caller can close given
123: * connection after constructing this connection, but leaving the closing
124: * of the streams to this connection.
125: *
126: * @param host hostname of the SSL server
127: * @param port port number of the SSL server
128: * @param in InputStream associated with the StreamConnection
129: * @param out OutputStream associated with the StreamConnection
130: * @param cs trusted certificate store to be used for this connection
131: *
132: * @exception IOException if there is a problem initializing the SSL
133: * data structures or the SSL handshake fails
134: */
135: public SSLStreamConnection(String host, int port, InputStream in,
136: OutputStream out, CertStore cs) throws IOException {
137:
138: if (cs == null) {
139: throw new IllegalArgumentException(
140: "SSLStreamConnection: no trusted certificate store given");
141: }
142:
143: if ((in == null) || (out == null)) {
144: throw new IllegalArgumentException(
145: "SSLStreamConnection: stream missing");
146: }
147:
148: this .host = host;
149: this .port = port;
150: this .sin = in;
151: this .sout = out;
152:
153: this .rec = new Record(sin, sout);
154:
155: uin = new In(rec, this );
156: uout = new Out(rec, this );
157:
158: try {
159: Handshake hndshk = new Handshake(host, port, rec, cs);
160:
161: hndshk.doHandShake(Record.CLIENT);
162: serverCert = hndshk.sCert;
163: cipherSuite = hndshk.negSuiteName;
164: } catch (IOException e) {
165: cleanupIfNeeded();
166: throw e;
167: }
168:
169: copen = true;
170: }
171:
172: /**
173: * Returns the InputStream associated with this SSLStreamConnection.
174: *
175: * @return InputStream object from which SSL protected bytes can
176: * be read
177: * @exception IOException if the connection is not open or the stream was
178: * already open
179: */
180: synchronized public InputStream openInputStream()
181: throws IOException {
182: if (!copen) {
183: throw new IOException("Connection closed");
184: }
185:
186: if (inputStreamState != READY) {
187: throw new IOException("Input stream already opened");
188: }
189:
190: inputStreamState = OPEN;
191: return (uin);
192: }
193:
194: /**
195: * Returns the OutputStream associated with this SSLStreamConnection.
196: *
197: * @return OutputStream object such that bytes written to this stream
198: * are sent over an SSL secured channel
199: * @exception IOException if the connection is not open or the stream was
200: * already open
201: */
202: synchronized public OutputStream openOutputStream()
203: throws IOException {
204: if (!copen) {
205: throw new IOException("Connection closed");
206: }
207:
208: if (outputStreamState != READY) {
209: throw new IOException("Output stream already opened");
210: }
211:
212: outputStreamState = OPEN;
213: return (uout);
214: }
215:
216: /**
217: * Returns the DataInputStream associated with this SSLStreamConnection.
218: * @exception IOException if the connection is not open or the stream was
219: * already open
220: * @return a DataInputStream object
221: */
222: public DataInputStream openDataInputStream() throws IOException {
223: return (new DataInputStream(openInputStream()));
224: }
225:
226: /**
227: * Returns the DataOutputStream associated with this SSLStreamConnection.
228: * @exception IOException if the connection is not open or the stream was
229: * already open
230: * @return a DataOutputStream object
231: */
232: public DataOutputStream openDataOutputStream() throws IOException {
233: return (new DataOutputStream(openOutputStream()));
234: }
235:
236: /**
237: * Closes the SSL connection. The underlying TCP socket, over which
238: * SSL is layered, is also closed unless the latter was opened by
239: * an external application and its input/output streams were passed
240: * as argument to the SSLStreamConnection constructor.
241: *
242: * @exception IOException if the SSL connection could not be
243: * terminated cleanly
244: */
245: synchronized public void close() throws IOException {
246: if (copen) {
247: copen = false;
248: cleanupIfNeeded();
249: }
250: }
251:
252: /**
253: * Returns the security information associated with this connection.
254: *
255: * @return the security information associated with this open connection
256: *
257: * @exception IOException if the connection is closed
258: */
259: public SecurityInfo getSecurityInfo() throws IOException {
260: if (!copen) {
261: throw new IOException("Connection closed");
262: }
263:
264: return new SSLSecurityInfo(this );
265: }
266:
267: /**
268: * Returns the server certificate associated with this connection.
269: *
270: * @return the server certificate associated with this connection
271: */
272: public X509Certificate getServerCertificate() {
273: return serverCert;
274: }
275:
276: /**
277: * Returns the cipher suite in use for the connection.
278: * The value returned is one of the CipherSuite definitions
279: * in Appendix C of RFC 2246.
280: * The cipher suite string should be used to represent the
281: * actual parameters used to establish the connection regardless
282: * of whether the secure connection uses SSL V3 or TLS 1.0 or WTLS.
283: *
284: * @return a String containing the cipher suite in use
285: */
286: String getCipherSuite() {
287: return cipherSuite;
288: }
289:
290: /**
291: * Closes the SSL connection. The underlying TCP socket, over which
292: * SSL is layered, is also closed unless the latter was opened by
293: * an external application and its input/output streams were passed
294: * as argument to the SSLStreamConnection constructor.
295: *
296: * @exception IOException if the SSL connection could not be
297: * terminated cleanly
298: */
299: void cleanupIfNeeded() throws IOException {
300: if (copen || inputStreamState == OPEN
301: || outputStreamState == OPEN || rec == null) {
302: // we do not need to cleanup
303: return;
304: }
305:
306: rec.shutdownConnection();
307: rec = null;
308: }
309: }
310:
311: /**
312: * This class implements methods
313: * to access information about a SSL secure network connection.
314: */
315: class SSLSecurityInfo implements SecurityInfo {
316:
317: /** Parent connection. */
318: private SSLStreamConnection parent;
319:
320: /**
321: * Creates a <code>SecurityInfo</code> object to pass back to
322: * an application.
323: *
324: * @param parentObj parent object
325: */
326: SSLSecurityInfo(SSLStreamConnection parentObj) {
327: parent = parentObj;
328: }
329:
330: /**
331: * Gets the <CODE>Certificate</CODE> used to establish the
332: * secure connection with the server.
333: *
334: * @return the <CODE>Certificate</CODE> used to establish the
335: * secure connection with the server
336: */
337: public Certificate getServerCertificate() {
338: return parent.getServerCertificate();
339: }
340:
341: /**
342: * Returns the security status of the connection.
343: *
344: * @return <CODE>true</CODE> if the connection has been made directly to
345: * the server specified in <code>Connector.open</code> and a handshake
346: * with that server has established a secure connection.
347: * <CODE>false</CODE> is returned otherwise
348: */
349: public boolean isSecure() {
350: return true;
351: }
352:
353: /**
354: * Returns the protocol version.
355: * If appropriate, it should contain the major and minor versions
356: * for the protocol separated with a "." (Unicode x2E).
357: * For example,
358: * for SSL V3 it MUST return "3.0";
359: * for TLS 1.0 it MUST return "3.1".
360: *
361: * @return a String containing the version of the protocol
362: */
363: public String getProtocolVersion() {
364: return "3.0";
365: }
366:
367: /**
368: * Returns the secure protocol name.
369: *
370: * @return a <code>String</code> containing the secure protocol identifier;
371: * if TLS (RFC 2246) is used for the connection the return value is "TLS".
372: * If SSL V3 (The SSL Protocol Version 3.0) is used for the connection
373: * the return value is "SSL").
374: * If WTLS (WAP 199) is used for the connection the return value is "WTLS".
375: */
376: public String getProtocolName() {
377: return "SSL";
378: }
379:
380: /**
381: * Returns the cipher suite in use for the connection.
382: * The value returned is one of the CipherSuite definitions
383: * in Appendix C of RFC 2246.
384: * The cipher suite string should be used to represent the
385: * actual parameters used to establish the connection regardless
386: * of whether the secure connection uses SSL V3 or TLS 1.0 or WTLS.
387: *
388: * @return a String containing the cipher suite in use
389: */
390: public String getCipherSuite() {
391: return parent.getCipherSuite();
392: }
393: }
|