org.noerp.base.crypto.HashCrypt.java Source code

Java tutorial

Introduction

Here is the source code for org.noerp.base.crypto.HashCrypt.java

Source

/*******************************************************************************
 * 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.noerp.base.crypto;

import static org.noerp.base.util.UtilIO.UTF8;

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

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.RandomStringUtils;
import org.noerp.base.util.Debug;
import org.noerp.base.util.GeneralRuntimeException;
import org.noerp.base.util.StringUtil;
import org.noerp.base.util.UtilValidate;

/**
 * Utility class for doing SHA-1/MD5 One-Way Hash Encryption
 *
 */
public class HashCrypt {

    public static final String module = HashCrypt.class.getName();
    public static final String CRYPT_CHAR_SET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";

    public static MessageDigest getMessageDigest(String type) {
        try {
            return MessageDigest.getInstance(type);
        } catch (NoSuchAlgorithmException e) {
            throw new GeneralRuntimeException("Could not load digestor(" + type + ")", e);
        }
    }

    public static boolean comparePassword(String crypted, String defaultCrypt, String password) {
        if (crypted.startsWith("{")) {
            // FIXME: should have been getBytes("UTF-8") originally
            return doCompareTypePrefix(crypted, defaultCrypt, password.getBytes());
        } else if (crypted.startsWith("$")) {
            return doComparePosix(crypted, defaultCrypt, password.getBytes(UTF8));
        } else {
            // FIXME: should have been getBytes("UTF-8") originally
            return doCompareBare(crypted, defaultCrypt, password.getBytes());
        }
    }

    private static boolean doCompareTypePrefix(String crypted, String defaultCrypt, byte[] bytes) {
        int typeEnd = crypted.indexOf("}");
        String hashType = crypted.substring(1, typeEnd);
        String hashed = crypted.substring(typeEnd + 1);
        MessageDigest messagedigest = getMessageDigest(hashType);
        messagedigest.update(bytes);
        byte[] digestBytes = messagedigest.digest();
        char[] digestChars = Hex.encodeHex(digestBytes);
        String checkCrypted = new String(digestChars);
        if (hashed.equals(checkCrypted)) {
            return true;
        }
        // This next block should be removed when all {prefix}oldFunnyHex are fixed.
        if (hashed.equals(oldFunnyHex(digestBytes))) {
            Debug.logWarning(
                    "Warning: detected oldFunnyHex password prefixed with a hashType; this is not valid, please update the value in the database with ({%s}%s)",
                    module, hashType, checkCrypted);
            return true;
        }
        return false;
    }

    private static boolean doComparePosix(String crypted, String defaultCrypt, byte[] bytes) {
        int typeEnd = crypted.indexOf("$", 1);
        int saltEnd = crypted.indexOf("$", typeEnd + 1);
        String hashType = crypted.substring(1, typeEnd);
        String salt = crypted.substring(typeEnd + 1, saltEnd);
        String hashed = crypted.substring(saltEnd + 1);
        return hashed.equals(getCryptedBytes(hashType, salt, bytes));
    }

    private static boolean doCompareBare(String crypted, String defaultCrypt, byte[] bytes) {
        String hashType = defaultCrypt;
        String hashed = crypted;
        MessageDigest messagedigest = getMessageDigest(hashType);
        messagedigest.update(bytes);
        return hashed.equals(oldFunnyHex(messagedigest.digest()));
    }

    /*
     * @deprecated use cryptBytes(hashType, salt, password); eventually, use
     * cryptUTF8(hashType, salt, password) after all existing installs are
     * salt-based.  If the call-site of cryptPassword is just used to create a *new*
     * value, then you can switch to cryptUTF8 directly.
     */
    @Deprecated
    public static String cryptPassword(String hashType, String salt, String password) {
        // FIXME: should have been getBytes("UTF-8") originally
        return password != null ? cryptBytes(hashType, salt, password.getBytes()) : null;
    }

    public static String cryptUTF8(String hashType, String salt, String value) {
        return value != null ? cryptBytes(hashType, salt, value.getBytes(UTF8)) : null;
    }

    public static String cryptValue(String hashType, String salt, String value) {
        return value != null ? cryptBytes(hashType, salt, value.getBytes()) : null;
    }

    public static String cryptBytes(String hashType, String salt, byte[] bytes) {
        if (hashType == null) {
            hashType = "SHA";
        }
        if (salt == null) {
            salt = RandomStringUtils.random(new Random().nextInt(15) + 1, CRYPT_CHAR_SET);
        }
        StringBuilder sb = new StringBuilder();
        sb.append("$").append(hashType).append("$").append(salt).append("$");
        sb.append(getCryptedBytes(hashType, salt, bytes));
        return sb.toString();
    }

