KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > servicemix > jbi > jmx > PasswordAuthenticator


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.servicemix.jbi.jmx;
18
19 /*
20  * Copyright (C) The MX4J Contributors.
21  * All rights reserved.
22  *
23  * This software is distributed under the terms of the MX4J License version 1.0.
24  * See the terms of the MX4J License in the documentation provided with this software.
25  */

26
27 import java.io.File JavaDoc;
28 import java.io.FileInputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.security.MessageDigest JavaDoc;
32 import java.security.NoSuchAlgorithmException JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.HashSet JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.Properties JavaDoc;
38 import java.util.Set JavaDoc;
39 import javax.management.remote.JMXAuthenticator JavaDoc;
40 import javax.management.remote.JMXPrincipal JavaDoc;
41 import javax.security.auth.Subject JavaDoc;
42
43 import mx4j.util.Base64Codec;
44
45 /**
46  * Implementation of the JMXAuthenticator interface to be used on server side
47  * to secure access to {@link javax.management.remote.JMXConnectorServer JMXConnectorServer}s. <br/>
48  * Usage:
49  * <pre>
50  * JMXAuthenticator authenticator = new PasswordAuthenticator(new File("users.properties"));
51  * Map environment = new HashMap();
52  * environment.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
53  * JMXServiceURL address = new JMXServiceURL("rmi", "localhost", 0);
54  * MBeanServer server = ...;
55  * JMXConnectorServer cntorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, environment, server);
56  * </pre>
57  * The format of the users.properties file is that of a standard properties file: <br/>
58  * &lt;user&gt;=&lt;password&gt;<br/>
59  * where &lt;password&gt; can be stored in 2 ways:
60  * <ul>
61  * <li>Clear text: the password is written in clear text</li>
62  * <li>Obfuscated text: the password is obfuscated</li>
63  * </ul>
64  * The obfuscated form can be obtained running this class as a main class:
65  * <pre>
66  * java -cp mx4j-remote.jar mx4j.tools.remote.PasswordAuthenticator
67  * </pre>
68  * and following the instructions printed on the console. The output will be a string that should be
69  * copy/pasted as the password into the properties file.<br/>
70  * The obfuscated password is obtained by digesting the clear text password using a
71  * {@link java.security.MessageDigest} algorithm, and then by Base64-encoding the resulting bytes.<br/>
72  * <br/>
73  * On client side, you are allowed to connect to a server side secured with the PasswordAuthenticator
74  * only if you provide the correct credentials:
75  * <pre>
76  * String[] credentials = new String[2];
77  * // The user will travel as clear text
78  * credentials[0] = "user";
79  * // You may send the password in clear text, but it's better to obfuscate it
80  * credentials[1] = PasswordAuthenticator.obfuscatePassword("password");
81  * Map environment = new HashMap();
82  * environment.put(JMXConnector.CREDENTIALS, credentials);
83  * JMXServiceURL address = ...;
84  * JMXConnector cntor = JMXConnectorFactory.connect(address, environment);
85  * </pre>
86  * Note that {@link #obfuscatePassword(java.lang.String,java.lang.String) obfuscating} the passwords only works if the server side has been
87  * setup with the PasswordAuthenticator.
88  * However, the PasswordAuthenticator can be used with other JSR 160 implementations, such as Sun's reference
89  * implementation.
90  *
91  * @version $Revision: 1.3 $
92  */

