com.feilong.tools.security.symmetric.SymmetricEncryption.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.tools.security.symmetric.SymmetricEncryption.java

Source

/*
 * Copyright (C) 2008 feilong (venusdrogon@163.com)
 *
 * 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 com.feilong.tools.security.symmetric;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

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

//import sun.misc.BASE64Decoder;
//import sun.misc.BASE64Encoder;
import com.feilong.commons.core.io.CharsetType;
import com.feilong.commons.core.tools.json.JsonUtil;
import com.feilong.commons.core.util.ByteUtil;
import com.feilong.commons.core.util.StringUtil;
import com.feilong.commons.core.util.Validator;
import com.feilong.tools.security.EncryptionException;

//}${person}{@code
//}
//${person} == ${person}
/**
 * 
 * 
 * <h3>:</h3> <blockquote>
 * <ul>
 * <li>?spring ?
 * 
 * <pre>
 * {@code
 *    <bean id="blowfishForPassword" class="com.feilong.commons.core.security.symmetric.SymmetricEncryption" lazy-init="true">
 *       <!-- 1?? -->
 *       <constructor-arg index="0" value="Blowfish" />
 *       <!-- 2? -->
 *       <constructor-arg index="1" value="feilong" />
 *    </bean>
 * }
 * </pre>
 * 
 * </li>
 * <li>????:{@link SymmetricType}</li>
 * </ul>
 * </blockquote>
 * 
 * 
 * <h3>?:</h3> <blockquote>
 * <ul>
 * <li>{@link SymmetricType#DES}</li>
 * <li>{@link SymmetricType#DESede}</li>
 * <li>{@link SymmetricType#AES}</li>
 * <li>{@link SymmetricType#Blowfish}</li>
 * <li>{@link SymmetricType#RC2}</li>
 * <li>{@link SymmetricType#RC4}</li>
 * <li>{@link SymmetricType#ARCFOUR}</li>
 * </ul>
 * </blockquote>
 * 
 * 
 * <h3>??:</h3>
 * 
 * <blockquote>
 * <ul>
 * <li>{@link #encryptBase64(String, String)},{@link #decryptBase64(String, String)}<br>
 * ??, Base64?.</li>
 * <li>{@link #encryptHex(Object, String)},{@link #decryptHex(String, String)}<br>
 * ??,<b></b> Hex?????<b>(???,??=?,?url?)</b></li>
 * </ul>
 * </blockquote>
 * 
 * 
 * <h3>:</h3>
 * 
 * <blockquote>
 * 
 * <pre>
 * {@code
 * Example 1,encryptHex:
 *       String original = "feilong";
 *       String keyString = "feilong";
 * 
 *       SymmetricEncryption symmetricEncryption = new SymmetricEncryption(SymmetricType.Blowfish, keyString);
 *       log.info(symmetricEncryption.encryptHex(original,CharsetType.UTF8));
 *       
 *       :055976934539FAAA2439E23AB9F165552F179E4C04C1F7F6
 * 
 * Example 2,decryptHex:
 *       String keyString = "feilong";
 *       String hexString = "055976934539FAAA2439E23AB9F165552F179E4C04C1F7F6";
 * 
 *       SymmetricEncryption symmetricEncryption = new SymmetricEncryption(SymmetricType.Blowfish, keyString);
 *       log.info(symmetricEncryption.decryptHex(hexString,CharsetType.UTF8));
 * 
 *       :feilong
 * }
 * </pre>
 * 
 * </blockquote>
 * 
 * @author <a href="mailto:venusdrogon@163.com"></a>
 * @version 1.0.0 2011-12-26 ?11:05:53
 * @version 1.0.1 2013-1-15 15:18 json log
 * @version 1.0.7 2014-6-5 16:26 javadoc
 * @see javax.crypto.Cipher
 * @see javax.crypto.Cipher#ENCRYPT_MODE
 * @see javax.crypto.Cipher#DECRYPT_MODE
 * @see javax.crypto.KeyGenerator
 * @see java.security.Key
 * @see org.apache.commons.codec.binary.Base64
 * @see SymmetricType
 * @see #encryptBase64(String, String)
 * @see #decryptBase64(String, String)
 * @see #encryptHex(Object, String)
 * @see #decryptHex(String, String)
 */
public final class SymmetricEncryption {

    /** The Constant log. */
    protected static final Logger log = LoggerFactory.getLogger(SymmetricEncryption.class);

    /** key. */
    private Key key;

    /** The key string. */
    private final String keyString;

    /** The algorithm. */
    private final String algorithm;

    /**
     * ??? DES/CBC/PKCS5Padding<br>
     * ?????? Java Cryptography Architecture Reference Guide  A.
     * 
     * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html">StandardNames</a>
     */
    private String transformation;

    /**
     * (?).
     * 
     * @param symmetricType
     *            the symmetric type
     * @param keyString
     *            
     * @throws NullPointerException
     *             if isNullOrEmpty(symmetricType) or isNullOrEmpty(keyString)
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see SymmetricType
     * @see #SymmetricEncryption(SymmetricType, String, CipherMode, CipherPadding)
     */
    public SymmetricEncryption(SymmetricType symmetricType, String keyString)
            throws NullPointerException, EncryptionException {
        this(symmetricType, keyString, null, null);
    }

