KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jgroups > protocols > ENCRYPT


1
2
3 //$Id: ENCRYPT.java,v 1.8 2005/04/11 14:34:13 steview Exp $
4

5 package org.jgroups.protocols;
6
7 import java.io.IOException JavaDoc;
8 import java.io.InputStream JavaDoc;
9 import java.security.InvalidKeyException JavaDoc;
10 import java.security.KeyFactory JavaDoc;
11 import java.security.KeyPair JavaDoc;
12 import java.security.KeyPairGenerator JavaDoc;
13 import java.security.KeyStore JavaDoc;
14 import java.security.KeyStoreException JavaDoc;
15 import java.security.MessageDigest JavaDoc;
16 import java.security.NoSuchAlgorithmException JavaDoc;
17 import java.security.PublicKey JavaDoc;
18 import java.security.SecureRandom JavaDoc;
19 import java.security.UnrecoverableKeyException JavaDoc;
20 import java.security.cert.CertificateException JavaDoc;
21 import java.security.spec.X509EncodedKeySpec JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Properties JavaDoc;
24 import java.util.WeakHashMap JavaDoc;
25
26 import javax.crypto.BadPaddingException;
27 import javax.crypto.Cipher;
28 import javax.crypto.IllegalBlockSizeException;
29 import javax.crypto.KeyGenerator;
30 import javax.crypto.NoSuchPaddingException;
31 import javax.crypto.SecretKey;
32 import javax.crypto.spec.SecretKeySpec;
33
34 import org.jgroups.Address;
35 import org.jgroups.Event;
36 import org.jgroups.Message;
37 import org.jgroups.View;
38 import org.jgroups.stack.Protocol;
39 import org.jgroups.util.QueueClosedException;
40
41 import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
42
43
44 /**
45 * ENCRYPT layer. Encrypt and decrypt the group communication in JGroups
46 *
47 * The file can be used in two ways:
48 * <ul>
49 * <li> Option 1. Configured with a secretKey in a keystore so it can be used at any
50 * layer in JGroups without the need for a coordinator, or if you want protection against passive
51 * monitoring but do not want the key exchange overhead and complexity. In this mode all nodes must be distributed with
52 * the same keystore file.
53 * <li> Option 2. Configured with algorithms and key sizes. The Encrypt Layer in this mode sould be used between the
54 * FRAG and PBCast layers in the stack. The coordinator then chooses the
55 * secretkey which it distributes amongst all the peers. In this form no keystore exists as the keys are
56 * distributed using a public/private key exchange. View changes that identify a new controller will result in a new session key
57 * being generated and then distributed to all peers. This overhead can be substantial in a an application with
58 * a reasonable peer churn.
59 * </ul>
60 * <p>
61 * <p>
62 * Each message is identified as encrypted with a specific encryption header which identifies
63 * the type of encrypt header and an MD5 digest that identifies the version of the key being used
64 * to encrypt/decrypt the messages.
65 * <p>
66 * <p>
67 *<h2>Option 1</h2><br>
68 * This is the simplest option and can be used by simply inserting the Encryption layer
69 * at any point in the JGroup stack - it will encrypt all Events of a type MSG that
70 * have a non-null message buffer. The format of the entry in this form is:<br>
71 * &lt;ENCRYPT key_store_name="defaultStore.keystore" store_password="changeit" alias="myKey"/&gt;<br>
72 * An example bare-bones.xml file showing the keystore version can be found in the conf
73 * ina file called EncryptKeyStore.xml - along with a defaultStore.keystore file.<br>
74 * In order to use the Encrypt layer in this manner it is necessary to have the secretKey already generated
75 * in a keystore file. The directory containing the keystore file must be on the application's classpath.
76 * You cannot create a SecretKey keystore file using the keytool application shipped with the JDK.
77 * A java file called KeyStoreGenerator is included in the demo
78 * package that can be used from the command line (or IDE) to generate a suitable keystore.
79 * <p>
80 * <p>
81 *<h2>Option 2</h2><br>
82 * This option is suited to an application that does not ship with a known key but instead it is generated
83 * and distributed by the controller. The secret key is first generated by the Controller (in JGroup terms). When a view change
84 * occurs a peer will request the secret key by sending a key request with its own public key. The controller
85 * encrypts the secret key with this key and sends it back to the peer who then decrypts it and installs the
86 * key as its own secret key.
87 * <br>All encryption and decryption of Messages is done using this key. When a peer receives
88 * a view change that shows a different keyserver it will repeat this process - the view change event
89 * also trigger the encrypt layer to queue up and down messages until the new key is installed.
90 * The previous keys are retained so that messages sent before the view change that are queued can be decrypted
91 * if the key is different.
92 * <br>
93 * An example EncryptNoKeyStore.xml is included in the conf file as a guide.
94 * <p><p>
95 * <br> Note: the current version does not support the concept of perfect forward encryption (PFE)
96 * which means that if a peer leaves the group the keys are re-generated preventing the departed peer from
97 * decrypting future messages if it chooses to listen in on the group. This is not included as it really requires
98 * a suitable authentication scheme as well to make this feature useful as there is nothing to stop the peer rejoining and receiving the new
99 * key. A future release will address this issue.
100 *
101 * @author Steve Woodcock
102 */

