Java tutorial
/** * * GFW.Press * Copyright (C) 2016 chinashiyu ( chinashiyu@gfw.press ; http://gfw.press ) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 com.gfw.press.encrypt; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.InvalidParameterException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.sql.Timestamp; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; /** * GFW.Press?? * * @author chinashiyu ( chinashiyu@gfw.press ; http://gfw.press ) * */ public class Encrypt { public static final String CHARSET = "UTF-8"; public static final int BLOCK_MAX_FILE = 64 * 1024 * 1024; // 64MB?? public static final int ENCRYPT_SIZE = 30; // ??30?14 public static final int IV_SIZE = 16; // IV16 public static final int NOISE_MAX = 1024 * 4; // ?4K private SecureRandom secureRandom = null; private Cipher cipher = null; private KeyGenerator keyGenerator = null; public Encrypt() { super(); secureRandom = new SecureRandom(); try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); keyGenerator = KeyGenerator.getInstance("AES"); } catch (NoSuchAlgorithmException | NoSuchPaddingException ex) { throw new RuntimeException(ex); } } /** * * * @param key * SecretKey * @param encrypt_bytes * ?16IV? * * @return ? * */ public byte[] decrypt(String nodePwd, byte[] encrypt_bytes) { if (encrypt_bytes == null || encrypt_bytes.length < IV_SIZE) { return null; } SecretKey key = null; try { key = getSecretKey(DigestUtils.md5Hex(nodePwd.getBytes(CHARSET))); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (null == key) { System.out.println("key!"); return null; } byte[] IV = new byte[IV_SIZE]; byte[] part2 = new byte[encrypt_bytes.length - IV_SIZE]; System.arraycopy(encrypt_bytes, 0, IV, 0, IV.length); System.arraycopy(encrypt_bytes, IV.length, part2, 0, part2.length); return decrypt(key, part2, IV); } /** * * * @param key * SecretKey * @param cipher_data * ? * @param IV * IV * * @return ? * */ private byte[] decrypt(SecretKey key, byte[] cipher_data, byte[] IV) { if (key == null || cipher_data == null || cipher_data.length == 0 || IV == null || IV.length == 0) { return null; } IvParameterSpec IVSpec = new IvParameterSpec(IV); try { cipher.init(Cipher.DECRYPT_MODE, key, IVSpec); } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) { log("?Cipher"); ex.printStackTrace(); return null; } try { return cipher.doFinal(cipher_data); } catch (IllegalBlockSizeException | BadPaddingException ex) { log("?"); ex.printStackTrace(); return null; } } /** * * * @param key * SecretKey * @param data * ? * * @return ? * */ private byte[] encrypt(SecretKey key, byte[] data) { if (key == null || data == null) { return null; } byte[] IV = getSecureRandom(IV_SIZE); IvParameterSpec IVSpec = new IvParameterSpec(IV); try { cipher.init(Cipher.ENCRYPT_MODE, key, IVSpec); } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) { log("?Cipher"); ex.printStackTrace(); return null; } byte[] cipher_bytes = null; try { cipher_bytes = cipher.doFinal(data); } catch (IllegalBlockSizeException | BadPaddingException ex) { log("?"); ex.printStackTrace(); return null; } byte[] iv_cipher_bytes = new byte[cipher_bytes.length + IV_SIZE]; System.arraycopy(IV, 0, iv_cipher_bytes, 0, IV.length); System.arraycopy(cipher_bytes, 0, iv_cipher_bytes, IV.length, cipher_bytes.length); return iv_cipher_bytes; } /** * ? * * @param key * SecretKey * * @param bytes * ? * * @return [?+?]? + [? + ?] * */ public byte[] encryptNet(String nodePwd, byte[] bytes) { if (bytes == null || bytes.length == 0) { return null; } SecretKey key = null; try { key = getSecretKey(DigestUtils.md5Hex(nodePwd.getBytes(CHARSET))); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (null == key) { System.out.println("key!"); return null; } byte[] IV = getSecureRandom(IV_SIZE); IvParameterSpec IVSpec = new IvParameterSpec(IV); try { cipher.init(Cipher.ENCRYPT_MODE, key, IVSpec); } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) { log("?Cipher"); ex.printStackTrace(); return null; } // ? byte[] cipher_bytes = null; try { cipher_bytes = cipher.doFinal(bytes); } catch (IllegalBlockSizeException | BadPaddingException ex) { log("?"); ex.printStackTrace(); return null; } // ? byte[] noise_bytes = (cipher_bytes.length < NOISE_MAX / 2) ? getSecureRandom(secureRandom.nextInt(NOISE_MAX)) : new byte[0]; byte[] size_bytes = encrypt(key, getBlockSizeBytes((IV_SIZE + cipher_bytes.length), noise_bytes.length)); if (size_bytes == null || size_bytes.length != ENCRYPT_SIZE) { return null; } byte[] all_cipher = new byte[size_bytes.length + IV_SIZE + cipher_bytes.length + noise_bytes.length]; System.arraycopy(size_bytes, 0, all_cipher, 0, size_bytes.length); System.arraycopy(IV, 0, all_cipher, size_bytes.length, IV.length); System.arraycopy(cipher_bytes, 0, all_cipher, size_bytes.length + IV.length, cipher_bytes.length); if (noise_bytes.length > 0) { // ?? System.arraycopy(noise_bytes, 0, all_cipher, size_bytes.length + IV.length + cipher_bytes.length, noise_bytes.length); } size_bytes = null; IV = null; cipher_bytes = null; noise_bytes = null; return all_cipher; } /** * ? * * @param bytes * ? * * @return ? * */ public int getBlockSize(byte[] bytes) { if (bytes == null) { return 0; } try { String size = new String(bytes, CHARSET); if (!size.matches("\\d+")) { return 0; } return Integer.valueOf(size); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); return 0; } } /** * ?? * * @param size * ? * @return ? */ public byte[] getBlockSizeBytes(int size) { try { return String.format("%08d", size).getBytes(CHARSET); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); return null; } } /** * ?? * * @param size * ??? * * @param noise_size * ??? * * @return ? */ public byte[] getBlockSizeBytes(int data_size, int noise_size) { try { return String.format("%08d,%05d", data_size, noise_size).getBytes(CHARSET); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); return null; } } /** * ? * * @param bytes * ? %08d,%05d * @return int[2] */ public int[] getBlockSizes(byte[] bytes) { if (bytes == null) { return null; } try { String size = new String(bytes, CHARSET); if (!size.matches("\\d+,\\d+")) { return null; } String[] sizes = size.split(","); return new int[] { Integer.valueOf(sizes[0]), Integer.valueOf(sizes[1]) }; } catch (UnsupportedEncodingException | NumberFormatException ex) { ex.printStackTrace(); return null; } } /** * ??AES SecretKey * * @param bits * ? * * @return SecretKey * */ public SecretKey getKey(int bits) { if (bits < 128) { return null; } try { keyGenerator.init(bits); return keyGenerator.generateKey(); } catch (InvalidParameterException ex) { log("?AES SecretKey"); ex.printStackTrace(); return null; } } /** * SecretKeySecretKey * * @param stringKey * SecretKey * * @return SecretKey * */ public SecretKey getSecretKey(String stringKey) { if (stringKey == null || (stringKey = stringKey.trim()).length() == 0) { return null; } byte[] bytes = Base64.decodeBase64(stringKey); return new SecretKeySpec(bytes, 0, bytes.length, "AES"); } /** * ?SecureRandom * * @param size * * @return */ public byte[] getSecureRandom(int size) { byte[] bytes = new byte[size]; secureRandom.nextBytes(bytes); return bytes; } /** * ?SecretKey * * @param secretKey * SecretKey * * @return SecretKey * */ public String getStringKey(SecretKey secretKey) { if (secretKey == null) { return null; } return Base64.encodeBase64String(secretKey.getEncoded()); } /** * ?? * * @param o */ private void log(Object o) { String time = (new Timestamp(System.currentTimeMillis())).toString().substring(0, 19); System.out.println("[" + time + "] " + o.toString()); } /** * ? * * @param content * ? * @return ?? */ public static String simpleEncrypt(String content, String key) { int SIMPLE_ENCRYPT_KEY_LENGTH = 16; byte[] keyBytes = null; byte[] encrypt = null; byte[] allData = null; try { keyBytes = key.getBytes(CHARSET); byte[] _keyBytes = new byte[SIMPLE_ENCRYPT_KEY_LENGTH]; if (keyBytes.length < SIMPLE_ENCRYPT_KEY_LENGTH) { SecureRandom secureRandom = new SecureRandom(); secureRandom.nextBytes(_keyBytes); System.arraycopy(keyBytes, 0, _keyBytes, 0, keyBytes.length); } else { System.arraycopy(keyBytes, 0, _keyBytes, 0, SIMPLE_ENCRYPT_KEY_LENGTH); } keyBytes = _keyBytes; Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding"); IvParameterSpec IVSpec = new IvParameterSpec(keyBytes); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"), IVSpec); encrypt = cipher.doFinal(content.getBytes(CHARSET)); allData = new byte[encrypt.length + keyBytes.length]; System.arraycopy(keyBytes, 0, allData, 0, keyBytes.length); System.arraycopy(encrypt, 0, allData, keyBytes.length, encrypt.length); return encodeBase64(allData); } catch (Exception e) { } finally { keyBytes = encrypt = allData = null; } return ""; } /** * ? * * @param content * ? * @return ?? */ public static String simpleDecrypt(String content, String key) { int SIMPLE_ENCRYPT_KEY_LENGTH = 16; byte[] keyBytes = new byte[SIMPLE_ENCRYPT_KEY_LENGTH]; byte[] decrypt = null; byte[] encrypt = null; byte[] allData = null; try { allData = decodeBase64(content); encrypt = new byte[allData.length - keyBytes.length]; System.arraycopy(allData, 0, keyBytes, 0, keyBytes.length); System.arraycopy(allData, keyBytes.length, encrypt, 0, encrypt.length); byte[] _keyBytes = key.getBytes(CHARSET); if (_keyBytes.length < SIMPLE_ENCRYPT_KEY_LENGTH) { System.arraycopy(_keyBytes, 0, keyBytes, 0, _keyBytes.length); } else { System.arraycopy(_keyBytes, 0, keyBytes, 0, SIMPLE_ENCRYPT_KEY_LENGTH); } Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding"); IvParameterSpec IVSpec = new IvParameterSpec(keyBytes); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES"), IVSpec); decrypt = cipher.doFinal(encrypt); return new String(decrypt, CHARSET); } catch (Exception e) { e.printStackTrace(); } finally { keyBytes = decrypt = encrypt = allData = null; } return ""; } public static String encodeBase64(byte[] b) { return Base64.encodeBase64String(b); } public static byte[] decodeBase64(String base64String) throws Exception { return Base64.decodeBase64(base64String.getBytes(CHARSET)); } }