    private static String getCryptedBytes(String hashType, String salt, byte[] bytes) {
        try {
            MessageDigest messagedigest = MessageDigest.getInstance(hashType);
            messagedigest.update(salt.getBytes(UTF8));
            messagedigest.update(bytes);
            return Base64.encodeBase64URLSafeString(messagedigest.digest()).replace('+', '.');
        } catch (NoSuchAlgorithmException e) {
            throw new GeneralRuntimeException("Error while comparing password", e);
        }
    }

    /**
     * @deprecated use digestHash("SHA", null, str)
     */
    @Deprecated
    public static String getDigestHash(String str) {
        return digestHash("SHA", null, str);
    }

    /**
     * @deprecated use digestHash(hashType, null, str))
     */
    @Deprecated
    public static String getDigestHash(String str, String hashType) {
        return digestHash(hashType, null, str);
    }

    /**
     * @deprecated use digestHash(hashType, code, str);
     */
    @Deprecated
    public static String getDigestHash(String str, String code, String hashType) {
        return digestHash(hashType, code, str);
    }

    public static String digestHash(String hashType, String code, String str) {
        if (str == null)
            return null;
        byte[] codeBytes;
        try {
            if (code == null)
                codeBytes = str.getBytes();
            else
                codeBytes = str.getBytes(code);
        } catch (UnsupportedEncodingException e) {
            throw new GeneralRuntimeException("Error while computing hash of type " + hashType, e);
        }
        return digestHash(hashType, codeBytes);
    }

    public static String digestHash(String hashType, byte[] bytes) {
        try {
            MessageDigest messagedigest = MessageDigest.getInstance(hashType);
            messagedigest.update(bytes);
            byte[] digestBytes = messagedigest.digest();
            char[] digestChars = Hex.encodeHex(digestBytes);

            StringBuilder sb = new StringBuilder();
            sb.append("{").append(hashType).append("}");
            sb.append(digestChars, 0, digestChars.length);
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new GeneralRuntimeException("Error while computing hash of type " + hashType, e);
        }
    }

    public static String digestHash64(String hashType, byte[] bytes) {
        if (hashType == null) {
            hashType = "SHA";
        }
        try {
            MessageDigest messagedigest = MessageDigest.getInstance(hashType);
            messagedigest.update(bytes);
            byte[] digestBytes = messagedigest.digest();

            StringBuilder sb = new StringBuilder();
            sb.append("{").append(hashType).append("}");
            sb.append(Base64.encodeBase64URLSafeString(digestBytes).replace('+', '.'));
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new GeneralRuntimeException("Error while computing hash of type " + hashType, e);
        }
    }

    /**
     * @deprecated use cryptPassword
     */
    @Deprecated
    public static String getHashTypeFromPrefix(String hashString) {
        if (UtilValidate.isEmpty(hashString) || hashString.charAt(0) != '{') {
            return null;
        }

        return hashString.substring(1, hashString.indexOf('}'));
    }

    /**
     * @deprecated use cryptPassword
     */
    @Deprecated
    public static String removeHashTypePrefix(String hashString) {
        if (UtilValidate.isEmpty(hashString) || hashString.charAt(0) != '{') {
            return hashString;
        }

        return hashString.substring(hashString.indexOf('}') + 1);
    }

    /**
     * @deprecated use digestHashOldFunnyHex(hashType, str)
     */
    @Deprecated
    public static String getDigestHashOldFunnyHexEncode(String str, String hashType) {
        return digestHashOldFunnyHex(hashType, str);
    }

    public static String digestHashOldFunnyHex(String hashType, String str) {
        if (UtilValidate.isEmpty(hashType))
            hashType = "SHA";
        if (str == null)
            return null;
        try {
            MessageDigest messagedigest = MessageDigest.getInstance(hashType);
            byte[] strBytes = str.getBytes();

            messagedigest.update(strBytes);
            return oldFunnyHex(messagedigest.digest());
        } catch (Exception e) {
            Debug.logError(e, "Error while computing hash of type " + hashType, module);
        }
        return str;
    }

    // This next block should be removed when all {prefix}oldFunnyHex are fixed.
    private static String oldFunnyHex(byte[] bytes) {
        int k = 0;
        char[] digestChars = new char[bytes.length * 2];
        for (int l = 0; l < bytes.length; l++) {
            int i1 = bytes[l];

            if (i1 < 0) {
                i1 = 127 + i1 * -1;
            }
            StringUtil.encodeInt(i1, k, digestChars);
            k += 2;
        }
        return new String(digestChars);
    }
}