103
104
105 public class ENCRYPT extends Protocol {
106
107     public static class EncryptHeader extends org.jgroups.Header {
108         int type;
109         public static final int ENCRYPT = 0;
110         public static final int KEY_REQUEST = 1;
111         public static final int SERVER_PUBKEY = 2;
112         public static final int SECRETKEY = 3;
113         public static final int SECRETKEY_READY = 4;
114
115         // adding key for Message object purpose
116
static final String JavaDoc KEY = "encrypt";
117
118         String JavaDoc version;
119
120
121         public EncryptHeader()
122         {
123             //this(type, 0l);
124
}
125
126
127         public EncryptHeader(int type)
128         {
129             //this(type, 0l);
130
this.type = type;
131             this.version = "";
132         }
133
134
135         public EncryptHeader(int type, String JavaDoc version)
136         {
137             this.type = type;
138             this.version = version;
139         }
140
141
142         public void writeExternal(java.io.ObjectOutput JavaDoc out) throws IOException JavaDoc
143         {
144             out.writeInt(type);
145             out.writeObject(version);
146         }
147
148
149         public void readExternal(java.io.ObjectInput JavaDoc in) throws IOException JavaDoc,
150                 ClassNotFoundException JavaDoc
151         {
152             //System.out.println("reading in int");
153
type = in.readInt();
154             //System.out.println("reading in long");
155
version = (String JavaDoc)in.readObject();
156         }
157
158
159         public String JavaDoc toString()
160         {
161             return "{ENCTYPT:[Type:" + type + " Version:" + version + "]}";
162             // return "{ENCTYPT:[Type:"+type + "]";
163
}
164
165
166         /*
167          * (non-Javadoc)
168          *
169          * @see java.lang.Object#equals(java.lang.Object)
170          */

171         public boolean equals(Object JavaDoc obj)
172         {
173             // TODO Auto-generated method stub
174
if (obj instanceof EncryptHeader)
175             {
176                 boolean res = ((((EncryptHeader) obj).getType() == type) && ((((EncryptHeader) obj)
177                         .getVersion() == version)));
178                 return res;
179             }
180             return false;
181         }
182
183
184         /**
185          * @return Returns the type.
186          */

187         protected int getType()
188         {
189             return type;
190         }
191
192
193         /**
194          * @return Returns the version.
195          */

196         protected String JavaDoc getVersion()
197         {
198             return version;
199         }
200
201     }
202
203     // address info
204
Address local_addr = null;
205     // keyserver address
206
Address keyServerAddr = null;
207
208     //used to see whether we are the key server
209
boolean keyServer = false;
210
211     // encryption properties in no supplied key mode
212
String JavaDoc asymProvider = null;
213     final String JavaDoc symProvider = null;
214     String JavaDoc asymAlgorithm = "RSA";
215     String JavaDoc symAlgorithm = "Blowfish";
216     int asymInit = 512; // initial public/private key length
217
int symInit = 56; // initial shared key length
218

219     // properties for functioning in supplied key mode
220
private boolean suppliedKey = false;
221     private String JavaDoc keyStoreName;
222     private String JavaDoc storePassword ="changeit"; //JDK default
223
private String JavaDoc keyPassword="changeit"; //JDK default
224
private String JavaDoc alias="mykey"; // JDK default
225

226     
227     // public/private Key
228
KeyPair JavaDoc Kpair; // to store own's public/private Key
229

230 // for client to store server's public Key
231
PublicKey JavaDoc serverPubKey = null;
232
233     // needed because we do simultaneous encode/decode with these ciphers - which
234
// would be a threading issue
235
Cipher symEncodingCipher;
236     Cipher symDecodingCipher;
237
238     // version filed for secret key
239
private String JavaDoc symVersion = null;
240     // dhared secret key to encrypt/decrypt messages
241
SecretKey secretKey = null;
242     
243     // map to hold previous keys so we can decrypt some earlier messages if we need to
244
final Map JavaDoc keyMap = new WeakHashMap JavaDoc();
245     // locks for queue access
246
final Object JavaDoc downLock = new Object JavaDoc();
247     final Object JavaDoc upLock = new Object JavaDoc();
248
249     // queues to buffer data while we are swapping shared key
250
// or obtsining key for first time
251

252     private boolean queue_up = true;
253     
254     private boolean queue_down = true;
255     
256     // queue to hold upcoming messages while key negotiation is happening
257
private LinkedQueue upMessageQueue = new LinkedQueue();
258     
259 // queue to hold downcoming messages while key negotiation is happening
260
private LinkedQueue downMessageQueue = new LinkedQueue();
261     // decrypting cypher for secret key requests
262
private Cipher asymCipher;
263     
264     public ENCRYPT()
265     {
266     }
267
268
269     public String JavaDoc getName()
270     {
271         return "ENCRYPT";
272     }
273
274
275     /*
276      * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding"
277      * taken from original ENCRYPT file
278      */

279     private String JavaDoc getAlgorithm(String JavaDoc s)
280     {
281         int index = s.indexOf("/");
282         if (index == -1)
283             return s;
284
285         return s.substring(0, index);
286     }
287
288
289     public boolean setProperties(Properties JavaDoc props)
290     {
291         String JavaDoc str;
292
293         super.setProperties(props);
294         // asymmetric key length
295
str = props.getProperty("asym_init");
296         if (str != null)
297         {
298             asymInit = Integer.parseInt(str);
299             props.remove("asym_init");
300
301             if (log.isInfoEnabled())
302                 log.info("Asym algo bits used is " + asymInit);
303         }
304
305         // symmetric key length
306
str = props.getProperty("sym_init");
307         if (str != null)
308         {
309             symInit = Integer.parseInt(str);
310             props.remove("sym_init");
311
312             if (log.isInfoEnabled())
313                 log.info("Sym algo bits used is " + symInit);
314         }
315
316         // asymmetric algorithm name
317
str = props.getProperty("asym_algorithm");
318         if (str != null)
319         {
320             asymAlgorithm = str;
321             props.remove("asym_algorithm");
322
323             if (log.isInfoEnabled())
324                 log.info("Asym algo used is " + asymAlgorithm);
325         }
326
327         // symmetric algorithm name
328
str = props.getProperty("sym_algorithm");
329         if (str != null)
330         {
331             symAlgorithm = str;
332             props.remove("sym_algorithm");
333
334             if (log.isInfoEnabled())
335                 log.info("Sym algo used is " + symAlgorithm);
336         }
337
338         // symmetric algorithm name
339
str = props.getProperty("asym_provider");
340         if (str != null)
341         {
342             asymProvider = str;
343             props.remove("asym_provider");
344
345             if (log.isInfoEnabled())
346                 log.info("asymProvider used is " + asymProvider);
347         }
348
349         //symmetric algorithm name
350
str = props.getProperty("key_store_name");
351         if (str != null)
352         {
353             keyStoreName = str;
354             props.remove("key_store_name");
355
356             if (log.isInfoEnabled())
357                 log.info("key_store_name used is " + keyStoreName);
358         }
359
360         // symmetric algorithm name
361
str = props.getProperty("store_password");
362         if (str != null)
363         {
364             storePassword = str;
365             props.remove("store_password");
366
367             if (log.isInfoEnabled())
368                 log.info("store_password used is not null");
369         }
370
371         // symmetric algorithm name
372
str = props.getProperty("key_password");
373         if (str != null)
374         {
375             keyPassword = str;
376             props.remove("key_password");
377
378             if (log.isInfoEnabled())
379                 log.info("key_password used is not null");
380         } else if (storePassword != null)
381         {
382             keyPassword = storePassword;
383
384             if (log.isInfoEnabled())
385                 log.info("key_password used is same as store_password");
386         }
387
388         // symmetric algorithm name
389
str = props.getProperty("alias");
390         if (str != null)
391         {
392             alias = str;
393             props.remove("alias");
394
395             if (log.isInfoEnabled())
396                 log.info("alias used is " + alias);
397         }
398
399         if (props.size() > 0)
400         {
401
402             if (log.isErrorEnabled())
403                 log.error("these properties are not recognized:" + props);
404             return false;
405         }
406
407         return true;
408     }
409
410
411     public void init() throws Exception JavaDoc
412     {
413         if (keyStoreName == null)
414         {
415             initSymKey();
416             initKeyPair();
417         } else
418         {
419             initConfiguredKey();
420         }
421         initSymCiphers(symAlgorithm, getSecretKey());
422     }
423
424
425     /**
426      * Initialisation if a supplied key is defined in the properties. This
427      * supplied key must be in a keystore which can be generated using the
428      * keystoreGenerator file in demos. The keystore must be on the classpath
429      * to find it.
430      *
431      * @throws KeyStoreException
432      * @throws Exception
433      * @throws IOException
434      * @throws NoSuchAlgorithmException
435      * @throws CertificateException
436      * @throws UnrecoverableKeyException
437      */

438     private void initConfiguredKey() throws KeyStoreException JavaDoc, Exception JavaDoc,
439             IOException JavaDoc, NoSuchAlgorithmException JavaDoc, CertificateException JavaDoc,
440             UnrecoverableKeyException JavaDoc
441     {
442         InputStream JavaDoc inputStream = null;
443         // must not use default keystore type - as does not support secret keys
444
KeyStore JavaDoc store = KeyStore.getInstance("JCEKS");
445         
446         SecretKey tempKey = null;
447         try
448         {
449             // load in keystore using this thread's classloader
450
inputStream = Thread.currentThread().getContextClassLoader()
451                     .getResourceAsStream(keyStoreName);
452             // we can't find a keystore here -
453
if (inputStream == null)
454             {
455                 throw new Exception JavaDoc("Unable to load keystore " + keyStoreName +
456                         " ensure file is on classpath");
457             }
458             // we have located a file lets load the keystore
459
try{
460             store.load(inputStream, storePassword.toCharArray());
461             // loaded keystore - get the key
462
tempKey = (SecretKey) store
463                     .getKey(alias, keyPassword.toCharArray());
464             } catch (IOException JavaDoc e){
465                 throw new Exception JavaDoc("Unable to load keystore "+ keyStoreName + ": " + e);
466             }catch (NoSuchAlgorithmException JavaDoc e){
467                 throw new Exception JavaDoc("No Such algorithm "+ keyStoreName + ": " + e);
468             }catch(CertificateException JavaDoc e){
469                 throw new Exception JavaDoc("Certificate exception "+ keyStoreName + ": " + e);
470             }
471             
472             if (tempKey == null)
473             {
474                 throw new Exception JavaDoc("Unable to retrieve key '" + alias
475                         + "' from keystore " + keyStoreName);
476             }
477             //set the key here
478
setSecretKey(tempKey);
479             symAlgorithm = tempKey.getAlgorithm();
480             
481             // set the fact we are using a supplied key
482

483             suppliedKey = true;
484             queue_down =false;
485             queue_up =false;
486         } finally
487         {
488             // close the input stream
489
try
490             {
491                 inputStream.close();
492             } catch (Exception JavaDoc e)
493             {
494
495             }
496         }
497
498     }
499
500
501     /**
502      * Used to initialise the symmetric key if none is supplied in a keystore.
503      * @throws Exception
504      */

505     public void initSymKey() throws Exception JavaDoc
506     {
507         KeyGenerator keyGen = null;
508         // see if we have a provider specified
509
if (symProvider != null && symProvider.trim().length() > 0)
510         {
511             keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm),
512                     symProvider);
513         } else
514         {
515             keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm));
516         }
517         // generate the key using the defined init properties
518
keyGen.init(symInit);
519         secretKey = keyGen.generateKey();
520         
521         setSecretKey(secretKey);
522
523         if (log.isInfoEnabled())
524             log.info(" Symmetric key generated ");
525     }
526
527
528     /**
529      * Initialises the Ciphers for both encryption and decryption using the
530      * generated or supplied secret key.
531      *
532      * @param algorithm
533      * @param secret
534      * @throws Exception
535      */