    /**
     * (?).
     * 
     * @param symmetricType
     *            the symmetric type
     * @param keyString
     *            the key string
     * @param cipherMode
     *            the cipher mode
     * @param cipherPadding
     *            the cipher padding
     * @throws NullPointerException
     *             if isNullOrEmpty(symmetricType) or isNullOrEmpty(keyString)
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see SymmetricType
     * @see javax.crypto.Cipher#tokenizeTransformation(String)
     * @since 1.0.7
     */
    public SymmetricEncryption(SymmetricType symmetricType, String keyString, CipherMode cipherMode,
            CipherPadding cipherPadding) throws NullPointerException, EncryptionException {
        if (Validator.isNullOrEmpty(keyString)) {
            throw new NullPointerException("the keyString can't be null");
        }
        if (Validator.isNullOrEmpty(symmetricType)) {
            throw new NullPointerException("the symmetricType can't be null");
        }

        this.keyString = keyString;
        this.algorithm = symmetricType.getAlgorithm();

        if (null == cipherMode && null == cipherPadding) {
            this.transformation = algorithm;
        } else {
            this.transformation = algorithm;
            if (null != cipherMode) {
                transformation += "/" + cipherMode;
            }
            if (null != cipherPadding) {
                transformation += "/" + cipherPadding;
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("algorithm:[{}],keyString:[{}],transformation:[{}]", algorithm, keyString, transformation);
        }

        //,??,????   NoSuchAlgorithmException
        try {
            this.key = getKey(keyString);
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getClass().getName(), e);
            throw new EncryptionException(e);
        }
    }

