View Javadoc

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