536     private void initSymCiphers(String JavaDoc algorithm, SecretKey secret) throws Exception JavaDoc
537     {
538         
539         if (log.isInfoEnabled())
540             log.info(" Initializing symmetric ciphers");
541         
542         symEncodingCipher = Cipher.getInstance(algorithm);
543         symDecodingCipher = Cipher.getInstance(algorithm);
544         symEncodingCipher.init(Cipher.ENCRYPT_MODE, secret);
545         symDecodingCipher.init(Cipher.DECRYPT_MODE, secret);
546         
547         //set the version
548
MessageDigest JavaDoc digest = MessageDigest.getInstance("MD5");
549         digest.reset();
550         digest.update(secret.getEncoded());
551          
552         symVersion = new String JavaDoc(digest.digest());
553         if (log.isInfoEnabled())
554             log.info(" Initialized symmetric ciphers with secret key version " +symVersion);
555     }
556
557
558     /**
559      * Generates the public/private key pair from the init params
560      * @throws Exception
561      */

562     public void initKeyPair() throws Exception JavaDoc
563     {
564         // generate keys according to the specified algorithms
565
// generate publicKey and Private Key
566
KeyPairGenerator JavaDoc KpairGen = null;
567         if (asymProvider != null && asymProvider.trim().length() > 0)
568         {
569             KpairGen = KeyPairGenerator.getInstance(
570                     getAlgorithm(asymAlgorithm), asymProvider);
571         } else
572         {
573             KpairGen = KeyPairGenerator
574                     .getInstance(getAlgorithm(asymAlgorithm));
575
576         }
577         KpairGen.initialize(asymInit, new SecureRandom JavaDoc());
578         Kpair = KpairGen.generateKeyPair();
579
580         // set up the Cipher to decrypt secret key responses encrypted with our key
581

582         asymCipher = Cipher.getInstance(asymAlgorithm);
583         asymCipher.init(Cipher.DECRYPT_MODE,Kpair.getPrivate());
584
585         if (log.isInfoEnabled())
586             log.info(" asym algo initialized");
587     }
588
589
590     /** Just remove if you don't need to reset any state */
591     public void reset()
592     {
593     }
594
595
596     /* (non-Javadoc)
597      * @see org.jgroups.stack.Protocol#up(org.jgroups.Event)
598      */

