KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > security > plugins > JaasSecurityDomain


1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7
8 package org.jboss.security.plugins;
9
10 import java.io.BufferedReader JavaDoc;
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.InputStream JavaDoc;
14 import java.io.InputStreamReader JavaDoc;
15 import java.lang.reflect.Constructor JavaDoc;
16 import java.lang.reflect.Method JavaDoc;
17 import java.net.MalformedURLException JavaDoc;
18 import java.net.URL JavaDoc;
19 import java.security.KeyStore JavaDoc;
20 import java.util.Arrays JavaDoc;
21 import java.util.StringTokenizer JavaDoc;
22 import javax.crypto.Cipher;
23 import javax.crypto.SecretKey;
24 import javax.crypto.SecretKeyFactory;
25 import javax.crypto.spec.PBEKeySpec;
26 import javax.crypto.spec.PBEParameterSpec;
27 import javax.management.MBeanServer JavaDoc;
28 import javax.management.ObjectName JavaDoc;
29 import javax.net.ssl.KeyManagerFactory;
30 import javax.net.ssl.TrustManagerFactory;
31 import javax.security.auth.callback.CallbackHandler JavaDoc;
32
33 import org.jboss.mx.util.MBeanServerLocator;
34 import org.jboss.security.SecurityDomain;
35 import org.jboss.security.Util;
36 import org.jboss.security.auth.callback.SecurityAssociationHandler;
37
38 /** The JaasSecurityDomain is an extension of JaasSecurityManager that addes
39  the notion of a KeyStore, and JSSE KeyManagerFactory and TrustManagerFactory
40  for supporting SSL and other cryptographic use cases.
41  
42  Attributes:
43  <ul>
44  <li>KeyStoreType: The implementation type name being used, defaults to 'JKS'.
45  </li>
46
47  <li>KeyStoreURL: Set the KeyStore database URL string. This is used to obtain
48  an InputStream to initialize the KeyStore. If the string is not a value
49  URL, its treated as a file.
50  </li>
51  
52  <li>KeyStorePass: the password used to load the KeyStore. Its format is one of:
53  <ul>
54  <li>The plaintext password for the KeyStore(or whatever format is used
55  by the KeyStore). The toCharArray() value of the string is used without any
56  manipulation.
57  </li>
58  <li>A command to execute to obtain the plaintext password. The format
59  is '{EXT}...' where the '...' is the exact command line that will be passed
60  to the Runtime.exec(String) method to execute a platform command. The first
61  line of the command output is used as the password.
62  </li>
63  <li>A class to create to obtain the plaintext password. The format
64  is '{CLASS}classname[:ctorarg]' where the '[:ctorarg]' is an optional
65  string delimited by the ':' from the classname that will be passed to the
66  classname ctor. The password is obtained from classname by invoking a 'char[]
67  toCharArray()' method if found, otherwise, the 'String toString()' method is
68  used.
69  </li>
70  </ul>
71  The KeyStorePass is also used in combination with the Salt and IterationCount
72  attributes to create a PBE secret key used with the encode/decode operations.
73  </li>
74
75  <li>ManagerServiceName: The JMX object name string of the security manager service
76  that the domain registers with to function as a security manager for the
77  security domain name passed to the ctor. The makes the JaasSecurityDomain
78  available under the standard JNDI java:/jaas/(domain) binding.
79  </li>
80
81  <li>LoadSunJSSEProvider: A flag indicating if the Sun com.sun.net.ssl.internal.ssl.Provider
82  security provider should be loaded on startup. This is needed when using
83  the Sun JSSE jars without them installed as an extension with JDK 1.3. This
84  should be set to false with JDK 1.4 or when using an alternate JSSE provider
85  </li>
86
87  <li>Salt:
88  </li>
89  
90  <li>IterationCount:
91  </li>
92  </ul>
93
94  @author Scott.Stark@jboss.org
95  @author <a HREF="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
96
97  @version $Revision: 1.21.2.1 $
98  */