    /**
     * ??, Base64?.
     * 
     * <pre>
     * keyString=feilong
     * encrypBase64("feilong") ---->BVl2k0U5+qokOeI6ufFlVS8XnkwEwff2
     * </pre>
     * 
     * @param original
     *            
     * @param charsetName
     *            ? {@link CharsetType}
     * @return ?
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see sun.misc.BASE64Encoder
     * @see org.apache.commons.codec.binary.Base64
     * @see com.feilong.commons.core.io.CharsetType
     */
    @SuppressWarnings("restriction")
    public String encryptBase64(String original, String charsetName) throws EncryptionException {
        try {
            byte[] bs1 = original.getBytes(charsetName);
            byte[] bs = opBytes(bs1, Cipher.ENCRYPT_MODE);

            String encode = new String(Base64.encodeBase64(bs));

            if (log.isDebugEnabled()) {
                Map<String, String> map = new LinkedHashMap<String, String>();

                map.put("algorithm", algorithm);
                map.put("original", original);
                map.put("keyString", keyString);
                map.put("encrypBase64", encode);
                map.put("encrypBase64Length", "" + encode.length());

                log.debug(JsonUtil.format(map));
            }
            return encode;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                Map<String, String> map = new LinkedHashMap<String, String>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("original", original);

                log.error(JsonUtil.format(map));
            }
            log.error(e.getClass().getName(), e);

            //?????????
            throw new EncryptionException(e);
        }
    }

    /**
     * des Base64.
     * 
     * <pre>
     * keyString=feilong
     * decryptBase64("BVl2k0U5+qokOeI6ufFlVS8XnkwEwff2") ---->feilong
     * 
     * </pre>
     * 
     * @param base64String
     *            ?
     * @param charsetName
     *            ? {@link CharsetType}
     * @return ?
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see sun.misc.BASE64Decoder
     * @see sun.misc.BASE64Decoder#decodeBuffer(String)
     * @see org.apache.commons.codec.binary.Base64
     * @see org.apache.commons.codec.binary.Base64#decodeBase64(byte[])
     * @see CharsetType
     */
    @SuppressWarnings("restriction")
    public String decryptBase64(String base64String, String charsetName) throws EncryptionException {
        try {
            byte[] byteMi = Base64.decodeBase64(base64String);
            byte[] bs = opBytes(byteMi, Cipher.DECRYPT_MODE);
            String original = new String(bs, charsetName);

            if (log.isDebugEnabled()) {
                Map<String, String> map = new LinkedHashMap<String, String>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("original", original);
                map.put("base64String", base64String);

                log.debug(JsonUtil.format(map));
            }
            return original;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                Map<String, String> map = new LinkedHashMap<String, String>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("base64String", base64String);

                log.error(JsonUtil.format(map));
            }
            log.error(e.getClass().getName(), e);
            throw new EncryptionException(e);
        }
    }

    /**
     * ??, Hex??.
     * 
     * 
     * <pre>
     * :key=feilong
     * encryptHex("feilong")---->055976934539FAAA2439E23AB9F165552F179E4C04C1F7F6
     * </pre>
     * 
     * @param original
     *            ,
     * @param charsetName
     *            ? {@link CharsetType}
     * @return String,String
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see StringUtil#toBytes(String, String)
     * @see #opBytes(byte[], int)
     * @see ByteUtil#bytesToHexStringUpperCase(byte[])
     */
    public String encryptHex(Object original, String charsetName) throws EncryptionException {
        try {
            byte[] bs = StringUtil.toBytes(original.toString(), charsetName);
            byte[] bs2 = opBytes(bs, Cipher.ENCRYPT_MODE);
            String hexStringUpperCase = ByteUtil.bytesToHexStringUpperCase(bs2);

            if (log.isDebugEnabled()) {
                Map<String, Object> map = new LinkedHashMap<String, Object>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("original", original);
                map.put("hexStringUpperCase", hexStringUpperCase);
                map.put("hexStringUpperCaseLength", hexStringUpperCase.length());

                log.debug(JsonUtil.format(map));
            }
            return hexStringUpperCase;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                Map<String, Object> map = new LinkedHashMap<String, Object>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("original", original);

                log.error(JsonUtil.format(map));
            }
            log.error(e.getClass().getName(), e);
            throw new EncryptionException(e);
        }
    }

    /**
     * 16 des , String,String.
     * 
     * <pre>
     * :key=feilong
     * decryptHex("055976934539FAAA2439E23AB9F165552F179E4C04C1F7F6")---->"feilong"
     * </pre>
     * 
     * @param hexString
     *            ?16?, 055976934539FAAA2439E23AB9F165552F179E4C04C1F7F6
     * @param charsetName
     *            ? {@link CharsetType}
     * @return  String
     * @throws EncryptionException
     *             ?,EncryptionException?
     * @see #opBytes(byte[], int)
     */
    public String decryptHex(String hexString, String charsetName) throws EncryptionException {
        try {
            byte[] bs = ByteUtil.hexBytesToBytes(hexString.getBytes(charsetName));

            byte[] bs2 = opBytes(bs, Cipher.DECRYPT_MODE);
            String original = new String(bs2);

            if (log.isDebugEnabled()) {
                Map<String, Object> map = new LinkedHashMap<String, Object>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("hexString", hexString);
                map.put("original", original);

                log.debug(JsonUtil.format(map));
            }
            return original;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                Map<String, Object> map = new LinkedHashMap<String, Object>();

                map.put("algorithm", algorithm);
                map.put("keyString", keyString);
                map.put("hexString", hexString);

                log.error(JsonUtil.format(map));
            }
            log.error(e.getClass().getName(), e);
            throw new EncryptionException(e);
        }
    }

    // **********************************************************************
    /**
     * ?.
     * 
     * @param _keyString
     *            
     * @return Key
     * @throws NoSuchAlgorithmException
     *             the no such algorithm exception
     * @see <a href="http://blog.csdn.net/hbcui1984/article/details/5753083">Linux?AES</a>
     * @see KeyGenerator
     * @see SecureRandom
     */
    private Key getKey(String _keyString) throws NoSuchAlgorithmException {
        // KeyGenerator ????????? KeyGenerator ??
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);

        // SHA1PRNG: It is just ensuring the random number generated is as close to "truly random" as possible.
        // Easily guessable random numbers break encryption.

        // ???? (RNG) ???
        //TODO
        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");

        // SecureRandom??getInstance?setSeed
        //  :windowslinux?
        // javax.crypto.BadPaddingException: Given final block not properly padded
        secureRandom.setSeed(_keyString.getBytes());

        keyGenerator.init(secureRandom);

        Key _key = keyGenerator.generateKey();
        keyGenerator = null;
        return _key;
    }

    /**
     * key.
     * 
     * @param keyRule
     *            the key rule
     * @return the key2
     */
    private Key getKey2(String keyRule) {
        byte[] keyByte = keyRule.getBytes();
        // ?,0
        byte[] byteTemp = new byte[8];
        int keyByteLenth = keyByte.length;
        // ???
        for (int i = 0; i < byteTemp.length && i < keyByteLenth; ++i) {
            byteTemp[i] = keyByte[i];
        }
        Key _key = new SecretKeySpec(byteTemp, algorithm);
        return _key;
    }

    /**
     * ?.
     * 
     * @param bytes
     *            the bytes
     * @param opmode
     *            ?,{@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
     * @return the new buffer with the result
     * 
     * @throws NoSuchAlgorithmException
     *             the no such algorithm exception
     * @throws NoSuchPaddingException
     *             the no such padding exception
     * @throws InvalidKeyException
     *             the invalid key exception
     * @throws IllegalBlockSizeException
     *             the illegal block size exception
     * @throws BadPaddingException
     *             the bad padding exception
     * @see Cipher
     * @see Cipher#getInstance(String)
     * @see Cipher#init(int, Key)
     * @see Cipher#doFinal(byte[])
     */
    private byte[] opBytes(byte[] bytes, int opmode) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        // ???? Java Cryptographic Extension (JCE) 
        // ?transformation??DES??????

        //  CFB  OFB ?Cipher ???? Cipher ???
        // ???????? "DES/CFB8/NoPadding"  "DES/OFB32/PKCS5Padding" ?

        // ??SunJCE ?? DES  64 ?
        //  CFB8  OFB8  8 ??Cipher ????? Cipher ?
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(opmode, key);

        // ? Cipher ? init ??
        // ???? init ???
        return cipher.doFinal(bytes);
    }
}