599     public void up(Event evt)
600     {
601
602         if (log.isDebugEnabled())
603         {
604             log.debug("Event going up is " + evt);
605         }
606         
607         switch (evt.getType()) {
608
609             // we need to know what our address is
610
case Event.SET_LOCAL_ADDRESS :
611                 if (log.isDebugEnabled())
612                     log.debug("Set local address ");
613                 local_addr = (Address) evt.getArg();
614                 break;
615                 // used to log out whether we are the key server
616
case Event.FIND_INITIAL_MBRS_OK :
617                 if (!suppliedKey){
618                     if (!keyServer)
619                     {
620                         if (log.isInfoEnabled())
621                             log.info("FIND_INIT_MBRS_OK called - I am not the keyserver");
622                     } else
623                     {
624                         if (log.isInfoEnabled())
625                             log.info("FIND_INIT_MBRS_OK called -I am keyserver ");
626                     }
627                 }
628                 break;
629                 // the event used to control the key exchange
630
case Event.VIEW_CHANGE:
631                 if (log.isInfoEnabled())
632                     log.info("handling view change event");
633                 if (!suppliedKey){
634                     handleViewChange(evt);
635                 }
636                 break;
637             // we try and decrypt all messages
638
case Event.MSG :
639                 // if empty just pass up
640
if (evt.getArg() == null || ((Message)evt.getArg()).getBuffer() == null){
641                     if (log.isDebugEnabled())
642                         log.debug("passing up message as is empty buffer ");
643                     break;
644                 }
645                 // try and handle message
646
try
647                 {
648                     
649                         handleUpMessage(evt);
650                     
651                 } catch (Exception JavaDoc e)
652                 {
653                     log.warn("Exception occurred decrypting up message",e);
654                 }
655                 return;
656             default :
657                 break;
658         }
659         
660         if (log.isDebugEnabled())
661             log.debug("passing event up " +evt);
662         passUp(evt);
663         return;
664     }
665
666
667     private synchronized void handleViewChange(Event evt){
668         View view = (View)evt.getArg();
669         
670         // if view is a bit broken set me as keyserver
671
if (view.getMembers() == null || view.getMembers().get(0) == null){
672             becomeKeyServer(local_addr);
673             return;
674         }
675         // otherwise get keyserver from view controller
676
Address tmpKeyServer = (Address)view.getMembers().get(0);
677         
678         //I am new keyserver - either first member of group or old key server is no more and
679
// I have been voted new controller
680
if (tmpKeyServer.equals(local_addr) && (keyServerAddr == null || (! tmpKeyServer.equals(keyServerAddr)))){
681             becomeKeyServer(tmpKeyServer);
682             // a new keyserver has been set and it is not me
683
}else if (keyServerAddr == null || (! tmpKeyServer.equals(keyServerAddr))){
684             handleNewKeyServer(tmpKeyServer);
685         } else{
686             if (log.isDebugEnabled())
687                 log.debug("Membership has changed but I do not care");
688         }
689         
690
691     }
692     
693     /**
694      * Handles becoming server - resetting queue settings
695      * and setting keyserver address to be local address.
696      *
697      * @param tmpKeyServer
698      */

