CodecCrypt.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.openspcoop2.utils.crypt;

import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.random.RandomGenerator;
import org.slf4j.Logger;

/**
 * CodecCrypt
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class CodecCrypt implements ICrypt {

	private static final String ITERATOR = "rounds=";
	
	private Logger log;
	private CodecType type;
	private CryptConfig config;
	private RandomGenerator randomGenerator;

	public CodecCrypt(CodecType type){
		this.type = type;
		if(this.type == null) {
			this.type = CodecType.SHA2_BASED_UNIX_CRYPT_SHA256;
		}
	}
	
	@Override
	public void init(Logger log, CryptConfig config) {
		this.log = log;
		
		this.config = config;
		if(this.config == null) {
			this.config = new CryptConfig();
		}
		
		this.randomGenerator = new RandomGenerator(this.config.isUseSecureRandom(), this.config.getAlgorithmSecureRandom());
	}
	
	@Override
	public String crypt(String password) throws UtilsException {
		
		Integer saltLength = this.config.getSaltLength();
		if(saltLength==null) {
			switch (this.type) {
			case LIBC_CRYPT_MD5:
			case LIBC_CRYPT_MD5_APACHE:
				saltLength = 8;
				break;
			case SHA2_BASED_UNIX_CRYPT_SHA256:
			case SHA2_BASED_UNIX_CRYPT_SHA512:
				saltLength = 16;
				break;
			case DES_UNIX_CRYPT:
				saltLength = 2;
				break;
			}
		}
		
		if(this.randomGenerator==null) {
			throw new UtilsException("RandomGenerator undefined");
		}
		String salt = this.randomGenerator.nextRandom(saltLength);
				
		if(!CodecType.DES_UNIX_CRYPT.equals(this.type)) {
			
			//	 a) the salt salt_prefix, $5$ or $6$ respectively
			//	 b) the rounds=<N> specification, if one was present in the input salt string. A trailing '$' is added in this case to separate the rounds specification from the following text.
			//   c) the salt string truncated to 16 characters (solo per SHA2)
			//   d) a '$' character
			
			StringBuilder sb = new StringBuilder();
			String prefix = this.type.getPrefix();
			if(salt.startsWith(prefix) && salt.length()>prefix.length()) {
				salt = salt.substring(prefix.length());
			}
			sb.append(prefix);
			
			if(this.config.getIteration()!=null && this.config.getIteration().intValue()>0 &&
					(CodecType.SHA2_BASED_UNIX_CRYPT_SHA256.equals(this.type) || CodecType.SHA2_BASED_UNIX_CRYPT_SHA512.equals(this.type)) &&
					!salt.startsWith(ITERATOR)) {
				sb.append(ITERATOR).append(this.config.getIteration().intValue()).append("$");
				sb.append(salt);
			}
			else {
				sb.append(salt);
			}
			
			if(!salt.endsWith("$")) {
				sb.append("$");
			}
			
			salt = sb.toString();
		}
		return this._crypt(password, salt);
	}

	@Override
	public boolean check(String password, String pwcrypt) {
		try{
			boolean checkPwS = false;
			String newPw = this._crypt(password, pwcrypt);
			if (newPw.equals(pwcrypt)) {
				checkPwS = true;
			}
			return checkPwS;
		}catch(Throwable e){
			if(this.log!=null) {
				this.log.error("Verifica password '"+pwcrypt+"' fallita: "+e.getMessage(),e);
			}
			return false;
		}
	}

	private String _crypt(String password, String salt) throws UtilsException {
		
		byte [] keyBytes = null;
		try {
			keyBytes = password.getBytes(this.config.getCharsetName());
		}catch(Throwable e){
			throw new UtilsException(e.getMessage(), e);
		}
		switch (this.type) {
			case LIBC_CRYPT_MD5:
				if(salt!=null) {
					return org.apache.commons.codec.digest.Md5Crypt.md5Crypt(keyBytes, salt);
				}
				else {
					return org.apache.commons.codec.digest.Md5Crypt.md5Crypt(keyBytes);
				}
			case LIBC_CRYPT_MD5_APACHE:
				if(salt!=null) {
					return org.apache.commons.codec.digest.Md5Crypt.apr1Crypt(keyBytes, salt);
				}
				else {
					return org.apache.commons.codec.digest.Md5Crypt.apr1Crypt(keyBytes);
				}
			case SHA2_BASED_UNIX_CRYPT_SHA256:
				if(salt!=null) {
					return org.apache.commons.codec.digest.Sha2Crypt.sha256Crypt(keyBytes, salt);
				}
				else {
					return org.apache.commons.codec.digest.Sha2Crypt.sha256Crypt(keyBytes);
				}
			case SHA2_BASED_UNIX_CRYPT_SHA512:
				if(salt!=null) {
					return org.apache.commons.codec.digest.Sha2Crypt.sha512Crypt(keyBytes, salt);
				}
				else {
					return org.apache.commons.codec.digest.Sha2Crypt.sha512Crypt(keyBytes);
				}
			case DES_UNIX_CRYPT:
				if(salt!=null) {
					return org.apache.commons.codec.digest.UnixCrypt.crypt(keyBytes, salt);
				}
				else {
					return org.apache.commons.codec.digest.UnixCrypt.crypt(keyBytes);
				}
		}
		
		throw new UtilsException("Unsupported type '"+this.type+"'");
	}

}