/** * Copyright (c) 2008-2011 Sonatype, Inc. * All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions. * * This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General * Public License Version 3 as published by the Free Software Foundation. * * 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 Affero General Public License Version 3 * for more details. * * You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see * http://www.gnu.org/licenses. * * Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of * Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation. * All other trademarks are the property of their respective owners. */ package org.sonatype.security.ldap.dao.password.hash; import java.util.Random; /** * Unix MD5 encryption, used for /etc/passwd style passwords. * * @author Kenney Westerhof */ public class MD5Crypt { private static final String SALTCHARS = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; private static final String ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; private MD5Crypt() { // prevent instantiation } private static int bytes2u( byte in ) { return (int) in & 0xff; } private static String to64( long v, int size ) { String result = ""; while ( --size >= 0 ) { result += ITOA64.charAt( (int) ( v & 0x3f ) ); v >>>= 6; } return result; } public static String unixMD5( final String s ) { Random r = new Random(); String salt = ""; while ( salt.length() < 8 ) { salt += SALTCHARS.charAt( (int) ( r.nextFloat() * SALTCHARS.length() ) ); } return unixMD5( s, salt ); } public static String unixMD5( String s, String salt ) { return unixMD5( s, salt, "$1$" ); } public static String unixMD5( String s, String salt, String magic ) { if ( salt.startsWith( magic ) ) salt = salt.substring( magic.length() ); if ( salt.indexOf( "$" ) >= 0 ) salt = salt.substring( 0, salt.indexOf( "$" ) ); if ( salt.length() > 8 ) salt = salt.substring( 0, 8 ); MD5 md5 = new MD5(); md5.update( s ); md5.update( "$1$" ); md5.update( salt ); MD5 md52 = new MD5(); md52.update( s ); md52.update( salt ); md52.update( s ); byte[] fin = md52.digest(); for ( int p = s.length(); p > 0; p -= 16 ) { md5.update( fin, 0, p > 16 ? 16 : p ); } for ( int i = 0; i < fin.length; i++ ) fin[i] = 0; for ( int i = s.length(); i != 0; i >>>= 1 ) { if ( ( i & 1 ) != 0 ) md5.update( fin, 0, 1 ); else md5.update( s.getBytes(), 0, 1 ); } fin = md5.digest(); for ( int i = 0; i < 1000; i++ ) { md5 = new MD5(); if ( ( i & 1 ) != 0 ) md5.update( s ); else md5.update( fin, 0, 16 ); if ( ( i % 3 ) != 0 ) md5.update( salt ); if ( ( i % 7 ) != 0 ) md5.update( s ); if ( ( i & 1 ) != 0 ) md5.update( fin, 0, 16 ); else md5.update( s ); fin = md5.digest(); } // make output string String result = "$1$" + salt + "$"; result += to64( ( bytes2u( fin[0] ) << 16 ) | ( bytes2u( fin[6] ) << 8 ) | bytes2u( fin[12] ), 4 ); result += to64( ( bytes2u( fin[1] ) << 16 ) | ( bytes2u( fin[7] ) << 8 ) | bytes2u( fin[13] ), 4 ); result += to64( ( bytes2u( fin[2] ) << 16 ) | ( bytes2u( fin[8] ) << 8 ) | bytes2u( fin[14] ), 4 ); result += to64( ( bytes2u( fin[3] ) << 16 ) | ( bytes2u( fin[9] ) << 8 ) | bytes2u( fin[15] ), 4 ); result += to64( ( bytes2u( fin[4] ) << 16 ) | ( bytes2u( fin[10] ) << 8 ) | bytes2u( fin[5] ), 4 ); result += to64( bytes2u( fin[11] ), 2 ); for ( int i = 0; i < fin.length; i++ ) fin[i] = 0; return result; } }