699     private void becomeKeyServer(Address tmpKeyServer){
700         keyServerAddr = tmpKeyServer;
701         keyServer =true;
702         if (log.isInfoEnabled())
703             log.info("I have become key server " + keyServerAddr);
704         queue_down = false;
705         queue_up = false;
706     }
707     
708     /**
709      * Sets up the peer for a new keyserver - this is
710      * setting queueing to buffer messages until we have a new
711      * secret key from the key server and sending a key request
712      * to the new keyserver.
713      *
714      * @param newKeyServer
715      */

716     private void handleNewKeyServer(Address newKeyServer){
717         // start queueing until we have new key
718
// to make sure we are not sending with old key
719
queue_up =true;
720         queue_down = true;
721         // set new keyserver address
722
keyServerAddr = newKeyServer;
723         keyServer =false;
724         if (log.isInfoEnabled())
725             log.info("Sending key request");
726         
727         // create a key request message
728
sendKeyRequest();
729         
730         
731     }
732
733     /**
734      * @param evt
735      */

736     private void handleUpMessage(Event evt) throws Exception JavaDoc
737     {
738         if (log.isDebugEnabled())
739             log.debug("Handling up message " + evt);
740
741         Message msg = (Message) evt.getArg();
742
743         if (msg == null)
744         {
745             if (log.isDebugEnabled())
746                 log.debug("Null message - passing straight up");
747             passUp(evt);
748             return;
749         }
750
751         Object JavaDoc obj = msg.getHeader(EncryptHeader.KEY);
752
753         // try and get the encryption header
754
if (obj == null || !(obj instanceof EncryptHeader))
755         {
756             if (log.isInfoEnabled())
757                 log.info("Dropping message as ENCRYPT header is null or has not been recognized, msg will not be passed up");
758             return;
759         }
760
761         EncryptHeader hdr = (EncryptHeader) obj;
762
763         
764         if (log.isDebugEnabled())
765             log.debug("Header received " + hdr);
766
767         // if a normal message try and decrypt it
768
if (hdr.getType() == EncryptHeader.ENCRYPT)
769         {
770             // if queueing then pass into queue to be dealt with later
771
if (queue_up){
772                 if (log.isDebugEnabled())
773                     log.debug("queueing up message as no session key established");
774                     upMessageQueue.put(evt);
775             }else{
776                 // make sure we pass up any queued messages first
777
// could be more optimised but this can wait
778
// we only need this if not using supplied key
779
if (!suppliedKey) {
780                     drainUpQueue();
781                 }
782                 // try and decrypt the message
783
Message tmpMsg =decryptMessage(symDecodingCipher, msg);
784                 if (tmpMsg != null){
785                     //log.info("normal message passup " + msg);
786
passUp(evt);
787                 } else {
788                     log.warn("Unrecognised cipher discarding message");
789                 }
790             }
791         } else
792         {
793             // check if we had some sort of encrypt control
794
// header if using supplied key we should not
795
// process it
796
if (suppliedKey)
797             {
798                 if (log.isWarnEnabled())
799                 {
800                     log.warn("We received an encrypt header of "
801                             + hdr.getType() + " while in configured mode");
802                 }
803             } else{
804                 // see what sort of encrypt control message we
805
// have received
806
switch (hdr.getType()){
807                     // if a key request
808
case EncryptHeader.KEY_REQUEST:
809                         if (log.isInfoEnabled()) {
810                             log.info("received a key request from peer");
811                         }
812                         
813                         //if a key request send response key back
814
try {
815                             // extract peer's public key
816
PublicKey JavaDoc tmpKey = generatePubKey(msg.getBuffer());
817                             // send back the secret key we have
818
sendSecretKey(getSecretKey(), tmpKey, msg.getSrc());
819                         } catch (Exception JavaDoc e){
820                             log.warn("unable to reconstitute peer's public key");
821                         }
822                         break;
823                     case EncryptHeader.SECRETKEY:
824                         if (log.isInfoEnabled()) {
825                             log.info("received a secretkey response from keyserver");
826                         }
827                         
828                         try {
829                             SecretKey tmp = decodeKey(msg.getBuffer());
830                             if (tmp == null) {
831                                 // unable to understand response
832
// lets try again
833
sendKeyRequest();
834                             }else{
835                                 // otherwise lets set the reurned key
836
// as the shared key
837
setKeys(tmp, hdr.getVersion());
838                                 if (log.isInfoEnabled()) {
839                                     log.info("Decoded secretkey response");
840                                 }
841                             }
842                         } catch (Exception JavaDoc e){
843                             log.warn("unable to process received public key");
844                             log.fatal(e);
845                         }
846                         break;
847                     default:
848                         log.warn("Received ignored encrypt header of "+hdr.getType());
849                         break;
850                 }
851             }
852         }
853     }
854
855
856     /**
857      * used to drain the up queue - synchronized so we
858      * can call it safely despite access from potentially two threads at once
859      * @throws QueueClosedException
860      * @throws Exception
861      */

