package org.ofdrw.gm.ses.v1;

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.Test;
import org.ofdrw.gm.cert.PKCS12Tools;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

/**
 * V1 版本电子印章构造
 * <p>
 * 注意：该方法只用于生成测试数据，电子印章请使用符合国家规范的电子印章服务器生成管理！
 *
 * @author 权观宇
 * @since 2020-04-21 10:16:02
 */
public class SESealTest {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }


    /**
     * 构造测试用电子印章
     * <p>
     * 注意：该方法只用于生成测试数据，电子印章请使用符合国家规范的电子印章服务器生成管理！
     */
    @Test
    void sealBuild() throws Exception {
        Path userP12 = Paths.get("src/test/resources", "USER.p12");
        Path sealerP12 = Paths.get("src/test/resources", "SealBuilder.p12");
        Path picturePath = Paths.get("src/test/resources", "StampImg.png");
        Path out = Paths.get("target/UserV1.esl");


        SES_Header header = new SES_Header(new ASN1Integer(1), new DERIA5String("OFDR&WTest"));

        /*
         * 印章属性信息构造
         */
        // 获取用户证书
        Certificate userCert = PKCS12Tools.ReadUserCert(userP12, "private", "777777");
        ASN1EncodableVector v = new ASN1EncodableVector(1);
        v.add(new DEROctetString(userCert.getEncoded()));

        Calendar then = Calendar.getInstance();
        then.add(Calendar.YEAR, 2);
        SES_ESPropertyInfo property = new SES_ESPropertyInfo()
                .setType(SES_ESPropertyInfo.OrgType)
                .setName(new DERUTF8String("OFDRW测试用印章"))
                .setCertList(new DERSequence(v))
                .setCreateDate(new ASN1UTCTime(new Date()))
                .setValidStart(new ASN1UTCTime(new Date()))
                .setValidEnd(new ASN1UTCTime(then.getTime()));

        /*
         * 印章图片信息 构造
         */
        SES_ESPictrueInfo picture = new SES_ESPictrueInfo()
                .setType("PNG")
                .setData(Files.readAllBytes(picturePath))
                .setWidth(40)
                .setHeight(40);

        /*
         * 印章信息构造
         */
        SES_SealInfo sealInfo = new SES_SealInfo()
                .setHeader(header)
                .setEsID(UUID.randomUUID().toString().replace("-", "").toUpperCase())
                .setProperty(property)
                .setPicture(picture);
        /*
         * 电子签章数据构造
         */
        Certificate sealerCert = PKCS12Tools.ReadUserCert(sealerP12, "private", "777777");
        PrivateKey privateKey = PKCS12Tools.ReadPrvKey(sealerP12, "private", "777777");
        DEROctetString signCert = new DEROctetString(sealerCert.getEncoded());

        // 印章信息、制章人证书、签名算法标识符组成的信息作为签名原文
        v = new ASN1EncodableVector(3);
        v.add(sealInfo);
        v.add(signCert);
        v.add(GMObjectIdentifiers.sm2sign_with_sm3);

        Signature signature = Signature.getInstance("SM3withSm2", "BC");
        signature.initSign(privateKey);
        signature.update(new DERSequence(v).getEncoded("DER"));
        byte[] sign = signature.sign();
        SES_SignInfo signInfo = new SES_SignInfo()
                .setCert(signCert)
                .setSignatureAlgorithm(GMObjectIdentifiers.sm2sign_with_sm3)
                .setSignData(sign);

        SESeal seal = new SESeal(sealInfo, signInfo);

        Files.write(out, seal.getEncoded("DER"));
    }


    @Test
    public void verify() throws IOException, NoSuchAlgorithmException, CertificateException, InvalidKeyException, SignatureException {
        Path path = Paths.get("target", "UserV1.esl");
//        Path path = Paths.get("target", "2_980_1587284330714.es");

        SESeal seal = SESeal.getInstance(Files.readAllBytes(path));
        SES_SignInfo signInfo = seal.getSignInfo();

        ASN1OctetString cert = signInfo.getCert();
        CertificateFactory factory = new CertificateFactory();
        X509Certificate certificate = (X509Certificate) factory.engineGenerateCertificate(cert.getOctetStream());

        ASN1EncodableVector v = new ASN1EncodableVector(3);
        v.add(seal.getEsealInfo());
        v.add(cert);
        v.add(signInfo.getSignatureAlgorithm());

        Signature sg = Signature.getInstance("SM3WithSM2", new BouncyCastleProvider());
        sg.initVerify(certificate);
        sg.update(new DERSequence(v).getEncoded("DER"));
        byte[] sigVal = signInfo.getSignData().getBytes();
        System.out.println(sg.verify(sigVal));
    }
}