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