Java tutorial
/* * 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); } }