package com.bootdo.data.util;

import cn.com.iwall.config.InitConfig;
import cn.com.iwall.dto.EnterpriseInfo;
import cn.com.iwall.dto.ServerInfo;
import cn.com.iwall.generator.CertRequstGenerator;
import cn.com.iwall.generator.ServerCertGenerator;
import cn.com.iwall.util.Util;
import com.alibaba.fastjson.JSONObject;
import com.bootdo.data.domain.CaDetail;
import com.bootdo.data.domain.CaInfo;
import com.bootdo.data.service.impl.CaInfoServiceImpl;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.util.Date;
import java.util.Random;
import java.util.UUID;

/**
 * 证书申请工具
 *
 * @author Mr.xiBel
 * @date 2022/5/23
 **/
@Component
public class ApplyCertUtils {

    private static Logger log = LoggerFactory.getLogger(ApplyCertUtils.class);

    /**
     * # ca 证书文件目录
     */
    @Value("${ca.file-path}")
    private String caFilePath;

    /**
     * 信长城服务地址
     */
    @Value("${ca.ra-url}")
    private String raUrl;

    /**
     * ra授信平台id，通过ra授信功能获取
     */
    @Value("${ca.platform-id}")
    private String platformId;

    /**
     * ra授信模板id，通过ra授信功能获取
     */
    @Value("${ca.template-id}")
    private String templateId;

