Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/ciphercore/Client.java


1   /* $Id: Client.java,v 1.9 2001/04/02 08:42:50 cvsbob Exp $ */
2   
3   /*
4    * Client.java, core class for use by ciphercore clients.
5    * Copyright (C) 2001 Robert Bushman.
6    *
7    * I reserve the right to release this program under seperate license.
8    * If you require a special license grant contact Robert Bushman.
9    *
10   * This program is free software; you can redistribute it and/or
11   * modify it under the terms of the GNU General Public License
12   * as published by the Free Software Foundation; either version 2
13   * of the License, or (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Public License for more details.
19   * 
20   * You should have received a copy of the GNU General Public License
21   * along with this program; if not, write to the Free Software
22   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 
23   * 02111-1307, USA.
24   */
25  
26  package com.ciphercore;
27  
28  import java.io.BufferedInputStream;
29  import java.io.DataInputStream;
30  import java.io.DataOutputStream;
31  import java.io.FileInputStream;
32  import java.io.InputStream;
33  import java.io.IOException;
34  import java.io.OutputStream;
35  import java.math.BigInteger;
36  import java.net.Socket;
37  import java.security.InvalidAlgorithmParameterException;
38  import java.security.InvalidKeyException;
39  import java.security.Provider;
40  import java.security.Security;
41  import java.security.spec.InvalidKeySpecException;
42  import java.util.Properties;
43  import javax.crypto.Cipher;
44  import javax.crypto.CipherInputStream;
45  import javax.crypto.CipherOutputStream;
46  
47  import com.traxel.crypto.CipherPartner;
48  import com.traxel.crypto.CryptoUtil;
49  import com.traxel.crypto.Sha1Util;
50  import com.traxel.util.ClassUtil;
51  
52  /**
53   * Client is the class that establishes the connection
54   * with the Child. It handles all the crypto negotiation
55   * and prepares a pair of streams for communicating with
56   * your child.
57   * <br><br>
58   * <b>Typical Usage:</b><pre><tt>
59   * import com.ciphercore.Client;
60   * import com.ciphercore.Server;
61   * 
62   * public class MyClient {
63   *     
64   *     public static void main( String[] args ) {
65   *         
66   *         // Instantiate a client, tell it the child type, host and port.
67   *         Client client = new Client( "mypackage.MyChild",
68   *                                     "localhost",
69   *                                     Server.DEFAULT_PORT );
70   *         
71   *         // Tell the client to establish the connection to the child.
72   *         client.connect();
73   *         
74   *         try {
75   *             
76   *             // Get the streams that connect to the Child.
77   *             InputStream in = client.getBaseInputStream();
78   *             OutputStream out = client.getBaseOutputStream();
79   *             
80   *             // Do whatever you do.
81   *             while( ! finished ) {
82   *                 ... Communicate With MyChild ...
83   *             }
84   *             
85   *             // Not vital (Client closes them), but good habit.
86   *             out.close();
87   *             in.close();
88   *         
89   *         } catch( IOException e ) {
90   *             ... handle the exception ...
91   *         } finally {
92   *             // Release the network connection.
93   *             client.close();
94   *         }
95   *     }
96   * }
97   * </tt></pre>
98   */
99  public class Client {
100     
101     // -------------------------------------------------------
102     // CONSTANTS
103     // -------------------------------------------------------
104     
105     /** If no host is specified, connect to "localhost". */
106     public static final String DEFAULT_HOST_NAME = "localhost";
107     
108     // -------------------------------------------------------
109     // CONSTRUCTORS
110     // -------------------------------------------------------
111     
112     /**
113      * Creates a client using the default host and port.
114      * 
115      * @param   childClassName  Specifies the class of the child that
116      *                          this client communicates with.
117      */
118     public Client( String childClassName ) {
119         this( childClassName, DEFAULT_HOST_NAME, Server.DEFAULT_PORT );
120     }
121     
122     /**
123      * Creates a client using the specified host and port.
124      * 
125      * @param   childClassName  Specifies the class of the child that
126      *                          this client communicates with.
127      * @param   hostName        Specifies the hostname where the
128      *                          CipherCore server is located.
129      * @param   port            Specifiest the port that the CipherCore
130      *                          server is listening on.
131      */
132     public Client( String childClassName,
133                    String hostName,
134                    int port ) {
135         locateFiles();
136         readBasicCfg();
137         readAdvancedCfg();
138         readServerHashes();
139         setChildClassName( childClassName );
140         setHostName( hostName );
141         setPort( port );
142         CryptoUtil.initializeProvider( getProviderName() );
143         setCipherSelf( new CipherPartner( "Client", DHStock.PARAMS ) );
144     }
145     
146     // ----------------------------------------------------------
147     // PUBLIC API
148     // ----------------------------------------------------------
149     
150     /**
151      * Establishes the connection as specified in the constructor.
152      * This is where host switching would be implemented
153      * if this system is intended to be capable of talking
154      * to a balanced cluster.
155      * 
156      * @exception       IOException     If the connection fails.
157      */
158     public void connect()
159         throws IOException,
160                ServerKeyException,
161                UserPasswordException {
162         
163         Socket                          socket;
164         InputStream                     rawIn;
165         OutputStream                    rawOut;
166         BufferedInputStream             buffIn;
167         DataInputStream                 in;
168         DataOutputStream                out;
169         
170         socket = new Socket( getHostName(), getPort() );
171         rawIn = socket.getInputStream();
172         rawOut = socket.getOutputStream();
173         buffIn = new BufferedInputStream( rawIn );
174         in = new DataInputStream( buffIn );
175         out = new DataOutputStream( rawOut );
176         
177         System.out.println( "Requesting Child Type: " + getChildClassName() );
178         
179         out.writeInt( getChildClassName().length() );
180         out.write( getChildClassName().getBytes() );
181         
182         // FOR CLUSTER BALANCING ADD SOMETHING LIKE:
183         // in.readFully( newHostNameBytes );
184         
185         try {
186             System.out.println( "Negotiating Shared Secret" );
187             
188             int publicEncodedLength = in.readInt();
189             byte[] serverPublicEncoded = new byte[ publicEncodedLength ];
190             in.readFully( serverPublicEncoded );
191             
192             verifyServer( serverPublicEncoded );
193             
194             getCipherSelf().initFromPartner( serverPublicEncoded );
195             
196             out.writeInt( getPublicEncoded().length );
197             out.write( getPublicEncoded() );
198             out.writeInt( getIv().length );
199             out.write( getIv() );
200             
201             CipherInputStream cipherIn = 
202                 new CipherInputStream( rawIn, getDecryptCipher() );
203             CipherOutputStream cipherOut =
204                 new CipherOutputStream( rawOut, getEncryptCipher() );
205             
206             setBaseInputStream( cipherIn );
207             setBaseOutputStream( cipherOut );
208             
209             sendPassword();
210             
211         } catch( ServerKeyException e ) {
212             System.out.println( "Caught ServerKeyException" );
213             try {
214                 out.writeInt( -1 );
215             } catch( Exception f ) {
216                 e.printStackTrace();
217                 System.out.println( "out.writeInt( -1 ) exception" );
218                 // Not much we can do about this.
219             }
220             throw e;
221         } catch( InvalidKeyException e ) {
222             e.printStackTrace();
223             System.exit( 1 );
224         } catch( InvalidAlgorithmParameterException e ) {
225             e.printStackTrace();
226             System.exit( 1 );
227         } catch( InvalidKeySpecException e ) {
228             e.printStackTrace();
229             System.exit( 1 );
230         }
231     }
232     
233     protected void sendPassword()
234         throws UserPasswordException,
235                IOException {
236         DataInputStream         in;
237         DataOutputStream        out;
238         int                     serverResponse;
239         
240         in = new DataInputStream( getBaseInputStream() );
241         out = new DataOutputStream( getBaseOutputStream() );
242         
243         out.writeInt( getUserName().getBytes().length );
244         out.write( getUserName().getBytes() );
245         out.writeInt( getPassword().getBytes().length );
246         out.write( getPassword().getBytes() );
247         serverResponse = in.readInt();
248         
249         if( serverResponse == CipherCoreProtocol.PASSWORD_NOT_REGISTERED ) {
250             throw new UserPasswordException
251                 ( UserPasswordException.TYPE_USER_UNKNOWN );
252         }
253         if( serverResponse == CipherCoreProtocol.PASSWORD_INCORRECT ) {
254             throw new UserPasswordException
255                 ( UserPasswordException.TYPE_PASSWORD_INCORRECT );
256         }
257     }
258     
259     protected void verifyServer( byte[] serverPublicEncoded )
260         throws ServerKeyException {
261         
262         String serverHash;
263         ServerKeyException digestException;
264         
265         serverHash = getServerHash( getHostName() );
266         if( serverHash == null ) {
267             digestException = new ServerKeyException
268                 ( ServerKeyException.TYPE_HOST_UNKNOWN );
269             digestException.setHostName( getHostName() );
270             digestException.setCurrentDigest( serverHash );
271             digestException.setNewDigest
272                 ( Sha1Util.getSha1( serverPublicEncoded ) );
273             throw digestException;
274         } else {
275             System.out.println( "Found hash for " + getHostName() + "." );
276         }
277         
278         try {
279             boolean serverVerified =
280                 Sha1Util.verify( serverPublicEncoded, serverHash );
281             if( ! serverVerified ) {
282                 digestException = new ServerKeyException
283                     ( ServerKeyException.TYPE_KEY_VERIFICATION_FAILURE );
284                 digestException.setHostName( getHostName() );
285                 digestException.setCurrentDigest( serverHash );
286                 digestException.setNewDigest
287                     ( Sha1Util.getSha1( serverPublicEncoded ) );
288                 throw digestException;
289             } else {
290                 System.out.println( "Server key matches hash." );
291             }
292         } catch( NumberFormatException e ) {
293             digestException = new ServerKeyException
294                 ( ServerKeyException.TYPE_DIGEST_CORRUPT );
295             digestException.setHostName( getHostName() );
296             digestException.setCurrentDigest( (BigInteger)null );
297             digestException.setNewDigest
298                 ( Sha1Util.getSha1( serverPublicEncoded ) );
299             throw digestException;
300         }
301     }
302     
303     /** Closes the IO Streams and the socket. */
304     public void close() {
305         try {
306             getBaseOutputStream().close();
307             getBaseInputStream().close();
308         } catch( Exception e ) {
309         } finally {
310             try {
311                 getSocket().close();
312             } catch( Exception e ) {}
313         }
314     }
315     
316     // -------------------------------------------------------------
317     // INTERNAL API
318     // -------------------------------------------------------------
319     
320     protected void locateFiles() {
321         String byteCodePath = ClassUtil.pathToBytecode( this );
322         String configPath = null;
323         String jarLocation = "/lib/";
324         String classLocation = "/src/com/ciphercore/Client.class";
325         if( byteCodePath.indexOf( ".jar" ) > -1 ) {
326             configPath = byteCodePath.substring
327                 ( 5, byteCodePath.lastIndexOf( jarLocation ) );
328         } else {
329             configPath = byteCodePath.substring
330                 ( 5, byteCodePath.lastIndexOf( classLocation ) );
331         }
332         configPath = configPath + "/config/client";
333         
334         setBasicCfgLocation( configPath + "/basic.cfg" );
335         setAdvancedCfgLocation( configPath + "/advanced.cfg" );
336         setServerHashesLocation( configPath + "/server.hashes" );
337     }
338     
339     protected void readServerHashes() {
340         FileInputStream         fileIn;
341         Properties              serverHashes;
342         
343         serverHashes = new Properties();
344         try {
345             fileIn = new FileInputStream( getServerHashesLocation() );
346             serverHashes.load( fileIn );
347         } catch( Exception e ) {
348             e.printStackTrace();
349             System.err.println( "Failed to access server hashes file." );
350             System.err.println( "  Location: " + getServerHashesLocation() );
351             System.exit( 1 );
352         }
353         setServerHashes( serverHashes );
354     }
355     
356     protected void readBasicCfg() {
357         FileInputStream fileIn;
358         Properties properties;
359         
360         properties = new Properties();
361         try {
362             fileIn = new FileInputStream( getBasicCfgLocation() );
363             properties.load( fileIn );
364         } catch( Exception e ) {
365             e.printStackTrace();
366             System.err.println( "Failed to access basic config file." );
367             System.err.println( "  Location: " + getBasicCfgLocation() );
368             System.exit( 1 );
369         }
370     }
371     
372     protected void readAdvancedCfg() {
373         FileInputStream fileIn;
374         Properties properties;
375         
376         properties = new Properties();
377         try {
378             fileIn = new FileInputStream( getAdvancedCfgLocation() );
379             properties.load( fileIn );
380         } catch( Exception e ) {
381             e.printStackTrace();
382             System.err.println( "Failed to access advanced config file." );
383             System.err.println( "  Location: " + getAdvancedCfgLocation() );
384             System.exit( 1 );
385         }
386     }
387     
388     // ------------------------------------------------------------------
389     // INSTANCE PARAMETERS AND ACCESSORS
390     // ------------------------------------------------------------------
391     
392     // PARAMETERS
393     private String _childClassName;
394     private String _hostName;
395     private String _userName = "bob";
396     private String _password = "arglebargle";
397     private int _port;
398     private Socket _socket;
399     private CipherPartner _cipherSelf;
400     private InputStream _baseIn;
401     private OutputStream _baseOut;
402     private String _basicCfgLocation;
403     private String _advancedCfgLocation;
404     private String _serverHashesLocation;
405     private Properties _serverHashes;
406     
407     // SETTERS
408     protected void setCipherSelf( CipherPartner self ) { _cipherSelf = self; }
409     protected void setChildClassName( String name ) { _childClassName = name; }
410     protected void setHostName( String name ) { _hostName = name; }
411     protected void setPort( int port ) { _port = port; }
412     protected void setSocket( Socket socket ) { _socket = socket; }
413     protected void setBasicCfgLocation( String location ) {
414         _basicCfgLocation = location;
415     }
416     protected void setAdvancedCfgLocation( String location ) {
417         _advancedCfgLocation = location;
418     }
419     protected void setServerHashesLocation( String location ) {
420         _serverHashesLocation = location;
421     }
422     protected void setServerHashes( Properties serverHashes ) {
423         _serverHashes = serverHashes;
424     }
425     
426     protected void setBaseInputStream( InputStream baseIn ) {
427         _baseIn = baseIn;
428     }
429     protected void setBaseOutputStream( OutputStream baseOut ) {
430         _baseOut = baseOut;
431     }
432     public void setUserName( String userName ) {
433         _userName = userName;
434     }
435     public void setPassword( String password ) {
436         _password = password;
437     }
438     
439     // GETTERS
440     /** Returns the childClassName that this client is configured for. */
441     public String getChildClassName() { return( _childClassName ); }
442     /** Returns the hostname that this client is configured for. */
443     public String getHostName() { return( _hostName ); }
444     /** Returns the port that this client is configured for. */
445     public int getPort() { return( _port ); }
446     /** Returns the InputStream that connects to your child's OutputStream */
447     public OutputStream getBaseOutputStream() { return( _baseOut ); }
448     /** Returns the OutputStream the connects to your child's InputStream */
449     public InputStream getBaseInputStream() { return( _baseIn ); }
450     public String getBasicCfgLocation() { return( _basicCfgLocation ); }
451     public String getAdvancedCfgLocation() { return( _advancedCfgLocation ); }
452     public String getServerHashesLocation() {
453         return( _serverHashesLocation );
454     }
455     public Properties getServerHashes() { return( _serverHashes ); }
456     public String getProviderName() { return( Server.DEFAULT_PROVIDER_NAME ); }
457     public String getUserName() { return( _userName ); }
458     public String getPassword() { return( _password ); }
459     
460     protected Socket getSocket() { return( _socket ); }
461     protected CipherPartner getCipherSelf() { return( _cipherSelf ); }
462     
463     // CONVENIENCE METHODS
464     protected Cipher getEncryptCipher() {
465         return( getCipherSelf().getEncryptCipher() );
466     }
467     protected Cipher getDecryptCipher() {
468         return( getCipherSelf().getDecryptCipher() );
469     }
470     protected byte[] getPublicEncoded() {
471         return( getCipherSelf().getPublicEncoded() );
472     }
473     protected byte[] getIv()
474         throws IOException {
475         return( getCipherSelf().getIv() );
476     }
477     public String getServerHash( String serverName ) {
478         return( (String)getServerHashes().get( serverName ) );
479     }
480     public void setServerHash( String serverName, String hashBase16 ) {
481         getServerHashes().put( serverName, hashBase16 );
482     }
483 }