99 public class JaasSecurityDomain
100    extends JaasSecurityManager
101    implements SecurityDomain, JaasSecurityDomainMBean
102 {
103    /** The permission required to access encode, encode64 */
104    private static final RuntimePermission JavaDoc encodePermission =
105       new RuntimePermission JavaDoc("org.jboss.security.plugins.JaasSecurityDomain.encode");
106    /** The permission required to access decode, decode64 */
107    private static final RuntimePermission JavaDoc decodePermission =
108       new RuntimePermission JavaDoc("org.jboss.security.plugins.JaasSecurityDomain.decode");
109
110    /** The KeyStore associated with the security domain */
111    private KeyStore JavaDoc keyStore;
112    private KeyManagerFactory keyMgr;
113    /** The KeyStore implementation type which defaults to 'JKS' */
114    private String JavaDoc keyStoreType = "JKS";
115    /** The resource for the keystore location */
116    private URL JavaDoc keyStoreURL;
117    /** The keystore password for loading */
118    private char[] keyStorePassword;
119    /** A command string to execute to obtain the keyStorePassword */
120    private String JavaDoc keyStorePasswordCmd;
121    /** The type of command string: EXT, CLASS */
122    private String JavaDoc keyStorePasswordCmdType;
123    /** */
124    private SecretKey cipherKey;
125    /** */
126    private String JavaDoc cipherAlgorithm = "PBEwithMD5andDES";
127    private byte[] salt = {1, 2, 3, 4, 5, 6, 7, 8};
128    private int iterationCount = 103;
129    private PBEParameterSpec cipherSpec;
130    /** The JMX object name of the security manager service */
131    private ObjectName JavaDoc managerServiceName = JaasSecurityManagerServiceMBean.OBJECT_NAME;
132
133    private KeyStore JavaDoc trustStore;
134    private String JavaDoc trustStoreType = "JKS";
135    private char[] trustStorePassword;
136    private URL JavaDoc trustStoreURL;
137    private TrustManagerFactory trustMgr;
138
139    /** Creates a default JaasSecurityDomain for with a securityDomain
140     name of 'other'.
141     */

142    public JaasSecurityDomain()
143    {
144       super();
145    }
146
147    /** Creates a JaasSecurityDomain for with a securityDomain
148     name of that given by the 'securityDomain' argument.
149     @param securityDomain , the name of the security domain
150     */

151    public JaasSecurityDomain(String JavaDoc securityDomain)
152    {
153       this(securityDomain, new SecurityAssociationHandler());
154    }
155
156    /** Creates a JaasSecurityDomain for with a securityDomain
157     name of that given by the 'securityDomain' argument.
158     @param securityDomain , the name of the security domain
159     @param handler , the CallbackHandler to use to obtain login module info
160     */

161    public JaasSecurityDomain(String JavaDoc securityDomain, CallbackHandler JavaDoc handler)
162    {
163       super(securityDomain, handler);
164    }
165
166    public KeyStore JavaDoc getKeyStore() throws SecurityException JavaDoc
167    {
168       return keyStore;
169    }
170
171    public KeyManagerFactory getKeyManagerFactory() throws SecurityException JavaDoc
172    {
173       return keyMgr;
174    }
175
176    public KeyStore JavaDoc getTrustStore() throws SecurityException JavaDoc
177    {
178       return trustStore;
179    }
180
181    public TrustManagerFactory getTrustManagerFactory() throws SecurityException JavaDoc
182    {
183       return trustMgr;
184    }
185
186    /** The JMX object name string of the security manager service.
187     @return The JMX object name string of the security manager service.
188     */

189    public ObjectName JavaDoc getManagerServiceName()
190    {
191       return this.managerServiceName;
192    }
193
194    /** Set the JMX object name string of the security manager service.
195     */

196    public void setManagerServiceName(ObjectName JavaDoc managerServiceName)
197    {
198       this.managerServiceName = managerServiceName;
199    }
200
201    public String JavaDoc getKeyStoreType()
202    {
203       return this.keyStoreType;
204    }
205
206    public void setKeyStoreType(String JavaDoc type)
207    {
208       this.keyStoreType = type;
209    }
210
211    public String JavaDoc getKeyStoreURL()
212    {
213       String JavaDoc url = null;
214       if( keyStoreURL != null )
215          url = keyStoreURL.toExternalForm();
216       return url;
217    }
218
219    public void setKeyStoreURL(String JavaDoc storeURL) throws IOException JavaDoc
220    {
221       this.keyStoreURL = this.validateStoreURL(storeURL);
222       log.debug("Using KeyStore=" + keyStoreURL.toExternalForm());
223    }
224
225    public void setKeyStorePass(String JavaDoc password)
226    {
227       this.keyStorePassword = null;
228       // Look for a {...} prefix indicating a password command
229
if( password.charAt(0) == '{' )
230       {
231          StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(password, "{}");
232          this.keyStorePasswordCmdType = tokenizer.nextToken();
233          this.keyStorePasswordCmd = tokenizer.nextToken();
234       }
235       else
236       {
237          // Its just the keystore password string
238
this.keyStorePassword = password.toCharArray();
239       }
240    }
241
242    public String JavaDoc getTrustStoreType()
243    {
244       return this.trustStoreType;
245    }
246
247    public void setTrustStoreType(String JavaDoc type)
248    {
249       this.trustStoreType = type;
250    }
251
252    public void setTrustStorePass(String JavaDoc password)
253    {
254       this.trustStorePassword = password.toCharArray();
255    }
256
257    public String JavaDoc getTrustStoreURL()
258    {
259       String JavaDoc url = null;
260       if( trustStoreURL != null )
261          url = trustStoreURL.toExternalForm();
262       return url;
263    }
264
265    public void setTrustStoreURL(String JavaDoc storeURL) throws IOException JavaDoc
266    {
267       this.trustStoreURL = validateStoreURL(storeURL);
268    }
269
270    public void setSalt(String JavaDoc salt)
271    {
272       this.salt = salt.getBytes();
273    }
274
275    public void setIterationCount(int iterationCount)
276    {
277       this.iterationCount = iterationCount;
278    }
279
280    public String JavaDoc getCipherAlgorithm()
281    {
282       return cipherAlgorithm;
283    }
284
285    public void setCipherAlgorithm(String JavaDoc cipherAlgorithm)
286    {
287       this.cipherAlgorithm = cipherAlgorithm;
288    }
289
290    public String JavaDoc getName()
291    {
292       return "JaasSecurityDomain(" + getSecurityDomain() + ")";
293    }
294
295    /** Encrypt the secret using the cipherKey.
296     * @param secret - the plaintext secret to encrypt
297     * @return the encrypted secret
298     * @throws Exception
299     */

300    public byte[] encode(byte[] secret)
301       throws Exception JavaDoc
302    {
303       SecurityManager JavaDoc sm = System.getSecurityManager();
304       if( sm != null )
305       {
306          System.out.println("Checking: "+encodePermission);
307          sm.checkPermission(encodePermission);
308       }
309
310       Cipher cipher = Cipher.getInstance(cipherAlgorithm);
311       cipher.init(Cipher.ENCRYPT_MODE, cipherKey, cipherSpec);
312       byte[] encoding = cipher.doFinal(secret);
313       return encoding;
314    }
315    /** Decrypt the secret using the cipherKey.
316     *
317     * @param secret - the encrypted secret to decrypt.
318     * @return the decrypted secret
319     * @throws Exception
320     */

321    public byte[] decode(byte[] secret)
322       throws Exception JavaDoc
323    {
324       SecurityManager JavaDoc sm = System.getSecurityManager();
325       if( sm != null )
326          sm.checkPermission(decodePermission);
327
328       Cipher cipher = Cipher.getInstance(cipherAlgorithm);
329       cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherSpec);
330       byte[] decode = cipher.doFinal(secret);
331       return decode;
332    }
333    /** Encrypt the secret using the cipherKey and return a base64 encoding.
334     * @param secret - the plaintext secret to encrypt
335     * @return the encrypted secret as a base64 string
336     * @throws Exception
337     */