862     private void drainUpQueue() throws QueueClosedException, Exception JavaDoc
863     {
864         //we do not synchronize here as we only have one up thread so we should never get an issue
865
//synchronized(upLock){
866
Event tmp =null;
867             while ((tmp = (Event)upMessageQueue.poll(0L)) != null){
868                 //if (log.isDebugEnabled()){
869
// log.info("queue draining with message " + tmp);
870
//}
871
if (tmp != null){
872                         Message msg = decryptMessage(symDecodingCipher, (Message)tmp.getArg());
873                 
874                         if (msg != null){
875                             if (log.isDebugEnabled()){
876                                 log.debug("passing up message from drain " + new String JavaDoc(msg.getBuffer()));
877                             }
878                             passUp(tmp);
879                         }else{
880                             log.warn("discarding message in queue up drain as cannot decode it");
881                         }
882                     }
883             }
884         //}
885
}
886
887
888     /**
889      * Sets the keys for the app. and drains the queues - the drains could
890      * be called att he same time as the up/down messages calling in to
891      * the class so we may have an extra call to the drain methods but this slight expense
892      * is better than the alternative of waiting until the next message to
893      * trigger the drains which may never happen.
894      *
895      * @param key
896      * @param version
897      * @throws Exception
898      */

899     private void setKeys(SecretKey key, String JavaDoc version) throws Exception JavaDoc{
900         
901         // put the previous key into the map
902
// if the keys are already there then they will overwrite
903
keyMap.put(getSymVersion(), getSymDecodingCipher());
904         
905         setSecretKey(key);
906         initSymCiphers(key.getAlgorithm(),key );
907         setSymVersion(version);
908
909         // drain the up queue
910
log.info("setting queue up to false in setKeys");
911         queue_up =false;
912         drainUpQueue();
913         
914         queue_down =false;
915         drainDownQueue();
916     }
917     
918     
919     /**
920      * Does the actual work for decrypting - if version does not match current cipher
921      * then tries to use previous cipher
922      * @param cipher
923      * @param msg
924      * @return
925      * @throws Exception
926      */

927     private Message decryptMessage(Cipher cipher, Message msg) throws Exception JavaDoc
928     {
929
930         if (log.isDebugEnabled())
931             log.debug(" Starting to decypher message:"
932                     + formatArray(msg.getBuffer()));
933
934         EncryptHeader hdr = (EncryptHeader)msg.getHeader(EncryptHeader.KEY);
935         
936         if (!hdr.getVersion().equals(getSymVersion())){
937             log.warn("attempting to use stored cipher as message does not uses current encryption version ");
938             Cipher temp = (Cipher)keyMap.get(hdr.getVersion());
939             if (temp == null) {
940                 log.warn("Unable to find a matching cipher in previous key map");
941                 return null;
942             } else{
943                 log.info("Decrypting using previous cipher version "+ hdr.getVersion());
944                 msg.setBuffer(temp.doFinal(msg.getBuffer()));
945                 return msg;
946             }
947         } else {
948         
949             // reset buffer with decrypted message
950
msg.setBuffer(cipher.doFinal(msg.getBuffer()));
951     
952             // log out unencrypted bytes so we can see the
953
// message in readable form
954
if (log.isDebugEnabled())
955                 log.debug(" Decyphered message:" + formatArray(msg.getBuffer()));
956     
957             return msg;
958         }
959     }
960
961
962     /**
963      * @param msg
964      * @param pubKey
965      * @throws InvalidKeyException
966      * @throws IllegalStateException
967      * @throws IllegalBlockSizeException
968      * @throws BadPaddingException
969      */

970     private void sendSecretKey(SecretKey secret, PublicKey JavaDoc pubKey, Address source)
971             throws InvalidKeyException JavaDoc, IllegalStateException JavaDoc,
972             IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException,
973             NoSuchAlgorithmException JavaDoc
974     {
975         Message newMsg;
976
977         if (log.isDebugEnabled())
978             log.debug("encoding shared key ");
979         
980         // create a cipher with peer's public key
981
Cipher tmp = Cipher.getInstance(asymAlgorithm);
982         tmp.init(Cipher.ENCRYPT_MODE, pubKey);
983         
984         //encrypt current secret key
985
byte[] encryptedKey = tmp.doFinal(secret.getEncoded());
986
987         //SW logout encrypted bytes we are sending so we
988
// can match the clients log to see if they match
989
if (log.isDebugEnabled())
990             log.debug(" Generated encoded key which only client can decode:"
991                     + formatArray(encryptedKey));
992
993         newMsg = new Message(source, local_addr, encryptedKey);
994
995         newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(
996                 EncryptHeader.SECRETKEY, getSymVersion()));
997
998         if (log.isDebugEnabled())
999             log.debug(" Sending version " + getSymVersion()
1000                    + " encoded key to client");
1001        passDown(new Event(Event.MSG, newMsg));
1002    }
1003
1004
1005    /**
1006     * @param msg
1007     * @return
1008     */

1009    private PublicKey JavaDoc handleKeyRequest(Message msg)
1010    {
1011        Message newMsg;
1012        if (log.isDebugEnabled())
1013            log.debug("Request for key recieved");
1014
1015        //SW log the clients encoded public key so we can
1016
// see if they match
1017
if (log.isDebugEnabled())
1018            log.debug("Got peer's encoded public key:"
1019                    + formatArray(msg.getBuffer()));
1020
1021        PublicKey JavaDoc pubKey = generatePubKey(msg.getBuffer());
1022
1023        //SW log the clients resulting public key so we can
1024
// see if it is created correctly
1025
if (log.isDebugEnabled())
1026            log.debug("Generated requestors public key" + pubKey);
1027
1028        /*
1029         * SW why do we send this as the client does not use it ? - although we
1030         * could make use to provide some authentication later on rahter than
1031         * just encryption send server's publicKey
1032         */

1033        newMsg = new Message(msg.getSrc(), local_addr, Kpair.getPublic()
1034                .getEncoded());
1035
1036        //SW Log out our public key in encoded format so we
1037
// can match with the client debugging to
1038
// see if they match
1039
if (log.isInfoEnabled())
1040            log.debug("encoded key is "
1041                    + formatArray(Kpair.getPublic().getEncoded()));
1042
1043
1044        newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(
1045                EncryptHeader.SERVER_PUBKEY, getSymVersion()));
1046
1047    
1048        passDown(new Event(Event.MSG, newMsg));
1049        return pubKey;
1050    }
1051
1052
1053    /**
1054     * @return
1055     */