93 public class PasswordAuthenticator implements JMXAuthenticator JavaDoc
94 {
95    private static final String JavaDoc LEFT_DELIMITER = "OBF(";
96    private static final String JavaDoc RIGHT_DELIMITER = "):";
97
98    /**
99     * Runs this class as main class to obfuscate passwords.
100     * When no arguments are provided, it prints out the usage.
101     *
102     * @see #obfuscatePassword(java.lang.String,java.lang.String)
103     */

104    public static void main(String JavaDoc[] args) throws Exception JavaDoc
105    {
106       if (args.length == 1)
107       {
108          if (!"-help".equals(args[0]))
109          {
110             printPassword("MD5", args[0]);
111             return;
112          }
113       }
114       else if (args.length == 3)
115       {
116          if ("-alg".equals(args[0]))
117          {
118             printPassword(args[1], args[2]);
119             return;
120          }
121       }
122       printUsage();
123    }
124
125    private static void printPassword(String JavaDoc algorithm, String JavaDoc input)
126    {
127       String JavaDoc password = obfuscatePassword(input, algorithm);
128       System.out.println(password);
129    }
130
131    private static void printUsage()
132    {
133       System.out.println();
134       System.out.println("Usage: java -cp <lib>/mx4j-tools.jar mx4j.tools.remote.PasswordAuthenticator <options> <password>");
135       System.out.println("Where <options> is one of the following:");
136       System.out.println(" -help Prints this message");
137       System.out.println(" -alg <digest algorithm> Specifies the digest algorithm (default is MD5)");
138       System.out.println();
139    }
140
141    /**
142     * Obfuscates the given password using MD5 as digest algorithm
143     *
144     * @see #obfuscatePassword(java.lang.String,java.lang.String)
145     */

146    public static String JavaDoc obfuscatePassword(String JavaDoc password)
147    {
148       return obfuscatePassword(password, "MD5");
149    }
150
151    /**
152     * Obfuscates the given password using the given digest algorithm.<br/>
153     * Obfuscation consists of 2 steps: first the clear text password is {@link java.security.MessageDigest#digest digested}
154     * using the specified algorithm, then the resulting bytes are Base64-encoded.<br/>
155     * For example, the obfuscated version of the password "password" is "OBF(MD5):X03MO1qnZdYdgyfeuILPmQ=="
156     * or "OBF(SHA-1):W6ph5Mm5Pz8GgiULbPgzG37mj9g=". <br/>
157     * OBF stands for "obfuscated", in parenthesis the algorithm used to digest the password.
158     */

159    public static String JavaDoc obfuscatePassword(String JavaDoc password, String JavaDoc algorithm)
160    {
161       try
162       {
163          MessageDigest JavaDoc digest = MessageDigest.getInstance(algorithm);
164          byte[] digestedBytes = digest.digest(password.getBytes());
165          byte[] obfuscatedBytes = Base64Codec.encodeBase64(digestedBytes);
166          return LEFT_DELIMITER + algorithm + RIGHT_DELIMITER + new String JavaDoc(obfuscatedBytes);
167       }
168       catch (NoSuchAlgorithmException JavaDoc x)
169       {
170          throw new SecurityException JavaDoc("Could not find digest algorithm " + algorithm);
171       }
172    }
173
174    private Map JavaDoc passwords;
175
176    /**
177     * Creates a new PasswordAuthenticator that reads user/password pairs from the specified properties file.
178     * The file format is described in the javadoc of this class.
179     *
180     * @see #obfuscatePassword
181     */

182    public PasswordAuthenticator(File JavaDoc passwordFile) throws IOException JavaDoc
183    {
184       this(new FileInputStream JavaDoc(passwordFile));
185    }
186
187    /**
188     * Creates a new PasswordAuthenticator that reads user/password pairs from the specified InputStream.
189     * The file format is described in the javadoc of this class.
190     *
191     * @see #obfuscatePassword
192     */

193    public PasswordAuthenticator(InputStream JavaDoc is) throws IOException JavaDoc
194    {
195       passwords = readPasswords(is);
196    }
197
198    private Map JavaDoc readPasswords(InputStream JavaDoc is) throws IOException JavaDoc
199    {
200       Properties JavaDoc properties = new Properties JavaDoc();
201       try
202       {
203          properties.load(is);
204       }
205       finally
206       {
207          is.close();
208       }
209       return new HashMap JavaDoc(properties);
210    }
211
212    public Subject JavaDoc authenticate(Object JavaDoc credentials) throws SecurityException JavaDoc
213    {
214       if (!(credentials instanceof String JavaDoc[])) throw new SecurityException JavaDoc("Bad credentials");
215       String JavaDoc[] creds = (String JavaDoc[])credentials;
216       if (creds.length != 2) throw new SecurityException JavaDoc("Bad credentials");
217
218       String JavaDoc user = creds[0];
219       String JavaDoc password = creds[1];
220
221       if (password == null) throw new SecurityException JavaDoc("Bad password");
222
223       if (!passwords.containsKey(user)) throw new SecurityException JavaDoc("Unknown user " + user);
224
225       String JavaDoc storedPassword = (String JavaDoc)passwords.get(user);
226       if (!isPasswordCorrect(password, storedPassword)) throw new SecurityException JavaDoc("Bad password");
227
228       Set JavaDoc principals = new HashSet JavaDoc();
229       principals.add(new JMXPrincipal JavaDoc(user));
230       return new Subject JavaDoc(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET);
231    }
232
233    private boolean isPasswordCorrect(String JavaDoc password, String JavaDoc storedPassword)
234    {
235       if (password.startsWith(LEFT_DELIMITER))
236       {
237          if (storedPassword.startsWith(LEFT_DELIMITER))
238          {
239             return password.equals(storedPassword);
240          }
241          else
242          {
243             String JavaDoc algorithm = getAlgorithm(password);
244             String JavaDoc obfuscated = obfuscatePassword(storedPassword, algorithm);
245             return password.equals(obfuscated);
246          }
247       }
248       else
249       {
250          if (storedPassword.startsWith(LEFT_DELIMITER))
251          {
252             // Password was sent in clear, bad practice
253
String JavaDoc algorithm = getAlgorithm(storedPassword);
254             String JavaDoc obfuscated = obfuscatePassword(password, algorithm);
255             return obfuscated.equals(storedPassword);
256          }
257          else
258          {
259             return password.equals(storedPassword);
260          }
261       }
262    }
263
264    private String JavaDoc getAlgorithm(String JavaDoc obfuscatedPassword)
265    {
266       try
267       {
268          return obfuscatedPassword.substring(LEFT_DELIMITER.length(), obfuscatedPassword.indexOf(RIGHT_DELIMITER));
269       }
270       catch (IndexOutOfBoundsException JavaDoc x)
271       {
272          throw new SecurityException JavaDoc("Bad password");
273       }
274    }
275 }
276
Popular Tags