/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.core.util; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; /** * Description: it's our hash factory * * * @author Sabina Jeger * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ public class Encoder { private static final OLog log = Tracing.createLoggerFor(Encoder.class); public enum Algorithm { md5("MD5", 1, true), md5_noSalt("MD5", 1, false), sha1("SHA-1", 100, true), sha256("SHA-256", 100, true), sha512("SHA-512", 100, true), pbkdf2("PBKDF2WithHmacSHA1", 20000, true), sha256Exam("SHA-256", 1, false); private final boolean salted; private final int iterations; private final String algorithm; private Algorithm(String algorithm, int iterations, boolean salted) { this.algorithm = algorithm; this.iterations = iterations; this.salted = salted; } public boolean isSalted() { return salted; } public String getAlgorithm() { return algorithm; } public int getIterations() { return iterations; } public static final Algorithm find(String str) { if(StringHelper.containsNonWhitespace(str)) { for(Algorithm value:values()) { if(value.name().equals(str)) { return value; } } } return md5; } } /** * The MD5 helper object for this class. */ public static final MD5Encoder md5Encoder = new MD5Encoder(); /** * encrypt the supplied argument with md5. * * @param s * @return MD5 encrypted string */ public static String md5hash(String s) { return md5(s, null); } public static String sha256Exam(String s) { try { String HEXES = "0123456789abcdef"; java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256"); byte[] array = md.digest(s.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; ++i) { sb.append(HEXES.charAt((array[i] & 0xF0) >> 4)).append(HEXES.charAt((array[i] & 0x0F))); } return sb.toString(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { log.error("", e); return null; } } public static String encrypt(String s, String salt, Algorithm algorithm) { switch(algorithm) { case md5: return md5(s, salt); case sha1: case sha256: case sha512: return digest(s, salt, algorithm); case pbkdf2: return secretKey(s, salt, algorithm); default: return md5(s, salt); } } protected static String md5(String s, String salt) { try { byte[] inbytes = s.getBytes(); MessageDigest digest = MessageDigest.getInstance(Algorithm.md5.algorithm); digest.reset(); if(salt != null) { digest.update(salt.getBytes()); } byte[] outbytes = digest.digest(inbytes); return md5Encoder.encode(outbytes); } catch (NoSuchAlgorithmException e) { log.error("", e); return null; } } protected static String digest(String password, String salt, Algorithm algorithm) { try { MessageDigest digest = MessageDigest.getInstance(algorithm.getAlgorithm()); digest.reset(); if(salt != null) { digest.update(salt.getBytes()); } byte[] input = password.getBytes("UTF-8"); for(int i=algorithm.getIterations(); i-->0; ) { input = digest.digest(input); } return byteToBase64(input); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { log.error("", e); return null; } } protected static String secretKey(String password, String salt, Algorithm algorithm) { try { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), algorithm.getIterations(), 160); SecretKeyFactory f = SecretKeyFactory.getInstance(algorithm.getAlgorithm()); return byteToBase64(f.generateSecret(spec).getEncoded()); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { log.error("", e); return null; } } public static String getSalt() { try { //Always use a SecureRandom generator SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); //Create array for salt byte[] salt = new byte[16]; //Get a random salt sr.nextBytes(salt); //return salt return byteToBase64(salt); } catch (NoSuchAlgorithmException e) { log.error("", e); return null; } } public static String byteToBase64(byte[] data){ return StringHelper.encodeBase64(data); } }