    /**
     * 通信服务器私钥
     */
    @Value("${ca.server-private-key}")
    private String serverPrivateKey;
//    private final static String SERVER_PRIVATE_KEY =
//            "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDo3k3I6tzHW7SsD+zjeGtP9JEtlwACDOMvaWoWrFigL3SFAga7uaPsy2bZFuHzbO3VT+UAO0YP3Dlb/8eJBWKtd2LUJDJA0ZUi6Ycr5Et0vJyb2ulJ61G/oOPQj9hWaesrZ4UwnrlAvDS6Y0OryWf7aIQGu8TKzaH8h1rdDy7iiwuUd6zk8eyvWFPdeY4XXP9aFwWwKyYsIBbheyl59iyYDvyTIzp7r9uw4EVExR4NAVu89X79O/Xkn4f3snUR4mWrtOzB8N3sdgRkMyE+GVCYztvPSvSMLAl69AjaXgVoNiuc1K0EtmINJhufX1OA6Ex+UrJ/zQ8cUaW470jzOoyRAgMBAAECggEAEz7Zfjhtj2BfOd/bESBQ+3utSppHMDmGcShXfJDj24vwfP73itOYiP0f0+NAlfteW0R4xaB1yuEa2yVJhWPXjZnnULxpFtL7bguPgKt5J6hgPRVGNeC9PIbR19kmOltonzQKsu5Qmy6yy5C2oApLjgtLlg7gqxjRlsBNByYLwZcvPAusSEPGHHVPT0aiNNJAqhkb8A3QYGX6DAoAA/5sSpYbMk1jxOwidHES3uhd+GGa0OhSpJs0nm40wJXxJySoaQa3dgJuXEkL1gklCFn1OPVZj2H2HInIQvf4zrRKxDKfbf9tXTalaTEIiYyMs6HQATgIZRZ3U31D27q3dwiqXQKBgQD4t85ZQeyT+BaUTOy4R36W3TKDjUfSn5DB/ro87f6AzLXdAyRKN17/uo6eqe1evI6KsRBB7sdNAqpo5KUUqrQxlEmkhY8nnsT/PBNJGNXMlWFaKWThhJ9414MoVdckMJyzRBc3eKuq4W9NRvBOYgk6zsfw83ZISzCQQMY4F/12owKBgQDvr7OZXtzm5F/d4Xg0gij798ZuQwIPNdw1tMtqBLUX71hIZTmvU5Y5wT9cRkaCawP01TxGCkGehHYAvZC4mC/Y6uPUq+Y9ahgNB7/IsroHHPiEp3MWjPtMnvSdFizpy9Fy5cy5Z319yJxt9iKg+XHz4EkfvZRPpkujDuQdFANHOwKBgQDf6ssRvLI6/S2G0kKEHYmwVPB3UyiITTPME4Kj/DgWyTYqBfwT/Qm//bsMyTt1ARjrcEGMumYfxPiaes7MbmrQAArU3ZFmQCBnETTjqEJDb3E39PBnwDi6WHwyQiXw5PsOwYyNWkxjCZZk4irSxpw8n5GOMgIZT+gm1soeEisVNQKBgQDve0Veg4sB48LpheM9y6M5hmqsNqVzHySEQL1Rk5yBarJxuzo0tLaPxEcTYmAG5PgSA5ByhsLjqIPi34Ue+2grOx4imxKz4DJhqpFz7Sez/b1NhaWXLYvVFalg3wxHGU+Plo3D3eRj7jJmKByXbETwH/PQE/KA1q4SIMnJR/qyiwKBgQDyh4zEfoKF3syYicxcUwPLQ6gBxaIAmWZByI1m3WFyHuZHafe2hDJ3xtUnv94xR8yHRxLV0lI5LaT24gujzHbUv41eqJBUUy8sn6/Cj2aJB+Mgf4ZW5oFH88+jvaFqBYWXY95KhuK8/LW8KQHTDR0+gizV39/Ozbf2NFLOr/lflQ==";
//            "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDX3tL+vCqL6seOD3ztu+ypMZh92zEIWB8yFrLJo77riG7Nq95+QtIRBzh+Rh3Z5V+ebgr0TCuKykfH/cp9PbYoIz6/qyQm6jMKyUP/eOdFZIu0yX7sH3mpYjwO8FSUgYp/QBd6TtUuWL+JuDDLb9R0ZPJ4z0d234hv3l4o/ILG+QkORrEUyXBLrAJf4GVQyMYGOQPkbtTiusgsemLg0XNdBKiEeuCq2IMXrVFzFQwTMky5X0JySYNtK5RqZichD/olc7nOSIu0axBkhmks6Kf/4BxsTLgVDbiP1A4vQux3RJPxjbyujXTCFepyqiIBKHBUGM/wyvjUVpV+XS7C8CAdAgMBAAECggEALWZ2Yy9BVnxp/ElrGZeyqhHdc6jACvR3mSH3d5t52ZDv1s8tA8p7Yivt2HbYK93HCquzn5YOeUnaJ2hLqCPstcvV1FtCxvFuCzhxld8AatJ+5GaVTEVBYdugbHbknvgQn/sjhOxj/vOY0vbbQknyeYlKiphze/9+syJmFMIoof5WwlHzMc4ej3mKPU1GibkKu8HIJAdkyESRAWfr+3dbIxV0LRI26/m/fVN7V/2OXVjA3tLn7BpcXDLz5jt/teqiimDi8N4gS0pzB32gv8obBxvbOcCQqq9fH6DUIf/RMGN0Wnux5H2Gn9af2NDdErBaRMxE/kBjGWIyGC50odtyoQKBgQD3pJOpHds9Nz/Wa9+wT9Yl+pNWKEVsnj5sEKw+cZDEZZGaUspC/NeymZFJPfkYLod2+GMQ996lO2o3cYzXL7PVYd8ylnWbGyDR0pCGmg9C7FGD7BryTZDSAEfaDeoYo5nvoTvlaR4yrCPY/hMx+tfgDmUgzuiAol60MSZqBZccvQKBgQDfJ8KmpSt06AVywLLPIIVU40q1bFetQ6OMiMalO6UsFidsxytq+c1bmuJ5mQfUzaqyP+aCUXQYS4na6fk8c6UeaDN46EOe8DVZIrx3SpQjdSvFOlIvNw1aZvdJoJt4L+h1Xvfs5XfRe7ChkQfYuAjOeKqN+Vr4k+/R13aP1lA24QKBgBnMCCHG3W7MEZdZl2y7dFhyeOSxv0GoXmJNflPm6qi61+cWlsFRq3qMaskRbqBpQG1RRKig2Y2m2dK6vmwwKPCiiGixqcClm86fu7R52fsjuP4gG1KAaASoSNPbPTah3EhNKHsmbyHiMr3JAeDj+5b4iOpZA3la8LieohzLRkmVAoGBAN06YquotUNJvhT8Wogi2mvEhU0EoosfsiThd+VZEy94qw8ZCL8d9Otqyjj4cB4U4rOhy9jH/YsUYA9Jw4+dqkVR0i18PYV05759RFAS/HeGcqUtY0pxjmhgCanRci+ddxvc0XbmKnnf4MTbrea40+o1EAh9+yTSLH9g1qZUNeOhAoGBAIAcRzlzQiM1ypeM1uNaJsgH+F966mbs2w8PiH+NnnpwOWjNNw/cubHXUVABh+g85q+3lw9mFd5cTjmUyYlCKm2rCx3ap5ggzTuSBKrI3d6cR1kI3bm8xnn7A/Wa99lBFiKXNBirCRmt4LP65l+k9JT/wFHZgPpRQBmK4ViGchSW";
    /**
     * 通信服务器公钥
     */
    @Value("${ca.server-public-key}")
    private String serverPublicKey;
//    final static String SERVER_PUBLIC_KEY =
//            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6N5NyOrcx1u0rA/s43hrT/SRLZcAAgzjL2lqFqxYoC90hQIGu7mj7Mtm2Rbh82zt1U/lADtGD9w5W//HiQVirXdi1CQyQNGVIumHK+RLdLycm9rpSetRv6Dj0I/YVmnrK2eFMJ65QLw0umNDq8ln+2iEBrvEys2h/Ida3Q8u4osLlHes5PHsr1hT3XmOF1z/WhcFsCsmLCAW4XspefYsmA78kyM6e6/bsOBFRMUeDQFbvPV+/Tv15J+H97J1EeJlq7TswfDd7HYEZDMhPhlQmM7bz0r0jCwJevQI2l4FaDYrnNStBLZiDSYbn19TgOhMflKyf80PHFGluO9I8zqMkQIDAQAB";
//            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA197S/rwqi+rHjg987bvsqTGYfdsxCFgfMhayyaO+64huzavefkLSEQc4fkYd2eVfnm4K9EwrispHx/3KfT22KCM+v6skJuozCslD/3jnRWSLtMl+7B95qWI8DvBUlIGKf0AXek7VLli/ibgwy2/UdGTyeM9Hdt+Ib95eKPyCxvkJDkaxFMlwS6wCX+BlUMjGBjkD5G7U4rrILHpi4NFzXQSohHrgqtiDF61RcxUMEzJMuV9CckmDbSuUamYnIQ/6JXO5zkiLtGsQZIZpLOin/+AcbEy4FQ24j9QOL0Lsd0ST8Y28ro10whXqcqoiAShwVBjP8Mr41FaVfl0uwvAgHQIDAQAB";