1056    
1057    private Message sendKeyRequest()
1058    {
1059        
1060        // send client's public key to server and request
1061
// server's public key
1062
Message newMsg = new Message(keyServerAddr, local_addr, Kpair.getPublic()
1063                .getEncoded());
1064
1065        newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(
1066                EncryptHeader.KEY_REQUEST, getSymVersion()));
1067        passDown(new Event(Event.MSG, newMsg));
1068        return newMsg;
1069    }
1070
1071
1072    /* (non-Javadoc)
1073     * @see org.jgroups.stack.Protocol#down(org.jgroups.Event)
1074     */

1075    public void down(Event evt)
1076    {
1077
1078        if (log.isDebugEnabled())
1079            log.debug("down:evt is " + evt );
1080
1081        switch (evt.getType()) {
1082
1083            case Event.MSG :
1084                if (evt.getArg() == null || ((Message)evt.getArg()).getBuffer() == null){
1085                    log.debug("passing down message as is empty message or buffer ");
1086                    break;
1087                }
1088                try
1089                {
1090                    
1091                        if (queue_down){
1092                        // queue messages if we are waiting for a new key
1093
downMessageQueue.put(evt);
1094                        }else{
1095                            handleDownEvent(evt);
1096                        }
1097                    
1098                } catch (Exception JavaDoc e)
1099                {
1100                    log.warn("Unable to send down event " + e.getMessage());
1101                }
1102                return;
1103            default :
1104                break;
1105        }
1106        passDown(evt);
1107
1108    }
1109
1110
1111    /**
1112     * handle method for down mesages
1113     * @param evt
1114     */

1115    private void handleDownEvent(Event evt) throws Exception JavaDoc
1116    {
1117        if (log.isDebugEnabled())
1118            log.debug("Handling down message");
1119        // make sure the down queue is drained first to keep ordering
1120
if (!suppliedKey){
1121            drainDownQueue();
1122        }
1123        sendDown(evt);
1124    }
1125
1126
1127    /**
1128     * @throws Exception
1129     * @throws QueueClosedException
1130     */

1131    private void drainDownQueue() throws Exception JavaDoc, QueueClosedException
1132    {
1133// we do not synchronize here as we only have one down thread so we should never get an issue
1134
//synchronized(downLock){
1135
// first lets replay any oustanding events
1136
Event tmp =null;
1137            while((tmp = (Event)downMessageQueue.poll(0L) )!= null){
1138                sendDown(tmp);
1139            }
1140        //}
1141
}
1142
1143
1144    /**
1145     * @param evt
1146     * @throws Exception
1147     */

1148    private void sendDown(Event evt) throws Exception JavaDoc
1149    {
1150        if (evt.getType() != Event.MSG)
1151        {
1152            return;
1153        }
1154        Message msg = (Message) evt.getArg();
1155        
1156        // put our encrypt header on the message
1157
msg.putHeader(EncryptHeader.KEY, new EncryptHeader(
1158                EncryptHeader.ENCRYPT, getSymVersion()));
1159
1160        if (msg.getBuffer() != null)
1161        {
1162            encryptMessage(symEncodingCipher, msg);
1163
1164        } else
1165        {
1166            if (log.isInfoEnabled())
1167                log.info("buffer is null not encrypting ");
1168        }
1169        passDown(evt);
1170    }
1171
1172
1173    /**
1174     * @param symEncodingCipher2
1175     * @param msg
1176     */

1177    private Message encryptMessage(Cipher cipher, Message msg) throws Exception JavaDoc
1178    {
1179        //log out encrypted bytes so we can see the message
1180
// in readable form
1181

1182        if (log.isDebugEnabled())
1183            log.debug(" Starting to encypher message:"
1184                    + formatArray(msg.getBuffer()));
1185
1186        msg.setBuffer(cipher.doFinal(msg.getBuffer()));
1187
1188        //log out unencrypted bytes so we can see the
1189
// message in readable form
1190
if (log.isDebugEnabled())
1191            log.debug(" Encypher message:" + formatArray(msg.getBuffer()));
1192
1193        return msg;
1194
1195    }
1196
1197
1198    private SecretKeySpec decodeKey(byte[] encodedKey) throws Exception JavaDoc
1199    {
1200        // try and decode secrey key sent from keyserver
1201
byte[] keyBytes = asymCipher.doFinal(encodedKey);
1202        
1203        SecretKeySpec keySpec = null;
1204        try
1205        {
1206            keySpec = new SecretKeySpec(keyBytes, getAlgorithm(symAlgorithm));
1207
1208            // test reconstituted key to see if valid
1209
Cipher temp = Cipher.getInstance(symAlgorithm);
1210            temp.init(Cipher.SECRET_KEY, keySpec);
1211        } catch (Exception JavaDoc e)
1212        {
1213            log.fatal(e);
1214            keySpec = null;
1215        }
1216        return keySpec;
1217    }
1218
1219
1220    /**
1221     * used to reconstitute public key sent in byte form from peer
1222     * @param encodedKey
1223     * @return
1224     */

