| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License") + you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.openmeetings.utils.crypt; |
| |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| |
| /* |
| |
| 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): |
| <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 |
| ---------------------------------------------------------------------------- |
| |
| ----------------------------------------------------------------------- |
| |
| Ganymede Directory Management System |
| |
| Copyright (C) 1996, 1997, 1998, 1999 The University of Texas at Austin. |
| |
| Contact information |
| |
| Author Email: ganymede_author@arlut.utexas.edu |
| Email mailing list: ganymede@arlut.utexas.edu |
| |
| US Mail: |
| |
| Computer Science Division |
| Applied Research Laboratories |
| The University of Texas at Austin |
| PO Box 8029, Austin TX 78713-8029 |
| |
| Telephone: (512) 835-3200 |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
| */ |
| |
| /*------------------------------------------------------------------------------ |
| class |
| MD5Crypt |
| |
| ------------------------------------------------------------------------------*/ |
| |
| /** |
| * <p>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.</p> |
| * |
| * <p>Created: 3 November 1999</p> |
| * <p>Release: $Name: $</p> |
| * <p>Version: $Revision: 1.1 $</p> |
| * <p>Last Mod Date: $Date: 2004/02/04 08:10:35 $</p> |
| * <p>Java Code By: Jonathan Abbey, jonabbey@arlut.utexas.edu</p> |
| * <p>Original C Version:<pre> |
| * ---------------------------------------------------------------------------- |
| * "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 |
| * ---------------------------------------------------------------------------- |
| * </pre></p> |
| */ |
| |
| public final class MD5Crypt { |
| |
| /** |
| * |
| * Command line test rig. |
| * @throws NoSuchAlgorithmException |
| * |
| */ |
| |
| static public void main(String argv[]) throws NoSuchAlgorithmException |
| { |
| if ((argv.length < 1) || (argv.length > 2)) |
| { |
| System.err.println("Usage: MD5Crypt password salt"); |
| System.exit(1); |
| } |
| |
| if (argv.length == 2) |
| { |
| System.err.println(MD5Crypt.crypt(argv[0], argv[1])); |
| } |
| else |
| { |
| System.err.println(MD5Crypt.crypt(argv[0])); |
| } |
| |
| System.exit(0); |
| } |
| |
| static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; |
| |
| static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
| |
| static private final String to64(long v, int size) |
| { |
| StringBuffer result = new StringBuffer(); |
| |
| while (--size >= 0) |
| { |
| result.append(itoa64.charAt((int) (v & 0x3f))); |
| v >>>= 6; |
| } |
| |
| return result.toString(); |
| } |
| |
| static private final void clearbits(byte bits[]) |
| { |
| for (int i = 0; i < bits.length; i++) |
| { |
| bits[i] = 0; |
| } |
| } |
| |
| |
| |
| /** |
| * convert an encoded unsigned byte value into a int |
| * with the unsigned value. |
| */ |
| |
| @SuppressWarnings("cast") |
| static private final int bytes2u(byte inp) |
| { |
| return ((int)inp) & 0xff; |
| } |
| |
| /** |
| * <p>This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible |
| * md5-encoded password hash from a plaintext password and a |
| * salt.</p> |
| * |
| * <p>The resulting string will be in the form '$1$<salt>$<hashed mess></p> |
| * |
| * @param password Plaintext password |
| * |
| * @return An OpenBSD/FreeBSD/Linux-compatible md5-hashed password field. |
| * @throws NoSuchAlgorithmException |
| */ |
| |
| static public final String crypt(String password) throws NoSuchAlgorithmException |
| { |
| 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()); |
| } |
| |
| /** |
| * <p>This method actually generates a OpenBSD/FreeBSD/Linux PAM compatible |
| * md5-encoded password hash from a plaintext password and a |
| * salt.</p> |
| * |
| * <p>The resulting string will be in the form '$1$<salt>$<hashed mess></p> |
| * |
| * @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. |
| * @throws NoSuchAlgorithmException |
| */ |
| |
| static public final String crypt(String password, String salt) throws NoSuchAlgorithmException |
| { |
| /* This string is magic for this algorithm. Having it this way, |
| * we can get get better later on */ |
| |
| String magic = "$1$"; |
| byte finalState[]; |
| MessageDigest 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 = MessageDigest.getInstance("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 = MessageDigest.getInstance("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 = MessageDigest.getInstance("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(); |
| } |
| } |