package com.allinpay.demo.util;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.RSAPublicKeySpec;
import java.util.Enumeration;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.allinpay.demo.AIPGException;


/**
 * @Description
 * @Author meixf@allinpay.com
 * @Date 2018年5月23日
 **/
public class AIPGSignature {

    public static final String SHA1_RSA = "SHA1withRSA";
    public static final String SM3_SM2 = "SM3withSM2";

    private static final String BC = "BC";

    private String encoding = "GBK";
    private String algorithm = SHA1_RSA;
    private boolean useBC = false;
    static{
    	  try{
    	   Security.addProvider(new BouncyCastleProvider());
    	  }catch(Exception e){
    	   e.printStackTrace();
    	  }
    }

    public AIPGSignature(String algorithm){
        this.algorithm = algorithm;
    }

    public AIPGSignature(String algorithm, String encoding){
        this.algorithm = algorithm;
        this.encoding = encoding;
    }

    public AIPGSignature(String algorithm, boolean useBC){
        this.algorithm = algorithm;
        this.useBC = useBC;
    }

    public AIPGSignature(String algorithm, String encoding, boolean useBC){
        this.algorithm = algorithm;
        this.encoding = encoding;
        this.useBC = useBC;
    }

    public AIPGSignature(Provider prvd) {
		// TODO Auto-generated constructor stub
	}

	/**
     * @param msg  签名报文
     * @param keyfile
     * @param pass
     * @return  签名字符串
     * @throws Exception
     */
    public String signMsg(String msg, File p12File, String pass) throws Exception{
        String signedStr = null;

        KeyStore ks = useBC ? KeyStore.getInstance("PKCS12", BC) : KeyStore.getInstance("PKCS12");
        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("没有找到匹配私钥");
        }

        Signature sign = useBC ? Signature.getInstance(algorithm, BC) : Signature.getInstance(algorithm);
        sign.initSign(prikey);
        sign.update(msg.getBytes(encoding));
        byte signed[] = sign.sign();
        byte sign_asc[] = new byte[signed.length * 2];
        hex2Ascii(signed.length, signed, sign_asc);
        signedStr = new String(sign_asc);