1225    private PublicKey JavaDoc generatePubKey(byte[] encodedKey)
1226    {
1227        PublicKey JavaDoc pubKey = null;
1228        try
1229        {
1230            KeyFactory JavaDoc KeyFac = KeyFactory
1231                    .getInstance(getAlgorithm(asymAlgorithm));
1232            X509EncodedKeySpec JavaDoc x509KeySpec = new X509EncodedKeySpec JavaDoc(encodedKey);
1233            pubKey = KeyFac.generatePublic(x509KeySpec);
1234        } catch (Exception JavaDoc e)
1235        {
1236            e.printStackTrace();
1237        }
1238        return pubKey;
1239    }
1240
1241
1242    /*
1243     * simple helper method so we can see the format of the byte arrays in a
1244     * readable form could be better to use Base64 but will do for now
1245     */

1246    private String JavaDoc formatArray(byte[] array)
1247    {
1248        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1249        for (int i = 0; i < array.length; i++)
1250        {
1251            buf.append(Integer.toHexString(array[i]));
1252        }
1253        return buf.toString();
1254    }
1255
1256
1257    /**
1258     * @return Returns the asymInit.
1259     */

1260    protected int getAsymInit()
1261    {
1262        return asymInit;
1263    }
1264
1265
1266    /**
1267     * @return Returns the asymProvider.
1268     */

1269    protected String JavaDoc getAsymProvider()
1270    {
1271        return asymProvider;
1272    }
1273
1274
1275    /**
1276     * @return Returns the desKey.
1277     */

1278    protected SecretKey getDesKey()
1279    {
1280        return secretKey;
1281    }
1282
1283
1284    /**
1285     * @return Returns the kpair.
1286     */

1287    protected KeyPair JavaDoc getKpair()
1288    {
1289        return Kpair;
1290    }
1291
1292
1293    /**
1294     * @return Returns the asymCipher.
1295     */

1296    protected Cipher getAsymCipher()
1297    {
1298        return asymCipher;
1299    }
1300
1301
1302    /**
1303     * @return Returns the serverPubKey.
1304     */

1305    protected PublicKey JavaDoc getServerPubKey()
1306    {
1307        return serverPubKey;
1308    }
1309
1310
1311    /**
1312     * @return Returns the symAlgorithm.
1313     */

1314    protected String JavaDoc getSymAlgorithm()
1315    {
1316        return symAlgorithm;
1317    }
1318
1319
1320    /**
1321     * @return Returns the symInit.
1322     */

1323    protected int getSymInit()
1324    {
1325        return symInit;
1326    }
1327
1328
1329    /**
1330     * @return Returns the symProvider.
1331     */

1332    protected String JavaDoc getSymProvider()
1333    {
1334        return symProvider;
1335    }
1336
1337
1338    /**
1339     * @return Returns the asymAlgorithm.
1340     */

1341    protected String JavaDoc getAsymAlgorithm()
1342    {
1343        return asymAlgorithm;
1344    }
1345
1346
1347    /**
1348     * @return Returns the symVersion.
1349     */

1350    private String JavaDoc getSymVersion()
1351    {
1352
1353        
1354            return symVersion;
1355        
1356    }
1357
1358
1359    /**
1360     * @param symVersion
1361     * The symVersion to set.
1362     */

1363    private void setSymVersion(String JavaDoc symVersion)
1364    {
1365        
1366            this.symVersion = symVersion;
1367        
1368    }
1369
1370
1371    /**
1372     * @return Returns the secretKey.
1373     */

1374    private SecretKey getSecretKey()
1375    {
1376        
1377            return secretKey;
1378        
1379    }
1380
1381
1382    /**
1383     * @param secretKey
1384     * The secretKey to set.
1385     */

1386    private void setSecretKey(SecretKey secretKey)
1387    {
1388        
1389            this.secretKey = secretKey;
1390        
1391    }
1392
1393
1394    /**
1395     * @param serverPubKey
1396     * The serverPubKey to set.
1397     */

1398    private void setServerPubKey(PublicKey JavaDoc serverPubKey)
1399    {
1400        
1401            this.serverPubKey = serverPubKey;
1402        
1403    }
1404    /**
1405     * @return Returns the keyStoreName.
1406     */

1407    protected String JavaDoc getKeyStoreName()
1408    {
1409        return keyStoreName;
1410    }
1411    /**
1412     * @return Returns the symDecodingCipher.
1413     */

1414    protected Cipher getSymDecodingCipher()
1415    {
1416        return symDecodingCipher;
1417    }
1418    /**
1419     * @return Returns the symEncodingCipher.
1420     */

1421    protected Cipher getSymEncodingCipher()
1422    {
1423        return symEncodingCipher;
1424    }
1425    /**
1426     * @return Returns the local_addr.
1427     */

1428    protected Address getLocal_addr()
1429    {
1430        return local_addr;
1431    }
1432    /**
1433     * @param local_addr The local_addr to set.
1434     */

1435    protected void setLocal_addr(Address local_addr)
1436    {
1437        this.local_addr = local_addr;
1438    }
1439    /**
1440     * @return Returns the keyServerAddr.
1441     */

1442    protected Address getKeyServerAddr()
1443    {
1444        return keyServerAddr;
1445    }
1446    /**
1447     * @param keyServerAddr The keyServerAddr to set.
1448     */

1449    protected void setKeyServerAddr(Address keyServerAddr)
1450    {
1451        this.keyServerAddr = keyServerAddr;
1452    }
1453}
Popular Tags