press.gfw.Encrypt.java Source code

Java tutorial

Introduction

Here is the source code for press.gfw.Encrypt.java

Source

/**
* 
*    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 press.gfw;

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.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

    public static void main(String[] args) {

        Encrypt aes = new Encrypt();

        // 
        // aes.testEncryptFile();

        // 
        // aes.testSecureRandom();

        aes.testIsPassword();

    }

    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"); // Advanced Encryption Standard - Cipher Feedback Mode - No Padding

            keyGenerator = KeyGenerator.getInstance("AES");

        } catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {

            throw new RuntimeException(ex);

        }

    }

    /**
     * 
     * 
     * @param key
     *          SecretKey
     * @param encrypt_bytes
     *          ?16IV?
     * 
     * @return
     *             ?
     * 
     */
    public byte[] decrypt(SecretKey key, byte[] encrypt_bytes) {

        if (key == null || encrypt_bytes == null || encrypt_bytes.length < IV_SIZE) {

            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
     *             ?
     * 
     */
    public 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 src
     *          
     * @param dest
     *          ?
     * @return
     *             ??
     */
    public boolean decryptFile(SecretKey key, File src, File dest) {

        if (src == null || !src.exists() || src.isDirectory() || !src.canRead() || dest == null) {

            return false;

        }

        long len = src.length(); // ?

        if (len < ENCRYPT_SIZE) {

            return false;

        }

        byte[] encrypt_size_bytes = null;

        byte[] encrypt_block_bytes = null;

        InputStream bis = null;

        OutputStream bos = null;

        boolean close = true;

        try {

            bis = new BufferedInputStream(new FileInputStream(src));

            bos = new BufferedOutputStream(new FileOutputStream(dest));

            for (;;) {

                encrypt_size_bytes = new byte[ENCRYPT_SIZE];

                int read_size = bis.read(encrypt_size_bytes); // ?

                if (read_size == -1) {

                    break;

                }

                if (read_size != encrypt_size_bytes.length) {

                    return false;

                }

                byte[] size_bytes = decrypt(key, encrypt_size_bytes);

                if (size_bytes == null) {

                    return false;

                }

                int block_size = getBlockSize(size_bytes);

                if (block_size == 0) {

                    return false;

                }

                encrypt_block_bytes = new byte[block_size];

                for (int read_count = 0; read_count < block_size;) {

                    read_size = bis.read(encrypt_block_bytes, read_count, block_size - read_count); // ?

                    if (read_size == -1) {

                        return false;

                    }

                    read_count += read_size;

                }

                byte[] block_bytes = decrypt(key, encrypt_block_bytes); // ?

                if (block_bytes == null) {

                    return false;

                }

                bos.write(block_bytes);

            }

        } catch (IOException ex) {

            log("");

            ex.printStackTrace();

            return false;

        } finally {

            if (bos != null) { // ?

                try {

                    bos.close();

                } catch (IOException ex) {

                    log("?");

                    ex.printStackTrace();

                    close = false;

                }

            }

            if (bis != null) { // ?

                try {

                    bis.close();

                } catch (IOException ex) {

                    log("?");

                    ex.printStackTrace();

                    close = false;

                }

            }

            encrypt_block_bytes = null;

        }

        if (!close || !dest.exists() || dest.length() < len) {

            return false;

        }

        return true;

    }

    /**
     * 
     * 
     * @param key
     *          SecretKey
     * @param data
     *          ?
     * 
     * @return
     *             ?
     * 
     */
    public 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 src
     *          
     * @param dest
     *          
     * 
     * @return
     *             ??
     * 
     */
    public boolean encryptFile(SecretKey key, File src, File dest) {

        if (src == null || !src.exists() || src.isDirectory() || !src.canRead() || dest == null) {

            return false;

        }

        long len = src.length(); // ?

        if (len == 0L) {

            return false;

        }

        byte[] block_bytes = new byte[BLOCK_MAX_FILE];

        byte[] read_bytes = null;

        InputStream bis = null;

        OutputStream bos = null;

        boolean close = true;

        try {

            bis = new BufferedInputStream(new FileInputStream(src));

            bos = new BufferedOutputStream(new FileOutputStream(dest));

            for (;;) {

                int read_size = bis.read(block_bytes); // ?

                if (read_size == -1) {

                    break;

                }

                read_bytes = new byte[read_size];

                System.arraycopy(block_bytes, 0, read_bytes, 0, read_size); // ??

                byte[] cipher_bytes = encrypt(key, read_bytes); // ?

                if (cipher_bytes == null) {

                    return false;

                }

                byte[] size_cipher = encrypt(key, getBlockSizeBytes(cipher_bytes.length)); // ?24

                if (size_cipher == null) {

                    return false;

                }

                bos.write(size_cipher);

                bos.write(cipher_bytes);

            }

        } catch (IOException ex) {

            log("");

            ex.printStackTrace();

            return false;

        } finally {

            if (bos != null) { // ?

                try {

                    bos.close();

                } catch (IOException ex) {

                    log("?");

                    ex.printStackTrace();

                    close = false;

                }

            }

            if (bis != null) { // ?

                try {

                    bis.close();

                } catch (IOException ex) {

                    log("?");

                    ex.printStackTrace();

                    close = false;

                }

            }

            block_bytes = null;

            read_bytes = null;

        }

        if (!close || !dest.exists() || dest.length() < len) {

            return false;

        }

        return true;

    }

    /**
     * ?
     * 
     * @param key
     *          SecretKey
     * 
     * @param bytes
     *          ?
     * 
     * @return
     *             [?+?]? + [? + ?]
     * 
     */
    public byte[] encryptNet(SecretKey key, byte[] bytes) {

        if (key == null || bytes == null || bytes.length == 0) {

            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;

        }

        // long start = System.currentTimeMillis();

        // ?
        byte[] noise_bytes = (cipher_bytes.length < NOISE_MAX / 2)
                ? getSecureRandom(secureRandom.nextInt(NOISE_MAX))
                : new byte[0];

        // long end = System.currentTimeMillis();

        // log("" + noise_bytes.length);

        // log("" + (end - start));

        // [IV+?+?]30
        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 | NumberFormatException 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;

        }

    }

    /**
     * ?256?SecretKey
     * 
     * @return
     *             256?SecretKey
     * 
     */
    public SecretKey getKey() {

        return getKey(256);

    }

    /**
     * ??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;

        }

    }

    /**
     * ??SecretKey
     * 
     * @param password
     *          ??isPassword()?
     * 
     * @return
     *             SecretKey
     * 
     */
    public SecretKey getPasswordKey(String password) {

        if (!isPassword(password)) {

            return null;

        }

        try {

            return getSecretKey(DigestUtils.md5Hex(password.getBytes(CHARSET)));

        } catch (UnsupportedEncodingException ex) {

            log("??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());

    }

    /**
     * ???
     * 
     * 1?
     * 2??
     * 3???
     * 4????
     * 5???
     * 
     * @param password
     * @return
     */
    public boolean isPassword(String password) {

        if (password == null || !password.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=\\S+$).{8,}$")) {

            return false;

        }

        return true;

        /*
         * 2??
         * 3???
         * 4????
         * 5???
         */
        // return password.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$");

    }

    /**
     * ??
     * 
     * @param o
     */
    private void log(Object o) {

        String time = (new Timestamp(System.currentTimeMillis())).toString().substring(0, 19);

        System.out.println("[" + time + "] " + o.toString());

    }

    public void test() {

        byte[] bytes = getBlockSizeBytes(888, 0);

        byte[] size_bytes = encrypt(getKey(), bytes);

        log("" + size_bytes.length);

        log(new String(bytes));

        int[] sizes = this.getBlockSizes(bytes);

        System.out.println(sizes[0]);

        System.out.println(sizes[1]);

    }

    public void testEncryptFile() throws Exception {

        SecretKey key = getPasswordKey("abc123");

        log("");

        String ext = ".msi";

        File f1 = new File("d:/" + 1 + ext);

        File f2 = new File("d:/" + 2 + ext);

        File f3 = new File("d:/" + 3 + ext);

        long start = System.currentTimeMillis();

        encryptFile(key, f1, f2);

        long end = System.currentTimeMillis();

        log("" + (end - start));

        log("?" + f1.length() * 1000 / (end - start) / 1024 / 1024 + "M");

        log("");

        start = System.currentTimeMillis();

        decryptFile(key, f2, f3);

        end = System.currentTimeMillis();

        log("" + (end - start));

        log("?" + f2.length() * 1000 / (end - start) / 1024 / 1024 + "M");

    }

    public void testIsPassword() {

        String password = "xxXxab12";

        log(isPassword(password));

    }

    public void testSecureRandom() {

        // secureRandom.nextBytes(bytes);

        // secureRandom.nextBytes(bytes);

        long start = System.currentTimeMillis();

        byte[] bytes = this.getSecureRandom(1024);

        long end = System.currentTimeMillis();

        log("" + (end - start));

        try {

            log(new String(bytes, "UTF-8"));

        } catch (UnsupportedEncodingException ex) {

            ex.printStackTrace();

        }

    }

}