    public static ApplyCertUtils getInstance() {
        return new ApplyCertUtils();
    }


    public CaDetail applyCertByCsr(CaInfo caInfo) throws Exception {
        // 证书详情信息
        CaDetail caDetail = new CaDetail();
        // 初始化配置
        InitConfig initConfig = new InitConfig(serverPrivateKey, serverPublicKey, platformId);
        CertRequstGenerator applyCertRequstGenerator = new CertRequstGenerator(initConfig);
        // 生成秘钥对
        KeyPair kp = Util.generateKeyPair("SM2", 256);
        String base64PrivateKey = Base64.toBase64String(kp.getPrivate().getEncoded());
        caDetail.setKeyPairPrivate(base64PrivateKey);
        log.info("base64PrivateKey: " + base64PrivateKey);

        String base64PublicKey = Base64.toBase64String(kp.getPublic().getEncoded());
        caDetail.setKeyPairPublic(base64PublicKey);
        log.info("base64PublicKey: " + base64PublicKey);

        // 相关企业信息
        EnterpriseInfo enterpriseinfo = new EnterpriseInfo();
        String commonName = caInfo.getCompanyName();
        enterpriseinfo.setCompanyName(commonName);
        enterpriseinfo.setOrganizationCode(caInfo.getUniformSocialCreditCode());
        enterpriseinfo.setTaxationNum(caInfo.getUniformSocialCreditCode());
        enterpriseinfo.setConnectPhone(caInfo.getConnectPhone());
        enterpriseinfo.setConnectName(caInfo.getConnectName());
        enterpriseinfo.setConnectEmail(caInfo.getConnectEmail());
        enterpriseinfo.setConnectAddress(caInfo.getConnectAddress());

        X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
                // 国家(c)
                .addRDN(BCStyle.C, "CN")
                // 省份(st)
                .addRDN(BCStyle.ST, "beijing")
                // 城市(l)
                .addRDN(BCStyle.L, "beijing")
                // 通用名称(cn)
                .addRDN(BCStyle.CN, commonName)
                // 组织(o)  另, 部门(ou)
                .addRDN(BCStyle.O, commonName);

        X500Name subject = x500NameBld.build();

        PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());

        ExtensionsGenerator extGen = new ExtensionsGenerator();
        extGen.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.dNSName, commonName)));
        Extensions extensions = extGen.generate();
        requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensions);

        ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").setProvider("BC").build(kp.getPrivate());
        PKCS10CertificationRequest build = requestBuilder.build(signer);
        // 根据相关服务信息和模板生成 csr格式请求报文
        String sendMsg = applyCertRequstGenerator.generateApplyByCsr(org.bouncycastle.util.encoders.Base64.toBase64String(build.getEncoded()),
                enterpriseinfo, templateId, "");
        caDetail.setRequestContent(sendMsg);
        log.info("sendMsg: " + sendMsg);
        String ret = applyCertRequstGenerator.sendMsg(raUrl, sendMsg);
        caDetail.setResponseContent(ret);
        log.info("response: " + ret);

        // 读取证书内容并写入到文件中
        String signCert = JSONObject.parseObject(ret).getJSONObject("respBody").getString("signCert");
        String certPath = this.certFile(signCert);
        caDetail.setRemark(certPath);

        caDetail.setCrateTime(new Date());
        caDetail.setUpdateTime(new Date());
        return caDetail;
    }

    /**
     * 证书文件写入到本地 返回资源证书具体路径
     *
     * @param signCert
     * @return
     * @throws IOException
     */
    private String certFile(String signCert) throws IOException {
        // 生成随机文件名称
        String uuidString = UUID.randomUUID().toString().replaceAll("-", "");
        String certFileName = caFilePath + "/" + uuidString + ".cer";
        File pubkeyFile = new File(certFileName);
        FileOutputStream pubkeyoutf = new FileOutputStream(pubkeyFile);
        BufferedOutputStream pubkeybos = new BufferedOutputStream(pubkeyoutf);
        pubkeybos.write(signCert.getBytes());
        pubkeybos.close();
        pubkeyoutf.close();

        return certFileName;
    }
}
