com.redhat.rhn.common.util.MD5Crypt.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rhn.common.util.MD5Crypt.java

Source

/**
 * Copyright (c) 2009--2014 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */

package com.redhat.rhn.common.util;

import org.apache.commons.codec.binary.Hex;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * MD5Crypt
 * Utility class to create/check passwords generated by the perl crypt
 * function. Passwords are in the format of $1$salt$encodedpassword.
 * @version $Rev$
 */
public class MD5Crypt {

    private static Integer saltLength = 8; // MD5 encoded password salt length

    /**
     * MD5Crypt
     */
    private MD5Crypt() {
    }

    /**
     * generateEncodedKey - Handles generating the encoded key from the
     * final digest.
     * @param digest - Digest to use for encoding
     * @param salt - salt to prepend to output
     * @return Returns encoded string $1$salt$encodedkey
     */
    private static String generateEncodedKey(byte[] digest, String salt) {

        StringBuilder out = new StringBuilder(CryptHelper.getMD5Prefix());
        out.append(salt);
        out.append("$");

        int val = ((digest[0] & 0xff) << 16) | ((digest[6] & 0xff) << 8) | (digest[12] & 0xff);
        out.append(CryptHelper.to64(val, 4));

        val = ((digest[1] & 0xff) << 16) | ((digest[7] & 0xff) << 8) | (digest[13] & 0xff);
        out.append(CryptHelper.to64(val, 4));

        val = ((digest[2] & 0xff) << 16) | ((digest[8] & 0xff) << 8) | (digest[14] & 0xff);
        out.append(CryptHelper.to64(val, 4));

        val = ((digest[3] & 0xff) << 16) | ((digest[9] & 0xff) << 8) | (digest[15] & 0xff);
        out.append(CryptHelper.to64(val, 4));

        val = ((digest[4] & 0xff) << 16) | ((digest[10] & 0xff) << 8) | (digest[5] & 0xff);
        out.append(CryptHelper.to64(val, 4));

        val = (digest[11] & 0xff);
        out.append(CryptHelper.to64(val, 2));

        return out.toString();
    }

    /**
     * crypt - method to help in setting passwords.
     * @param key - The key to encode
     * @return Returns a string in the form of "$1$RandomSalt$encodedkey"
     */
    public static String crypt(String key) {
        return crypt(key, CryptHelper.generateRandomSalt(saltLength));
    }

    /**
     * crypt
     * Encodes a key using a salt (s) in the same manner as the
     * perl crypt() function.
     * This method will be called directly when checking passwords. It will
     * also be called from the crypt(key) function when setting a password.
     * @param key - The key to encode
     * @param s - The salt
     * @return Returns a string in the form of "$1$salt$encodedkey"
     * @throws MD5CryptException
     */
    public static String crypt(String key, String s) {

        /**
         * If this method is called in order for a comparison, s may be
         * in the form of $1$salt$encodedkey. We'll need to extract
         * the salt from it.
         */
        String salt = CryptHelper.getSalt(s, CryptHelper.getMD5Prefix(), saltLength);

        MessageDigest md1;
        MessageDigest md2;
        try {
            md1 = MessageDigest.getInstance("MD5");
            md2 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new MD5CryptException("Problem getting MD5 message digest " + "(NoSuchAlgorithm Exception).");
        }

        byte[] keyBytes = key.getBytes();
        byte[] saltBytes = salt.getBytes();
        byte[] prefixBytes = CryptHelper.getMD5Prefix().getBytes();
        int keylength = key.length();

        //Update first MessageDigest - key/prefix/salt
        md1.update(keyBytes);
        md1.update(prefixBytes);
        md1.update(saltBytes);

        //Update second MessageDigest - key/salt/key
        md2.update(keyBytes);
        md2.update(saltBytes);
        md2.update(keyBytes);

        byte[] md2Digest = md2.digest();
        int md2DigestLength = md2Digest.length;

        for (int i = keylength; i > 0; i -= md2DigestLength) {
            if (i > md2DigestLength) {
                md1.update(md2Digest, 0, md2DigestLength);
            } else {
                md1.update(md2Digest, 0, i);
            }
        }

        md2.reset();

        for (int i = keylength; i > 0; i >>= 1) {
            if ((i & 1) == 1) {
                md1.update((byte) 0);
            } else {
                md1.update(keyBytes[0]);
            }
        }

        byte[] md1Digest = md1.digest();

        for (int i = 0; i < 1000; i++) {
            md2.reset();
            if ((i & 1) == 1) {
                md2.update(keyBytes);
            } else {
                md2.update(md1Digest);
            }
            if ((i % 3) != 0) {
                md2.update(saltBytes);
            }
            if ((i % 7) != 0) {
                md2.update(keyBytes);
            }
            if ((i & 1) != 0) {
                md2.update(md1Digest);
            } else {
                md2.update(keyBytes);
            }
            md1Digest = md2.digest();
        }

        return generateEncodedKey(md1Digest, salt);
    }

    /**
     * MD5 and Hexify a string.  Take the input string, MD5 encode it
     * and then turn it into Hex.
     * @param inputString you want md5hexed
     * @return md5hexed String.
     */
    public static String md5Hex(String inputString) {
        byte[] secretBytes;
        try {
            secretBytes = inputString.getBytes("UTF-8");

        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UnsupportedEncodingException when"
                    + " trying to convert a String into UTF-8.  This shouldn't happen.", e);
        }
        return md5Hex(secretBytes);
    }

    /**
     * MD5 and Hexify an array of bytes.  Take the input array, MD5 encodes it
     * and then turns it into Hex.
     * @param secretBytes you want md5hexed
     * @return md5hexed String.
     */
    public static String md5Hex(byte[] secretBytes) {
        String retval = null;
        // add secret
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
            //byte[] secretBytes = inputString.getBytes("UTF-8");
            md.update(secretBytes);
            // generate the digest
            byte[] digest = md.digest();
            // hexify this puppy
            retval = new String(Hex.encodeHex(digest));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(
                    "NoSuchAlgorithm: MD5.  Something" + " weird with your JVM, you should be able to do this.", e);
        }
        return retval;
    }
}