338    public String JavaDoc encode64(byte[] secret)
339       throws Exception JavaDoc
340    {
341       byte[] encoding = encode(secret);
342       String JavaDoc b64 = Util.tob64(encoding);
343       return b64;
344    }
345    /** Decrypt the base64 encoded secret using the cipherKey.
346     *
347     * @param secret - the base64 encoded encrypted secret to decrypt.
348     * @return the decrypted secret
349     * @throws Exception
350     */

351    public byte[] decode64(String JavaDoc secret)
352       throws Exception JavaDoc
353    {
354       byte[] encoding = Util.fromb64(secret);
355       byte[] decode = decode(encoding);
356       return decode;
357    }
358
359    /**
360        Reload the key- and truststore
361    */

362    public void reloadKeyAndTrustStore()
363       throws Exception JavaDoc
364    {
365       loadKeyAndTrustStore();
366    }
367
368    protected void startService()
369       throws Exception JavaDoc
370    {
371       // Load the keystore password if it was
372
loadKeystorePassword();
373
374       // Load the key and/or truststore into memory
375
loadKeyAndTrustStore();
376
377       /* Register with the JaasSecurityManagerServiceMBean. This allows this
378        JaasSecurityDomain to function as the security manager for security-domain
379        elements that declare java:/jaas/xxx for our security domain name.
380        */

381       MBeanServer JavaDoc server = MBeanServerLocator.locateJBoss();
382       Object JavaDoc[] params = {getSecurityDomain(), this};
383       String JavaDoc[] signature = new String JavaDoc[]{"java.lang.String", "org.jboss.security.SecurityDomain"};
384       server.invoke(managerServiceName, "registerSecurityDomain", params, signature);
385    }
386
387    protected void stopService()
388    {
389       if( keyStorePassword != null )
390       {
391          Arrays.fill(keyStorePassword, '\0');
392          keyStorePassword = null;
393       }
394       cipherKey = null;
395    }
396
397    /** If keyStorePassword is null and keyStorePasswordCmd exists,
398     * execute it to obtain the password.
399     */

