KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mx4j > tools > remote > PasswordAuthenticator


1 /*
2  * Copyright (C) The MX4J Contributors.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the MX4J License version 1.0.
6  * See the terms of the MX4J License in the documentation provided with this software.
7  */

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

77 public class PasswordAuthenticator implements JMXAuthenticator JavaDoc
78 {
79    private static final String JavaDoc LEFT_DELIMITER = "OBF(";
80    private static final String JavaDoc RIGHT_DELIMITER = "):";
81
82    /**
83     * Runs this class as main class to obfuscate passwords.
84     * When no arguments are provided, it prints out the usage.
85     *
86     * @see #obfuscatePassword(java.lang.String,java.lang.String)
87     */

88    public static void main(String JavaDoc[] args) throws Exception JavaDoc
89    {
90       if (args.length == 1)
91       {
92          if (!"-help".equals(args[0]))
93          {
94             printPassword("MD5", args[0]);
95             return;
96          }
97       }
98       else if (args.length == 3)
99       {
100          if ("-alg".equals(args[0]))
101          {
102             printPassword(args[1], args[2]);
103             return;
104          }
105       }
106       printUsage();
107    }
108
109    private static void printPassword(String JavaDoc algorithm, String JavaDoc input)
110    {
111       String JavaDoc password = obfuscatePassword(input, algorithm);
112       System.out.println(password);
113    }
114
115    private static void printUsage()
116    {
117       System.out.println();
118       System.out.println("Usage: java -cp <lib>/mx4j-tools.jar mx4j.tools.remote.PasswordAuthenticator <options> <password>");
119       System.out.println("Where <options> is one of the following:");
120       System.out.println(" -help Prints this message");
121       System.out.println(" -alg <digest algorithm> Specifies the digest algorithm (default is MD5)");
122       System.out.println();
123    }
124
125    /**
126     * Obfuscates the given password using MD5 as digest algorithm
127     *
128     * @see #obfuscatePassword(java.lang.String,java.lang.String)
129     */

130    public static String JavaDoc obfuscatePassword(String JavaDoc password)
131    {
132       return obfuscatePassword(password, "MD5");
133    }
134
135    /**
136     * Obfuscates the given password using the given digest algorithm.<br/>
137     * Obfuscation consists of 2 steps: first the clear text password is {@link java.security.MessageDigest#digest digested}
138     * using the specified algorithm, then the resulting bytes are Base64-encoded.<br/>
139     * For example, the obfuscated version of the password "password" is "OBF(MD5):X03MO1qnZdYdgyfeuILPmQ=="
140     * or "OBF(SHA-1):W6ph5Mm5Pz8GgiULbPgzG37mj9g=". <br/>
141     * OBF stands for "obfuscated", in parenthesis the algorithm used to digest the password.
142     */

143    public static String JavaDoc obfuscatePassword(String JavaDoc password, String JavaDoc algorithm)
144    {
145       try
146       {
147          MessageDigest JavaDoc digest = MessageDigest.getInstance(algorithm);
148          byte[] digestedBytes = digest.digest(password.getBytes());
149          byte[] obfuscatedBytes = Base64Codec.encodeBase64(digestedBytes);
150          return LEFT_DELIMITER + algorithm + RIGHT_DELIMITER + new String JavaDoc(obfuscatedBytes);
151       }
152       catch (NoSuchAlgorithmException JavaDoc x)
153       {
154          throw new SecurityException JavaDoc("Could not find digest algorithm " + algorithm);
155       }
156    }
157
158    private Map JavaDoc passwords;
159
160    /**
161     * Creates a new PasswordAuthenticator that reads user/password pairs from the specified properties file.
162     * The file format is described in the javadoc of this class.
163     *
164     * @see #obfuscatePassword
165     */

166    public PasswordAuthenticator(File JavaDoc passwordFile) throws IOException JavaDoc
167    {
168       this(new FileInputStream JavaDoc(passwordFile));
169    }
170
171    /**
172     * Creates a new PasswordAuthenticator that reads user/password pairs from the specified InputStream.
173     * The file format is described in the javadoc of this class.
174     *
175     * @see #obfuscatePassword
176     */

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