
package com.mcwl.common.utils.sign;


import com.baidu.aip.util.Base64Util;

import com.mcwl.common.constant.NcountKey;
import com.mcwl.common.exception.NCountDemoException;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

/**
 * 签名 验签 加解密
 */
@Slf4j
public class HnapaySign {

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;


    /**
     * 签名
     *
     * @param privateKey
     * @param merData   明文串
     * @return 签名后的消息
     */
    public static byte[] sign(PrivateKey privateKey, String merData) throws NCountDemoException {

        try {
            return RSAAlgorithms.sign(privateKey, merData);
        } catch (Exception e) {
            throw new NCountDemoException("100F1002", "读取密钥异常", e);
        }

    }

    /**
     * 验证签名
     *
     * @param merData 明文串
     * @param signMsg 签名消息
     * @return 验证签名的结果 true--成功 false--失败
     * @throws NCountDemoException
     */
    public static boolean verify(String merData, String signMsg)
            throws NCountDemoException {
        boolean result = false;
        String hexPublicKey = HexStringByte.byteToHex(Base64Util.decode(NcountKey.NCOUNT_PUBLIC_KEY));
        String signVal = HexStringByte.byteToHex(Base64Util.decode(signMsg));
        result = verifySignatureByRSA(merData, signVal, "UTF-8", hexPublicKey);
        return result;
    }

    /**
     * @param src
     * @param dit
     * @param charsetType
     * @param publicKey
     * @return
     * @throws NCountDemoException
     */
    private static boolean verifySignatureByRSA(String src, String dit, String charsetType, String publicKey)
            throws NCountDemoException {
        if ((src == null) || ("".equals(src.trim()))) {
            throw new NCountDemoException("src is empty ,verifySignatureByRSA无法执行");
        }
        if ((dit == null) || ("".equals(dit.trim()))) {
            throw new NCountDemoException("dit is empty ,verifySignatureByRSA无法执行");
        }
        try {
            return RSAAlgorithms.verify(publicKey, src, dit);
        } catch (Exception e) {
            e.printStackTrace();
            throw new NCountDemoException("验证签名出现异常：请检查输入参数", e.getMessage());
        }
    }

    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data 源数据
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
        if (data == null) {
            throw new Exception("需要加密的数据为空");
        }
        PublicKey publicKey = getPublicKey(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
        KeyFactory keyFactory = KeyFactory.getInstance(NcountKey.ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * @param publicKey 密钥信息
     * @return 返回公钥
     * @throws Exception
     */
    private static PublicKey getPublicKey(String publicKey) throws Exception {
        PublicKey pubKey = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(NcountKey.ALGORITHM);
            pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64Util.decode(publicKey)));
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥无效!", e);
        }
        return pubKey;
    }

    /**
     * hex2byte.
     *
     * @param hexStr hexStr
     * @return byte[]
     */
    public static byte[] hex2byte(String hexStr) {
        byte[] bts = new byte[hexStr.length() / 2];
        for (int i = 0, j = 0; j < bts.length; j++) {
            bts[j] = (byte) Integer.parseInt(hexStr.substring(i, i + 2), 16);
            i += 2;
        }
        return bts;
    }
}
