All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.httpclient.security.DefaultCsrfValidator Maven / Gradle / Ivy

There is a newer version: 0.0.126
Show newest version
/**
 * Copyright © 2009 HotPads ([email protected])
 *
 * Licensed 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 io.datarouter.httpclient.security;

import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultCsrfValidator implements CsrfValidator{
	private static final Logger logger = LoggerFactory.getLogger(DefaultCsrfValidator.class);
	private static final String HASHING_ALGORITHM = "SHA-256";
	// AES/CBC requires IV to be generated for every encrypted message!!
	// More details here: https://tools.ietf.org/html/rfc3602
	// The Encapsulating Security Payload (ESP) payload is made up of the IV and the raw cipher-text.
	// The IV field MUST be the same size as the block size of the cipher algorithm being used.
	// The IV MUST be chosen at random, and MUST be unpredictable.
	private static final String MAIN_CIPHER_ALGORITHM = "AES";
	private static final String SUB_CIPHER_ALGORITHM = "CBC/PKCS5Padding";
	private static final String CIPHER_ALGORITHM = MAIN_CIPHER_ALGORITHM + "/" + SUB_CIPHER_ALGORITHM;
	private static final Long DEFAULT_REQUEST_TIMEOUT_IN_MS = 10000L;

	private final String cipherKey;
	private final long requestTimeoutMs;

	public DefaultCsrfValidator(String cipherKey){
		this(cipherKey, DEFAULT_REQUEST_TIMEOUT_IN_MS);
	}

	public DefaultCsrfValidator(String cipherKey, Long requestTimeoutMs){
		this.cipherKey = cipherKey;
		this.requestTimeoutMs = requestTimeoutMs;
	}

	public static String generateCsrfIv(){
		SecureRandom sr;
		try{
			sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
		}catch(NoSuchAlgorithmException | NoSuchProviderException e){
			throw new RuntimeException("error in SecureRandom.getInstance()");
		}
		byte[] salt = new byte[16];
		sr.nextBytes(salt);
		return Base64.getEncoder().encodeToString(salt);
	}

	@Override
	public boolean check(HttpServletRequest request){
		Long requestTime = null;
		try{
			requestTime = getRequestTimeMs(request);
		}catch(Exception e){
			logger.warn("DefaultCsrfValidator failed check. Bad key?", e);
		}
		if(requestTime == null){
			return false;
		}
		return System.currentTimeMillis() < requestTime + requestTimeoutMs;
	}

	public String generateCsrfToken(String cipherIv){
		try{
			Cipher aes = getCipher(Cipher.ENCRYPT_MODE, cipherIv);
			return Base64.getEncoder().encodeToString(aes.doFinal(String.valueOf(System.currentTimeMillis())
					.getBytes()));
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}

	@Override
	public Long getRequestTimeMs(HttpServletRequest request){
		String csrfToken = getParameterOrHeader(request, SecurityParameters.CSRF_TOKEN);
		String cipherIv = getParameterOrHeader(request, SecurityParameters.CSRF_IV);
		try{
			Cipher aes = getCipher(Cipher.DECRYPT_MODE, cipherIv);
			return Long.parseLong(new String(aes.doFinal(Base64.getDecoder().decode(csrfToken))));
		}catch(GeneralSecurityException e){
			throw new RuntimeException(e);
		}
	}

	private static String getParameterOrHeader(HttpServletRequest request, String key){
		String value = request.getParameter(key);
		return value != null ? value : request.getHeader(key);
	}

	private SecretKeySpec computeKey(String cipherKey) throws NoSuchAlgorithmException{
		MessageDigest digest = MessageDigest.getInstance(HASHING_ALGORITHM);
		digest.update(cipherKey.getBytes());
		return new SecretKeySpec(digest.digest(), 0, 16, MAIN_CIPHER_ALGORITHM);
	}

	private Cipher getCipher(int mode, String cipherIv)
	throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException{
		Cipher aes = Cipher.getInstance(CIPHER_ALGORITHM);
		aes.init(mode, computeKey(cipherKey), new IvParameterSpec(cipherIv.getBytes(), 0, 16));
		return aes;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy