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