001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.digest;
020
021import java.nio.charset.Charset;
022import java.security.MessageDigest;
023import java.security.NoSuchAlgorithmException;
024
025import lucee.commons.io.CharsetUtil;
026
027public class Hash {
028        
029        public static final char[] ENCODING_HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
030        public static final char[] ENCODING_HEXUC = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
031        //public static final char[] ENCODING_ASCII = "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray();
032        private static final byte[] DEL=new byte[]{58};
033        
034        
035        public static final String ALGORITHM_MD5="MD5";
036        public static final String ALGORITHM_SHA_256="SHA-256";
037        public static final String ALGORITHM_SHA_384="SHA-384";
038        public static final String ALGORITHM_SHA_512="SHA-512";
039        public static final String ALGORITHM_SHA="SHA";
040
041        // MD5
042        public static String md5(byte[] data) throws NoSuchAlgorithmException {
043                return hash(data, ALGORITHM_MD5,ENCODING_HEX);
044        }
045        
046        public static String md5(String str) throws NoSuchAlgorithmException {
047                return hash(str, ALGORITHM_MD5,ENCODING_HEX,CharsetUtil.UTF8);
048        }
049        
050        public static String md5(String str, Charset charset) throws NoSuchAlgorithmException {
051                return hash(str, ALGORITHM_MD5,ENCODING_HEX,charset);
052        }
053        
054        // SHA
055        public static String sha(byte[] data) throws NoSuchAlgorithmException {
056                return hash(data, ALGORITHM_SHA,ENCODING_HEX);
057        }
058        
059        public static String sha(String str) throws NoSuchAlgorithmException {
060                return hash(str, ALGORITHM_SHA,ENCODING_HEX,CharsetUtil.UTF8);
061        }
062        
063        public static String sha(String str,Charset charset) throws NoSuchAlgorithmException {
064                return hash(str, ALGORITHM_SHA,ENCODING_HEX,charset);
065        }
066
067        
068        // SHA256
069        public static String sha256(byte[] data) throws NoSuchAlgorithmException {
070                return hash(data, ALGORITHM_SHA_256,ENCODING_HEX);
071        }
072        
073        public static String sha256(String str) throws NoSuchAlgorithmException {
074                return hash(str, ALGORITHM_SHA_256,ENCODING_HEX,CharsetUtil.UTF8);
075        }
076        
077        public static String sha256(String str,Charset charset) throws NoSuchAlgorithmException {
078                return hash(str, ALGORITHM_SHA_256,ENCODING_HEX,charset);
079        }
080
081        
082        // SHA384
083        public static String sha384(byte[] data) throws NoSuchAlgorithmException {
084                return hash(data, ALGORITHM_SHA_384,ENCODING_HEX);
085        }
086        
087        public static String sha384(String str) throws NoSuchAlgorithmException {
088                return hash(str, ALGORITHM_SHA_384,ENCODING_HEX,CharsetUtil.UTF8);
089        }
090        
091        public static String sha384(String str,Charset charset) throws NoSuchAlgorithmException {
092                return hash(str, ALGORITHM_SHA_384,ENCODING_HEX,charset);
093        }
094
095        
096        // SHA384
097        public static String sha512(byte[] data) throws NoSuchAlgorithmException {
098                return hash(data, ALGORITHM_SHA_512,ENCODING_HEX);
099        }
100        
101        public static String sha512(String str) throws NoSuchAlgorithmException {
102                return hash(str, ALGORITHM_SHA_512,ENCODING_HEX,CharsetUtil.UTF8);
103        }
104        
105        public static String sha512(String str,Charset charset) throws NoSuchAlgorithmException {
106                return hash(str, ALGORITHM_SHA_512,ENCODING_HEX,charset);
107        }
108        
109        
110        public static String hash(String str, String nonce, String algorithm,char[] encoding) throws NoSuchAlgorithmException {
111                MessageDigest md=MessageDigest.getInstance(algorithm);
112            md.reset();
113            md.update(toBytes(str, CharsetUtil.UTF8));
114            md.update(DEL);
115            md.update(toBytes(nonce, CharsetUtil.UTF8));
116            return new String( enc(md.digest(),encoding)); // no charset needed because all characters are below us-ascii (hex)
117        }
118
119        public static String hash(String input, String algorithm, int numIterations) throws NoSuchAlgorithmException {
120                return hash(input, algorithm, numIterations, ENCODING_HEXUC);
121        }
122        
123        public static String hash(String str, String algorithm, int numIterations,char[] encoding) throws NoSuchAlgorithmException {
124                try {
125                        MessageDigest md=MessageDigest.getInstance(algorithm),mdc;
126                        for(int i=0;i<numIterations;i++){
127                                mdc=(MessageDigest) md.clone();
128                                mdc.reset();
129                            mdc.update(toBytes(str, CharsetUtil.UTF8));
130                            str=new String(enc(mdc.digest(),encoding));
131                        }
132                        return str;
133                }
134                catch (CloneNotSupportedException e) {}
135                
136                // if not possible to clone the MessageDigest create always a new instance
137                for(int i=0;i<numIterations;i++){
138                        str=hash(str, algorithm,encoding,CharsetUtil.UTF8);
139                }
140                return str;
141        }
142        
143
144        
145        public static String hash(String str, String algorithm,char[] encoding, Charset charset) throws NoSuchAlgorithmException {
146                return hash(toBytes(str, charset), algorithm,encoding); 
147        }
148        
149        public static String hash(byte[] data, String algorithm,char[] encoding) throws NoSuchAlgorithmException {
150                MessageDigest md=MessageDigest.getInstance(algorithm);
151            md.reset();
152            md.update(data);
153            return new String( enc(md.digest(),encoding)); // no charset needed because all characters are below us-ascii (hex)
154        }
155        
156        private static byte[] toBytes(String str, Charset charset) {
157                if (str==null) return null;
158                return str.getBytes(charset);
159        }
160        
161        public static String toHexString(byte[] data, boolean upperCase) {
162                return new String(enc(data, upperCase?ENCODING_HEXUC:ENCODING_HEX));
163        }
164        private static char[] enc(byte[] data,char[] enc) {
165                
166                int len = data.length;
167                char[] out = new char[len << 1];
168                // two characters form the hex value.
169                for (int i = 0, j = 0; i < len; i++) {
170                        out[j++] = enc[(0xF0 & data[i]) >>> 4];
171                        out[j++] = enc[0x0F & data[i]];
172                }
173                return out;
174        }
175}