// ======================================================================== // Copyright (c) 1998-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.util.security; import java.io.IOException; import java.util.Arrays; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** * Password utility class. * * This utility class gets a password or pass phrase either by: * * <PRE> * + Password is set as a system property. * + The password is prompted for and read from standard input * + A program is run to get the password. * </pre> * * Passwords that begin with OBF: are de obfuscated. Passwords can be obfuscated * by run org.eclipse.util.Password as a main class. Obfuscated password are * required if a system needs to recover the full password (eg. so that it may * be passed to another system). They are not secure, but prevent casual * observation. * <p> * Passwords that begin with CRYPT: are oneway encrypted with UnixCrypt. The * real password cannot be retrieved, but comparisons can be made to other * passwords. A Crypt can be generated by running org.eclipse.util.UnixCrypt as * a main class, passing password and then the username. Checksum passwords are * a secure(ish) way to store passwords that only need to be checked rather than * recovered. Note that it is not strong security - specially if simple * passwords are used. * * */ public class Password extends Credential { private static final Logger LOG = Log.getLogger(Password.class); private static final long serialVersionUID = 5062906681431569445L; public static final String __OBFUSCATE = "OBF:"; private String _pw; /* ------------------------------------------------------------ */ /** * Constructor. * * @param password The String password. */ public Password(String password) { _pw = password; // expand password while (_pw != null && _pw.startsWith(__OBFUSCATE)) _pw = deobfuscate(_pw); } /* ------------------------------------------------------------ */ @Override public String toString() { return _pw; } /* ------------------------------------------------------------ */ public String toStarString() { return "*****************************************************".substring(0, _pw.length()); } /* ------------------------------------------------------------ */ @Override public boolean check(Object credentials) { if (this == credentials) return true; if (credentials instanceof Password) return credentials.equals(_pw); if (credentials instanceof String) return credentials.equals(_pw); if (credentials instanceof char[]) return Arrays.equals(_pw.toCharArray(), (char[]) credentials); if (credentials instanceof Credential) return ((Credential) credentials).check(_pw); return false; } /* ------------------------------------------------------------ */ @Override public boolean equals(Object o) { if (this == o) return true; if (null == o) return false; if (o instanceof Password) { Password p = (Password) o; //noinspection StringEquality return p._pw == _pw || (null != _pw && _pw.equals(p._pw)); } if (o instanceof String) return o.equals(_pw); return false; } /* ------------------------------------------------------------ */ @Override public int hashCode() { return null == _pw ? super.hashCode() : _pw.hashCode(); } /* ------------------------------------------------------------ */ public static String obfuscate(String s) { StringBuilder buf = new StringBuilder(); byte[] b = s.getBytes(); buf.append(__OBFUSCATE); for (int i = 0; i < b.length; i++) { byte b1 = b[i]; byte b2 = b[s.length() - (i + 1)]; int i1 = 127 + b1 + b2; int i2 = 127 + b1 - b2; int i0 = i1 * 256 + i2; String x = Integer.toString(i0, 36); switch (x.length()) { case 1: buf.append('0'); buf.append('0'); buf.append('0'); buf.append(x); break; case 2: buf.append('0'); buf.append('0'); buf.append(x); break; case 3: buf.append('0'); buf.append(x); break; default: buf.append(x); break; } } return buf.toString(); } /* ------------------------------------------------------------ */ public static String deobfuscate(String s) { if (s.startsWith(__OBFUSCATE)) s = s.substring(4); byte[] b = new byte[s.length() / 2]; int l = 0; for (int i = 0; i < s.length(); i += 4) { String x = s.substring(i, i + 4); int i0 = Integer.parseInt(x, 36); int i1 = (i0 / 256); int i2 = (i0 % 256); b[l++] = (byte) ((i1 + i2 - 254) / 2); } return new String(b, 0, l); } /* ------------------------------------------------------------ */ /** * Get a password. A password is obtained by trying * <UL> * <LI>Calling <Code>System.getProperty(realm,dft)</Code> * <LI>Prompting for a password * <LI>Using promptDft if nothing was entered. * </UL> * * @param realm The realm name for the password, used as a SystemProperty * name. * @param dft The default password. * @param promptDft The default to use if prompting for the password. * @return Password */ public static Password getPassword(String realm, String dft, String promptDft) { String passwd = System.getProperty(realm, dft); if (passwd == null || passwd.length() == 0) { try { System.out.print(realm + ((promptDft != null && promptDft.length() > 0) ? " [dft]" : "") + " : "); System.out.flush(); byte[] buf = new byte[512]; int len = System.in.read(buf); if (len > 0) passwd = new String(buf, 0, len).trim(); } catch (IOException e) { LOG.warn(Log.EXCEPTION, e); } if (passwd == null || passwd.length() == 0) passwd = promptDft; } return new Password(passwd); } /* ------------------------------------------------------------ */ /** * @param arg */ public static void main(String[] arg) { if (arg.length != 1 && arg.length != 2) { System.err.println("Usage - java org.eclipse.jetty.security.Password [<user>] <password>"); System.err.println("If the password is ?, the user will be prompted for the password"); System.exit(1); } String p = arg[arg.length == 1 ? 0 : 1]; Password pw = new Password(p); System.err.println(pw.toString()); System.err.println(obfuscate(pw.toString())); System.err.println(Credential.MD5.digest(p)); if (arg.length == 2) System.err.println(Credential.Crypt.crypt(arg[0], pw.toString())); } }