package net.sourceforge.fenixedu.applicationTier.security;
/*
Based on the work of Jonathan Abbey
Adopted for Director by Thomas Aeby, 2004
MD5Crypt.java
Created: 3 November 1999
Release: $Name: $
Version: $Revision: 1.1 $
Last Mod Date: $Date: 2004/02/04 08:10:35 $
Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu
Original C Version:
----------------------------------------------------------------------------
"THE BEER-WARE LICENSE" (Revision 42):
* This class defines a method,
* {@link MD5Crypt#crypt(java.lang.String, java.lang.String) crypt()}, which
* takes a password and a salt string and generates an
* OpenBSD/FreeBSD/Linux-compatible md5-encoded password entry.
*
* Created: 3 November 1999
*
* Release: $Name: $
*
* Version: $Revision: 1.1 $
*
* Last Mod Date: $Date: 2004/02/04 08:10:35 $
*
* Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu
*
* Original C Version:
*
*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
*
*
* This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible * md5-encoded password hash from a plaintext password and a salt. *
* ** The resulting string will be in the form '$1$<salt>$<hashed * mess> *
* * @param password * Plaintext password * * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password * field. */ static public final String crypt(String password) { StringBuffer salt = new StringBuffer(); java.util.Random randgen = new java.util.Random(); /* -- */ while (salt.length() < 8) { int index = (int) (randgen.nextFloat() * SALTCHARS.length()); salt.append(SALTCHARS.substring(index, index + 1)); } return MD5Crypt.crypt(password, salt.toString()); } /** ** This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible * md5-encoded password hash from a plaintext password and a salt. *
* ** The resulting string will be in the form '$1$<salt>$<hashed * mess> *
* * @param password * Plaintext password * @param salt * A short string to use to randomize md5. May start with * $1$, which will be ignored. It is explicitly permitted * to pass a pre-existing MD5Crypt'ed password entry as * the salt. crypt() will strip the salt chars out * properly. * * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password * field. */ static public final String crypt(String password, String salt) { /* * This string is magic for this algorithm. Having it this way, we can * get get better later on */ String magic = "$1$"; byte finalState[]; MD5 ctx, ctx1; long l; /* -- */ /* Refine the Salt first */ /* If it starts with the magic string, then skip that */ if (salt.startsWith(magic)) { salt = salt.substring(magic.length()); } /* It stops at the first '$', max 8 chars */ if (salt.indexOf('$') != -1) { salt = salt.substring(0, salt.indexOf('$')); } if (salt.length() > 8) { salt = salt.substring(0, 8); } ctx = new MD5(); ctx.update(password.getBytes()); // The password first, since // that is what is most unknown ctx.update(magic.getBytes()); // Then our magic string ctx.update(salt.getBytes()); // Then the raw salt /* Then just as many characters of the MD5(pw,salt,pw) */ ctx1 = new MD5(); ctx1.update(password.getBytes()); ctx1.update(salt.getBytes()); ctx1.update(password.getBytes()); finalState = ctx1.digest(); for (int pl = password.length(); pl > 0; pl -= 16) { for (int i = 0; i < (pl > 16 ? 16 : pl); i++) ctx.update(finalState[i]); } /* * the original code claimed that finalState was being cleared to keep * dangerous bits out of memory, but doing this is also required in * order to get the right output. */ clearbits(finalState); /* Then something really weird... */ for (int i = password.length(); i != 0; i >>>= 1) { if ((i & 1) != 0) { ctx.update(finalState[0]); } else { ctx.update(password.getBytes()[0]); } } finalState = ctx.digest(); /* * and now, just to make sure things don't run too fast On a 60 Mhz * Pentium this takes 34 msec, so you would need 30 seconds to build a * 1000 entry dictionary... * * (The above timings from the C version) */ for (int i = 0; i < 1000; i++) { ctx1 = new MD5(); if ((i & 1) != 0) { ctx1.update(password.getBytes()); } else { for (int c = 0; c < 16; c++) ctx1.update(finalState[c]); } if ((i % 3) != 0) { ctx1.update(salt.getBytes()); } if ((i % 7) != 0) { ctx1.update(password.getBytes()); } if ((i & 1) != 0) { for (int c = 0; c < 16; c++) ctx1.update(finalState[c]); } else { ctx1.update(password.getBytes()); } finalState = ctx1.digest(); } /* Now make the output string */ StringBuffer result = new StringBuffer(); result.append(magic); result.append(salt); result.append("$"); l = (bytes2u(finalState[0]) << 16) | (bytes2u(finalState[6]) << 8) | bytes2u(finalState[12]); result.append(to64(l, 4)); l = (bytes2u(finalState[1]) << 16) | (bytes2u(finalState[7]) << 8) | bytes2u(finalState[13]); result.append(to64(l, 4)); l = (bytes2u(finalState[2]) << 16) | (bytes2u(finalState[8]) << 8) | bytes2u(finalState[14]); result.append(to64(l, 4)); l = (bytes2u(finalState[3]) << 16) | (bytes2u(finalState[9]) << 8) | bytes2u(finalState[15]); result.append(to64(l, 4)); l = (bytes2u(finalState[4]) << 16) | (bytes2u(finalState[10]) << 8) | bytes2u(finalState[5]); result.append(to64(l, 4)); l = bytes2u(finalState[11]); result.append(to64(l, 2)); /* Don't leave anything around in vm they could use. */ clearbits(finalState); return result.toString(); } }