/**
 * MOMOSEC Security SDK(MSS)
 *
 * This file is part of the Open MSS Project
 *
 * Copyright (c) 2019 - V0ld1ron
 *
 * The MSS is published by V0ld1ron under the BSD license. You should read and accept the
 * LICENSE before you use, modify, and/or redistribute this software.
 *
 * @author V0ld1ron (projectone .at. immomo.com)
 * @created 2019
 */

package com.immomo.rhizobia.rhizobia_J.crypto;

import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


/**
 * @program: java安全编码实践
 *
 * @description: AES 加解密方法
 *
 * 知识点1:oracle官方已经在如下版本去除了aes-256的限制,6u181,7u171,8u161,9 b148,openjdk7u
 *             https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8170157
 *
 * 知识点2:之所以没有用base64或16进制处理加密后的内容,是因为在使用base64编码后的内容中,可能存在'+'字符,
 *             '+'字符返回给前端后再返回给后端时,如果不经过处理,会变为' '空格字符,
 *             所以在对加密内容进行base64编码时,请注意'+'字符
 *
 * @author: V0ld1ron
 *
 **/

public class AESUtils {
    private static AESUtils instance = null;
    private String aesKey = "";
    private String secretKey = "";

    private String iVector = "";
    private String aesMode = "AES/CBC/PKCS5Padding";

    private AESUtils() {

    }

    private AESUtils(String aesKey, String secretKey, String aesMode) {
        this.secretKey = secretKey;
        this.aesKey = DigestUtils.md5Hex(DigestUtils.sha256Hex(aesKey + "|" + secretKey) + "|" + secretKey);
        this.iVector = this.aesKey.substring(8, 24);
        this.aesMode = aesMode == null ? "AES/CBC/PKCS5Padding" : aesMode;
    }

    public static AESUtils getInstance(String aesKey, String secretKey, String aesMode) {
        if (instance == null) {
            synchronized (AESUtils.class) {
                if (null == instance) {
                    instance = new AESUtils(aesKey, secretKey, aesMode);
                }
            }
        } else {
            instance.secretKey = secretKey;
            instance.aesKey = DigestUtils.md5Hex(DigestUtils.sha256Hex(aesKey + "|" + secretKey) + "|" + secretKey);
            instance.iVector = instance.aesKey.substring(8, 24);
            instance.aesMode = aesMode == null ? "AES/CBC/PKCS5Padding" : aesMode;
        }
        return instance;
    }

    public String getaesKey() {
        return aesKey;
    }

    public String getsecretKey() {
        return secretKey;
    }

    public void setiVector(String iVector) {
        this.iVector = iVector;
    }

    public String getaesMode() {
        return aesMode;
    }

    public void setaesMode(String aesMode) {
        this.aesMode = aesMode;
    }

    /**
     * @Description: AES 加密
     * @Param: sSrc 待加密数据
     * @return: byte[] 加密后byte流
     */
    public byte[] encrypt(String sSrc) throws Exception {
        byte[] raw = aesKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance(aesMode);
        IvParameterSpec iv = new IvParameterSpec(iVector.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes());

        return encrypted;
    }

    /**
     * @Description: AES 解密
     * @Param: encrypted 待解密二进制流
     * @return: String 解密后数据
     */
    public String decrypt(byte[] encrypted) throws Exception {
        String originalString = null;
        byte[] raw = aesKey.getBytes("ASCII");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance(aesMode);
        IvParameterSpec iv = new IvParameterSpec(iVector.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(encrypted);
        originalString = new String(original);

        return originalString;
    }

}