package com.allinpay.demo.trx.filedownload;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

import javax.crypto.Cipher;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;



/**
 * @Description
 * @Author meixf@allinpay.com
 * @Date 2024年11月17日
 **/
public class AipgCipherUtil {

    private static final String BC = "BC";
    static{
        if(Security.getProvider(BC) == null){
            System.out.println("AipgCipher->BC为空，初始化");
            Security.addProvider(new BouncyCastleProvider());//密码不能用特殊符号
        }
    }
    
    public static PrivateKey loadPriKeyFromPfx(File p12File, String pass)throws Exception{
        KeyStore ks = KeyStore.getInstance("PKCS12", BC);
        FileInputStream fiKeyFile = new FileInputStream(p12File);
        try {
            ks.load(fiKeyFile, pass.toCharArray());
        } catch (Exception ex) {
            if (fiKeyFile != null)
                fiKeyFile.close();
            throw new Exception("加载证书时出错" + ex.getMessage(), ex);
        }
        Enumeration<String> myEnum = ks.aliases();
        String keyAlias = null;
        PrivateKey prikey = null;
        while (myEnum.hasMoreElements()) {
            keyAlias = (String) myEnum.nextElement();
            if (ks.isKeyEntry(keyAlias)) {
                prikey = (PrivateKey) ks.getKey(keyAlias, pass.toCharArray());
                break;
            }
        }
        if (prikey == null) {
            throw new Exception("没有找到匹配私钥");
        }
        return prikey;
    }
    
    public static PublicKey loadPubKeyFromCrt(File certFile)throws Exception{
        FileInputStream certfile = null;
        CertificateFactory cf = CertificateFactory.getInstance("X.509", BC);
        X509Certificate x509cert = null;
        try {
            certfile = new FileInputStream(certFile);
            x509cert = (X509Certificate) cf.generateCertificate(certfile);
        } catch (Exception ex) {
            throw new Exception("加载证书时出错" + ex.getMessage(), ex);
        }finally{
            if (certfile != null) certfile.close();
        }
        PublicKey pubkey = (PublicKey) x509cert.getPublicKey();
        if(pubkey == null){
            throw new Exception("加载证书失败");
        }
        return pubkey;
    }
    
    public static String encryptBySm2(String plainStr, File certFile) throws Exception{
        PublicKey publicKey = loadPubKeyFromCrt(certFile);
        byte[] data = plainStr.getBytes(StandardCharsets.UTF_8);
        
        BCECPublicKey key = (BCECPublicKey) publicKey;
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(key.getQ().getEncoded(false));
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
        byte[] cipherBytes = null;
        try {
            cipherBytes = sm2Engine.processBlock(data, 0, data.length);
        } catch (Exception e) {
            System.out.println("SM2加密时出现异常:" + e.getMessage());
            throw e;
        }
        return Hex.toHexString(cipherBytes);
    }
    
    public static String decryptSm2Data(String cipherStr, File p12File, String pass)throws Exception{
        PrivateKey privateKey = loadPriKeyFromPfx(p12File, pass);
        byte[] cipherBytes = Hex.decode(cipherStr);
        
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(Hex.toHexString(key.getD().toByteArray()), 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);
        String result = "";
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherBytes, 0, cipherBytes.length);
            result = new String(arrayOfBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            System.out.println("SM2解密时出现异常:" + e.getMessage());
            throw e;
        }
        return result;
    }

    public static String encryptByRSA(String plainStr, File certFile) throws Exception{
        PublicKey publicKey = loadPubKeyFromCrt(certFile);
        byte[] data = plainStr.getBytes(StandardCharsets.UTF_8);
        
        Cipher cipher = Cipher.getInstance("RSA", BC);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherBytes = cipher.doFinal(data);
        return Hex.toHexString(cipherBytes);
    }
    
    public static String decryptRSAData(String cipherStr, File p12File, String pass)throws Exception{
        PrivateKey privateKey = loadPriKeyFromPfx(p12File, pass);
        byte[] cipherBytes = Hex.decode(cipherStr);
        
        Cipher cipher = Cipher.getInstance("RSA", BC);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] plainBytes = cipher.doFinal(cipherBytes);
        return new String(plainBytes, StandardCharsets.UTF_8);
    }
    
}