400    private void loadKeystorePassword()
401       throws Exception JavaDoc
402    {
403       if( keyStorePassword == null )
404       {
405          if( keyStorePasswordCmdType.equals("EXT") )
406             execPasswordCmd();
407          else if( keyStorePasswordCmdType.equals("CLASS") )
408             invokePasswordClass();
409          else
410             throw new IllegalArgumentException JavaDoc("Unknown keyStorePasswordCmdType: "+keyStorePasswordCmdType);
411       }
412
413       // Create the PBE secret key
414
cipherSpec = new PBEParameterSpec(salt, iterationCount);
415       PBEKeySpec keySpec = new PBEKeySpec(keyStorePassword);
416       SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithMD5andDES");
417       cipherKey = factory.generateSecret(keySpec);
418    }
419    
420    private void loadKeyAndTrustStore()
421       throws Exception JavaDoc
422    {
423       if( keyStoreURL != null )
424       {
425          keyStore = KeyStore.getInstance(keyStoreType);
426          InputStream JavaDoc is = keyStoreURL.openStream();
427          keyStore.load(is, keyStorePassword);
428          String JavaDoc algorithm = KeyManagerFactory.getDefaultAlgorithm();
429          keyMgr = KeyManagerFactory.getInstance(algorithm);
430          keyMgr.init(keyStore, keyStorePassword);
431       }
432       if( trustStoreURL != null )
433       {
434          trustStore = KeyStore.getInstance(trustStoreType);
435          InputStream JavaDoc is = trustStoreURL.openStream();
436          trustStore.load(is, trustStorePassword);
437          String JavaDoc algorithm = TrustManagerFactory.getDefaultAlgorithm();
438          trustMgr = TrustManagerFactory.getInstance(algorithm);
439          trustMgr.init(trustStore);
440       }
441       else if( keyStore != null )
442       {
443          trustStore = keyStore;
444          String JavaDoc algorithm = TrustManagerFactory.getDefaultAlgorithm();
445          trustMgr = TrustManagerFactory.getInstance(algorithm);
446          trustMgr.init(trustStore);
447       }
448    }
449
450    private void execPasswordCmd()
451       throws Exception JavaDoc
452    {
453       log.debug("Executing command: "+keyStorePasswordCmd);
454       Runtime JavaDoc rt = Runtime.getRuntime();
455       Process JavaDoc p = rt.exec(keyStorePasswordCmd);
456       InputStream JavaDoc stdin = p.getInputStream();
457       BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(stdin));
458       String JavaDoc password = reader.readLine();
459       stdin.close();
460       int exitCode = p.waitFor();
461       log.debug("Command exited with: "+exitCode);
462       keyStorePassword = password.toCharArray();
463    }
464    /**
465     *
466     * @throws Exception
467     */

