1 // ========================================================================
2 // Copyright (c) 1998-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.util.security;
15
16 import java.io.IOException;
17 import java.util.Arrays;
18
19 import org.eclipse.jetty.util.log.Log;
20 import org.eclipse.jetty.util.log.Logger;
21
22 /* ------------------------------------------------------------ */
23 /**
24 * Password utility class.
25 *
26 * This utility class gets a password or pass phrase either by:
27 *
28 * <PRE>
29 * + Password is set as a system property.
30 * + The password is prompted for and read from standard input
31 * + A program is run to get the password.
32 * </pre>
33 *
34 * Passwords that begin with OBF: are de obfuscated. Passwords can be obfuscated
35 * by run org.eclipse.util.Password as a main class. Obfuscated password are
36 * required if a system needs to recover the full password (eg. so that it may
37 * be passed to another system). They are not secure, but prevent casual
38 * observation.
39 * <p>
40 * Passwords that begin with CRYPT: are oneway encrypted with UnixCrypt. The
41 * real password cannot be retrieved, but comparisons can be made to other
42 * passwords. A Crypt can be generated by running org.eclipse.util.UnixCrypt as
43 * a main class, passing password and then the username. Checksum passwords are
44 * a secure(ish) way to store passwords that only need to be checked rather than
45 * recovered. Note that it is not strong security - specially if simple
46 * passwords are used.
47 *
48 *
49 */
50 public class Password extends Credential
51 {
52 private static final Logger LOG = Log.getLogger(Password.class);
53
54 private static final long serialVersionUID = 5062906681431569445L;
55
56 public static final String __OBFUSCATE = "OBF:";
57
58 private String _pw;
59
60 /* ------------------------------------------------------------ */
61 /**
62 * Constructor.
63 *
64 * @param password The String password.
65 */
66 public Password(String password)
67 {
68 _pw = password;
69
70 // expand password
71 while (_pw != null && _pw.startsWith(__OBFUSCATE))
72 _pw = deobfuscate(_pw);
73 }
74
75 /* ------------------------------------------------------------ */
76 @Override
77 public String toString()
78 {
79 return _pw;
80 }
81
82 /* ------------------------------------------------------------ */
83 public String toStarString()
84 {
85 return "*****************************************************".substring(0, _pw.length());
86 }
87
88 /* ------------------------------------------------------------ */
89 @Override
90 public boolean check(Object credentials)
91 {
92 if (this == credentials) return true;
93
94 if (credentials instanceof Password) return credentials.equals(_pw);
95
96 if (credentials instanceof String) return credentials.equals(_pw);
97
98 if (credentials instanceof char[]) return Arrays.equals(_pw.toCharArray(), (char[]) credentials);
99
100 if (credentials instanceof Credential) return ((Credential) credentials).check(_pw);
101
102 return false;
103 }
104
105 /* ------------------------------------------------------------ */
106 @Override
107 public boolean equals(Object o)
108 {
109 if (this == o)
110 return true;
111
112 if (null == o)
113 return false;
114
115 if (o instanceof Password)
116 {
117 Password p = (Password) o;
118 //noinspection StringEquality
119 return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
120 }
121
122 if (o instanceof String)
123 return o.equals(_pw);
124
125 return false;
126 }
127
128 /* ------------------------------------------------------------ */
129 @Override
130 public int hashCode()
131 {
132 return null == _pw ? super.hashCode() : _pw.hashCode();
133 }
134
135 /* ------------------------------------------------------------ */
136 public static String obfuscate(String s)
137 {
138 StringBuilder buf = new StringBuilder();
139 byte[] b = s.getBytes();
140
141 buf.append(__OBFUSCATE);
142 for (int i = 0; i < b.length; i++)
143 {
144 byte b1 = b[i];
145 byte b2 = b[s.length() - (i + 1)];
146 int i1 = 127 + b1 + b2;
147 int i2 = 127 + b1 - b2;
148 int i0 = i1 * 256 + i2;
149 String x = Integer.toString(i0, 36);
150
151 switch (x.length())
152 {
153 case 1:
154 buf.append('0');
155 buf.append('0');
156 buf.append('0');
157 buf.append(x);
158 break;
159 case 2:
160 buf.append('0');
161 buf.append('0');
162 buf.append(x);
163 break;
164 case 3:
165 buf.append('0');
166 buf.append(x);
167 break;
168 default:
169 buf.append(x);
170 break;
171 }
172 }
173 return buf.toString();
174
175 }
176
177 /* ------------------------------------------------------------ */
178 public static String deobfuscate(String s)
179 {
180 if (s.startsWith(__OBFUSCATE)) s = s.substring(4);
181
182 byte[] b = new byte[s.length() / 2];
183 int l = 0;
184 for (int i = 0; i < s.length(); i += 4)
185 {
186 String x = s.substring(i, i + 4);
187 int i0 = Integer.parseInt(x, 36);
188 int i1 = (i0 / 256);
189 int i2 = (i0 % 256);
190 b[l++] = (byte) ((i1 + i2 - 254) / 2);
191 }
192
193 return new String(b, 0, l);
194 }
195
196 /* ------------------------------------------------------------ */
197 /**
198 * Get a password. A password is obtained by trying
199 * <UL>
200 * <LI>Calling <Code>System.getProperty(realm,dft)</Code>
201 * <LI>Prompting for a password
202 * <LI>Using promptDft if nothing was entered.
203 * </UL>
204 *
205 * @param realm The realm name for the password, used as a SystemProperty
206 * name.
207 * @param dft The default password.
208 * @param promptDft The default to use if prompting for the password.
209 * @return Password
210 */
211 public static Password getPassword(String realm, String dft, String promptDft)
212 {
213 String passwd = System.getProperty(realm, dft);
214 if (passwd == null || passwd.length() == 0)
215 {
216 try
217 {
218 System.out.print(realm + ((promptDft != null && promptDft.length() > 0) ? " [dft]" : "") + " : ");
219 System.out.flush();
220 byte[] buf = new byte[512];
221 int len = System.in.read(buf);
222 if (len > 0) passwd = new String(buf, 0, len).trim();
223 }
224 catch (IOException e)
225 {
226 LOG.warn(Log.EXCEPTION, e);
227 }
228 if (passwd == null || passwd.length() == 0) passwd = promptDft;
229 }
230 return new Password(passwd);
231 }
232
233 /* ------------------------------------------------------------ */
234 /**
235 * @param arg
236 */
237 public static void main(String[] arg)
238 {
239 if (arg.length != 1 && arg.length != 2)
240 {
241 System.err.println("Usage - java org.eclipse.jetty.security.Password [<user>] <password>");
242 System.err.println("If the password is ?, the user will be prompted for the password");
243 System.exit(1);
244 }
245 String p = arg[arg.length == 1 ? 0 : 1];
246 Password pw = new Password(p);
247 System.err.println(pw.toString());
248 System.err.println(obfuscate(pw.toString()));
249 System.err.println(Credential.MD5.digest(p));
250 if (arg.length == 2) System.err.println(Credential.Crypt.crypt(arg[0], pw.toString()));
251 }
252 }