package com.njcn.common.utils.sm;


import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;


/**
 * @author yexibao
 */
@Slf4j
public class Sm2 {


    /**
     * SM2解密密碼
     *
     * @param privateKey SM2私有秘钥
     * @param encryptedData 需要解密字符串
     */
    public static String decrypt(String privateKey, String encryptedData) {
        byte[] encryptedDataByte = Hex.decode(encryptedData);
        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, getSm2EcDomain());
        //用私钥解密
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);
       //processBlock得到Base64格式，记得解码
        byte[] arrayOfBytes = new byte[encryptedDataByte.length];
        try {
            arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(encryptedDataByte, 0, encryptedDataByte.length));
        } catch (InvalidCipherTextException e) {
            log.error("SM2解密byte转换出错，异常为：" + e.toString());
        }
        //得到明文
        return new String(arrayOfBytes);
    }

    /**
     * 获取SM2秘钥队证书
     */
    public static ECDomainParameters getSm2EcDomain() {
        ECDomainParameters domainParameters ;
        //生成密钥对
        X9ECParameters sm2EcParameters = GMNamedCurves.getByName("sm2p256v1");
        domainParameters = new ECDomainParameters(sm2EcParameters.getCurve(), sm2EcParameters.getG(), sm2EcParameters.getN());
        //返回证书
        return domainParameters;
    }

    /**
     * 获取SM2秘钥对
     */
    public static Map<String, String> getSm2CipHer() throws NoSuchAlgorithmException {
        //秘钥队
        Map<String, String> resultMap = new HashMap<>();
        //生成密钥对
        ECDomainParameters domainParameters = getSm2EcDomain();
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();

        //私钥，16进制格式，自己保存，格式如a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853
        BigInteger privateKey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
        //公钥，16进制格式，发给前端，格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
        ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();

        resultMap.put("privateKey", privateKey.toString(16));
        resultMap.put("publicKey", Hex.toHexString(ecPoint.getEncoded(false)));
        return resultMap;
    }


    /**
     * SM2密码有效性，传输完整性验证
     */
    public static String getPasswordSM2Verify(String privateKey, String strPwd) {
        String strResultPwd = null;
        try {
            String password = Sm2.decrypt(privateKey, strPwd);
            //数据完整性判断
            String passwordIntegrity = Sm3.getPasswordIntegrity(password);
            //数据完整性未破坏
            if (passwordIntegrity != null && passwordIntegrity.length() != 0) {
                strResultPwd = passwordIntegrity;
            }
        } catch (Exception e) {
            strResultPwd = null;
            log.error("解析密码有效性出错，异常为：" + e.toString());
        }
        return strResultPwd;
    }

}