        return signedStr;
    }

    /**
     *
     * @param signedStr  签名字符串
     * @param msg   报文
     * @param pkfile
     * @return
     * @throws AIPGException
     */
    public boolean verifyMsg(String signedStr, String msg, File crtfile) throws Exception{
        FileInputStream certfile = new FileInputStream(crtfile);
        CertificateFactory cf = useBC ? CertificateFactory.getInstance("X.509", BC)
                : CertificateFactory.getInstance("X.509");
        X509Certificate x509cert = null;
        try {
            x509cert = (X509Certificate) cf.generateCertificate(certfile);
        } catch (Exception ex) {
            if (certfile != null)
                certfile.close();
            throw new Exception("加载证书时出错" + ex.getMessage(), ex);
        }
        PublicKey pubkey = (PublicKey) x509cert.getPublicKey();
        Signature verify = useBC ? Signature.getInstance(algorithm, BC):Signature.getInstance(algorithm);
        verify.initVerify(pubkey);
        //TEST-----------证书位数
//        RSAPublicKeySpec keySpec = KeyFactory.getInstance("RSA", BC).getKeySpec(pubkey, RSAPublicKeySpec.class);
//        System.out.println(keySpec.getModulus().toString(2).length());
        //TEST-----------
        signedStr = signedStr.toLowerCase();
        byte signeddata[] = new byte[signedStr.length() / 2];
        ascii2Hex(signedStr.length(), signedStr.getBytes(encoding), signeddata);
        verify.update(msg.getBytes(encoding));
        return verify.verify(signeddata);
    }

    /**
     * 将十六进制数据转换成ASCII字符串
     * @param len  十六进制数据长度
     * @param data_in 待转换的十六进制数据
     * @param data_out 已转换的ASCII字符串
     */
    private static void hex2Ascii(int len, byte data_in[], byte data_out[]) {
        byte temp1[] = new byte[1];
        byte temp2[] = new byte[1];
        for (int i = 0, j = 0; i < len; i++) {
            temp1[0] = data_in[i];
            temp1[0] = (byte) (temp1[0] >>> 4);
            temp1[0] = (byte) (temp1[0] & 0x0f);
            temp2[0] = data_in[i];
            temp2[0] = (byte) (temp2[0] & 0x0f);
            if (temp1[0] >= 0x00 && temp1[0] <= 0x09) {
                (data_out[j]) = (byte) (temp1[0] + '0');
            } else if (temp1[0] >= 0x0a && temp1[0] <= 0x0f) {
                (data_out[j]) = (byte) (temp1[0] + 0x57);
            }
            if (temp2[0] >= 0x00 && temp2[0] <= 0x09) {
                (data_out[j + 1]) = (byte) (temp2[0] + '0');
            } else if (temp2[0] >= 0x0a && temp2[0] <= 0x0f) {
                (data_out[j + 1]) = (byte) (temp2[0] + 0x57);
            }
            j += 2;
        }
    }

    /**
     * 将ASCII字符串转换成十六进制数据
     * @param len ASCII字符串长度
     * @param data_in 待转换的ASCII字符串
     * @param data_out 已转换的十六进制数据
     */
    private static void ascii2Hex(int len, byte data_in[], byte data_out[]) {
        byte temp1[] = new byte[1];
        byte temp2[] = new byte[1];
        for (int i = 0, j = 0; i < len; j++) {
            temp1[0] = data_in[i];
            temp2[0] = data_in[i + 1];
            if (temp1[0] >= '0' && temp1[0] <= '9') {
                temp1[0] -= '0';
                temp1[0] = (byte) (temp1[0] << 4);
                temp1[0] = (byte) (temp1[0] & 0xf0);
            } else if (temp1[0] >= 'a' && temp1[0] <= 'f') {
                temp1[0] -= 0x57;
                temp1[0] = (byte) (temp1[0] << 4);
                temp1[0] = (byte) (temp1[0] & 0xf0);
            }
            if (temp2[0] >= '0' && temp2[0] <= '9') {
                temp2[0] -= '0';
                temp2[0] = (byte) (temp2[0] & 0x0f);
            } else if (temp2[0] >= 'a' && temp2[0] <= 'f') {
                temp2[0] -= 0x57;
                temp2[0] = (byte) (temp2[0] & 0x0f);
            }
            data_out[j] = (byte) (temp1[0] | temp2[0]);
            i += 2;
        }
    }

    public static void main(String[] args) throws Exception{
        AIPGSignature signature = new AIPGSignature(AIPGSignature.SHA1_RSA, true);
/*        String signedStr = signature.signMsg("6225487875454545454||3000||TC20240410175645227300000007|0|100", new File("config/allinpay-pds-ec.pfx"), "aipg02");
        System.out.println(signedStr);*/
        System.out.println(signature.verifyMsg("3d804c643f32bd5d0df208cd3a17756aa238f0929860c86f87a52c1cd3e7e9c6f938087354b259f638611d0eff11ba3578ac34d6db6b1b900e3014496e080a06b55ad41c679c86a50d7a391f43dfc951bddcd97b592a6a24b1a4b57e13766b927771f5d15aa9df384f99228769da60f4c45815ea46420fb1f5a47320c4c43997d1fe48a7bafad99c0b49e89b25215a1354b78cba208313dfc43105fa37e5393abd0ef2b66320fcf8f9a197e26417515badaf3945fdff658ec417de475e6be64d1b5ad44cbe00e8af4c0e2d5d84b05c631372d5e68688b474b41eb26b8bb4a9cf9044ca9c5062df19a1c73c43d0a0b02a2fe743e62009eef2c108b24229596e58", "6217003360011605523||1|20240420120804917439|0|0", new File("config/allinpay-pds.cer")));
    }

}
