br.com.vectorx.BlowfishCryptox.java Source code

Java tutorial

Introduction

Here is the source code for br.com.vectorx.BlowfishCryptox.java

Source

package br.com.vectorx;

/*
 * =============================================================================
 * Copyright (c) 2012 Renan Vizza Campos/Vector X. All rights reserved. Just Kidding.
 * LICENSE: Apache 2.0
 * Use como quiser, se for melhorar me avise!!
 * Feel free to use this class/project, if you enhance it, please, contact me!
 * 
 * contact: renanvcampos@gmail.com
 */
import java.nio.charset.Charset;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Classe que criptografa e descriptografa somente para <b> Blowfish</b><br>
 * How to use: BlowfishCryptox myCrypt = BlowfishCryptox
 * {@link BlowfishCryptox#getInstance(String, int)}<br>
 * <br>
 * Getting too much <b>java.security.InvalidKeyException: Illegal key size or
 * default parameters?</b> <br>
 * Check Oracle Unlimited JCE <br>
 * If your country laws do not allow you to use that, use cifraCesar between -64
 * AND 64 and salt length <=16
 * 
 * @see <a
 *      href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html">Oracle
 *      Unlimited JCE</a>
 * @author renan.campos
 * 
 */
public class BlowfishCryptox {

    /**
     * Tamanho mximo da palavra salt caso no tenha <a href=
     * "http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html"
     * >Oracle Unlimited JCE</a>
     */
    private static final int LIMITEDSALTLENGTH = 16;
    /**
     * Tamanho mximo da palavra salt caso tenha <a href=
     * "http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html"
     * >Oracle Unlimited JCE</a>
     */
    private static final int UNLIMITEDJCESALT = 56;
    /**
     * Valor mximo Cifra de Csar
     */
    private static final int CIFRAMAX = 128;
    /**
     * Valor mnimo Cifra de Csar
     */
    private static final int CIFRAMIN = -128;
    /**
     * Valor cifra de cesar
     */
    private int cifraCesar = CIFRAMAX;
    private Charset charset;
    /**
     * {@link Cipher} para passo de criptografia
     */
    private Cipher encrypt;
    /**
     * {@link Cipher} para passo de descriptografia
     */
    private Cipher decrypt;

    /**
     * Enumerador contendo dois casos: <br>
     * {@link #ENCRYPT}<br>
     * {@link #DECRYPT}
     * 
     * @author renan.campos
     * 
     */
    private static enum Mode {
        /**
         * Criptografar
         */
        ENCRYPT,
        /**
         * Descriptografar
         */
        DECRYPT;
    }

    private static Logger LOG = LoggerFactory.getLogger(BlowfishCryptox.class);

    /**
     * Construtor private. Define o tamanho mximo da palavra salt de acordo com
     * o SecurityPolice instalado
     * 
     * @param palavraChave
     *            Palavra chave
     * @param algoritmo
     *            Algoritmo a ser utilizado (no caso o Blowfish)
     * @param cifraCesar
     *            em quantos numeros os caracteres sero trocados
     * @param charset
     * @throws IllegalArgumentException
     */
    private BlowfishCryptox(String palavraChave, String algoritmo, int cifraCesar, Charset charset)
            throws IllegalArgumentException {
        try {
            int maxSalt = LIMITEDSALTLENGTH;
            // Checagem para ver se tem JCE Unlimited Strength Policy instalado
            if (Cipher.getMaxAllowedKeyLength(algoritmo) > BlowfishCryptox.CIFRAMAX) {
                maxSalt = BlowfishCryptox.UNLIMITEDJCESALT;
            }
            validateInput(palavraChave, cifraCesar, maxSalt);
            this.charset = charset;
            SecretKey chave = new SecretKeySpec(palavraChave.getBytes(charset), algoritmo);
            this.encrypt = Cipher.getInstance(algoritmo);
            this.decrypt = Cipher.getInstance(algoritmo);
            this.encrypt.init(Cipher.ENCRYPT_MODE, chave);
            this.decrypt.init(Cipher.DECRYPT_MODE, chave);
            this.cifraCesar = cifraCesar;
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Valida o input<br>
     * As validaes feitas so:
     * <ul>
     * <li>Palavra Chave nula</li>
     * <li>Palavra Chave com tamanho maior que 16</li>
     * <li>cifra de cesar < -128 ou > 128</li>
     * </ul>
     * 
     * @param palavraChave
     *            - palavra chave (Salt)
     * @param cifraCesar
     *            - cifra de cesar (caesar cipher, shift chars) - Min -128 and
     *            Max 128
     * @param maxSaltLength
     *            - Tamanho mximo da palavra chave (salt)
     * @throws IllegalArgumentException
     */
    private void validateInput(String palavraChave, int cifraCesar, int maxSaltLength)
            throws IllegalArgumentException {
        // Checagem de Nulo
        if (palavraChave == null || palavraChave.trim().isEmpty()) {
            throw new IllegalArgumentException("Palavra chave (sal) no pode ser nula");
        }
        // Checagem de tamanho da palavra Salt
        if (palavraChave.length() > maxSaltLength) {
            throw new IllegalArgumentException("Tamanho da Palavra chave (sal) maior que " + maxSaltLength);
        }

        // Checagem do tamanho da cifra de cesar
        if (cifraCesar < BlowfishCryptox.CIFRAMIN || cifraCesar > BlowfishCryptox.CIFRAMAX) {
            throw new IllegalArgumentException("Cifra de Cesar deve estar entre -128 e 128");
        }
    }

    /**
     * Mtodo getInstance com charset padro UTF-8
     * 
     * @param palavraChave
     *            chave para criar salt (sal)
     * @param val
     *            valor para cifra de cesar
     * @return instancia de {@link BlowfishCryptox}
     */
    public static BlowfishCryptox getInstance(String palavraChave, int val) {
        return new BlowfishCryptox(palavraChave, "Blowfish", val, Charset.forName("UTF-8"));
    }

    /**
     * Checa se uma senha digitada  igual a uma senha Encriptada
     * 
     * @param senha
     *            senha normal
     * @param senhaEncriptada
     * @return senha corresponde  senha encriptada?
     */
    public boolean checaSenha(String senha, String senhaEncriptada) {
        boolean result = false;
        String cryptPass = crypt(senha);
        if (cryptPass != null && cryptPass.equals(senhaEncriptada)) {
            result = true;
        }

        return result;
    }

    /**
     * Mtodo getInstance com charset definido pelo usurio
     * 
     * @param palavraChave
     *            chave para criar salt (sal) com tamanho mximo 16 ou 56 (se
     *            tiver <a href=
     *            "http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html"
     *            >Oracle Unlimited JCE</a> instalado)
     * @param val
     *            valor para cifra de cesar (> -128 e < 128)
     * @param cs
     *            Charset
     * @return instancia de {@link BlowfishCryptox}
     */
    public static BlowfishCryptox getInstance(String palavraChave, int val, Charset cs) {
        return new BlowfishCryptox(palavraChave, "Blowfish", val, cs);
    }

    /**
     * Criptografa uma String
     * 
     * @param str
     *            - String a ser criptografada
     * @return String criptografada
     * @throws UnsupportedEncodingException
     *             - Problema de charset
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
    public String crypt(String str) {
        byte[] enc = null;
        byte[] cc = null;
        // Retorna a string com aplicao cifra de cesar
        String crypted = this.cifraCesar(str, Mode.ENCRYPT);
        try {
            // Pega o byte[] correspondente  String para o charset informado
            cc = crypted.getBytes(this.charset);
            // Cipher
            enc = this.encrypt.doFinal(cc);
        } catch (IllegalBlockSizeException e) {
            LOG.error("Falha de converso. Tamanho de Block Size", e);
            return null;
        } catch (BadPaddingException e) {
            LOG.error("Falha de BadPaddingException. Tamanho de Block Size", e);
            return null;
        }
        // Faz encoding com Base64
        return new String(Base64.encodeBase64(enc));
    }

    /**
     * Aplica o conceito <a href=http://pt.wikipedia.org/wiki/Cifra_de_Csar>
     * Cifra de cesar</a> para a String, dependendo de seu modo
     * 
     * @param str
     *            - String a ser aplicada
     * @param modo
     *            {@link Mode}
     * @return String alterada
     */
    private String cifraCesar(String str, Mode modo) {
        char[] newStr = new char[str.length()];
        int i = 0;
        // Caso para criptografia
        if (modo.equals(Mode.ENCRYPT)) {
            for (char c : str.toCharArray()) {
                // Valor do char + int da cifra de cesar
                newStr[i] = (char) (c + this.cifraCesar);
                i++;
            }
        } // Caso para descriptografia
        else {
            for (char c : str.toCharArray()) {
                // Valor do char - int da cifra de cesar
                newStr[i] = (char) (c - this.cifraCesar);
                i++;
            }
        }
        // Retorna uma String com o valor do char[]
        return String.copyValueOf(newStr);
    }

    /**
     * Descriptografa uma string criptografada
     * 
     * @param str
     *            String criptografada
     * @return String descriptografada
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public String decrypt(String str) {
        byte[] dec;
        byte[] cc = null;
        try {
            // Decoding com base64
            dec = Base64.decodeBase64(str.getBytes(this.charset));
            // Cipher
            cc = this.decrypt.doFinal(dec);
        } catch (IllegalBlockSizeException e) {
            LOG.error("Falha de converso. Tamanho de Block Size", e);
            return null;
        } catch (BadPaddingException e) {
            LOG.error("Falha de BadPaddingException. Tamanho de Block Size", e);
            return null;
        }
        // Retorna uma String aplicando a cifra de cesar para descriptografia
        return this.cifraCesar(new String(cc, this.charset), Mode.DECRYPT);
    }
}