468    private void invokePasswordClass()
469       throws Exception JavaDoc
470    {
471       keyStorePassword = null;
472
473       // Check for a ctor argument delimited by ':'
474
String JavaDoc classname = keyStorePasswordCmd;
475       String JavaDoc ctorArg = null;
476       int colon = keyStorePasswordCmd.indexOf(':');
477       if( colon > 0 )
478       {
479          classname = keyStorePasswordCmd.substring(0, colon);
480          ctorArg = keyStorePasswordCmd.substring(colon+1);
481       }
482       log.debug("Loading class: "+classname+", ctorArg="+ctorArg);
483       ClassLoader JavaDoc loader = SubjectActions.getContextClassLoader();
484       Class JavaDoc c = loader.loadClass(classname);
485       Object JavaDoc instance = null;
486       // Check for a ctor(String) if ctorArg is not null
487
if( ctorArg != null )
488       {
489          Class JavaDoc[] sig = {String JavaDoc.class};
490          Constructor JavaDoc ctor = c.getConstructor(sig);
491          Object JavaDoc[] args = {ctorArg};
492          instance = ctor.newInstance(args);
493       }
494       else
495       {
496          // Use the default ctor
497
instance = c.newInstance();
498       }
499
500       // Look for a toCharArray() method
501
try
502       {
503          log.debug("Checking for toCharArray");
504          Class JavaDoc[] sig = {};
505          Method JavaDoc toCharArray = c.getMethod("toCharArray", sig);
506          Object JavaDoc[] args = {};
507          log.debug("Invoking toCharArray");
508          keyStorePassword = (char[]) toCharArray.invoke(instance, args);
509       }
510       catch(NoSuchMethodException JavaDoc e)
511       {
512          log.debug("No toCharArray found, invoking toString");
513          String JavaDoc tmp = instance.toString();
514          if( tmp != null )
515             keyStorePassword = tmp.toCharArray();
516       }
517    }
518
519    private URL JavaDoc validateStoreURL(String JavaDoc storeURL) throws IOException JavaDoc
520    {
521       URL JavaDoc url = null;
522       // First see if this is a URL
523
try
524       {
525          url = new URL JavaDoc(storeURL);
526       }
527       catch(MalformedURLException JavaDoc e)
528       {
529          // Not a URL or a protocol without a handler
530
}
531
532       // Next try to locate this as file path
533
if( url == null )
534       {
535          File JavaDoc tst = new File JavaDoc(storeURL);
536          if( tst.exists() == true )
537             url = tst.toURL();
538       }
539
540       // Last try to locate this as a classpath resource
541
if( url == null )
542       {
543          ClassLoader JavaDoc loader = SubjectActions.getContextClassLoader();
544          url = loader.getResource(storeURL);
545       }
546
547       // Fail if no valid key store was located
548
if( url == null )
549       {
550          String JavaDoc msg = "Failed to find url=" + storeURL + " as a URL, file or resource";
551          throw new MalformedURLException JavaDoc(msg);
552       }
553       return url;
554    }
555 }
556
Popular Tags