package xades4j.production;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERUTF8String;


import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;

import org.bouncycastle.asn1.x500.style.RFC4519Style;

import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import xades4j.algorithms.EnvelopedSignatureTransform;
import xades4j.properties.DataObjectDesc;

import xades4j.providers.impl.DirectKeyingDataProvider;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Random;


/**
 * @author Artem R. Romanenko
 * @version 02/04/2018
 */
@RunWith(Parameterized.class)
public class SignerSpecificTest extends SignerTestBase {
    private static final String NATIONAL_DN_CYRILLIC = "National name '\u043F\u0440\u0438\u043C\u0435\u0440'";
    private static final String NATIONAL_DN_ARABIC = "National name '\u0645\u062B\u0627\u0644'";

    @Parameterized.Parameters
    public static Collection<ASN1Encodable[]> data() {
        ArrayList<ASN1Encodable[]> result = new ArrayList<ASN1Encodable[]>();
        result.add(new ASN1Encodable[]{new DERBMPString(NATIONAL_DN_CYRILLIC)});
        result.add(new ASN1Encodable[]{new DERUTF8String(NATIONAL_DN_CYRILLIC)});
        result.add(new ASN1Encodable[]{new DERBMPString(NATIONAL_DN_ARABIC)});
        result.add(new ASN1Encodable[]{new DERUTF8String(NATIONAL_DN_ARABIC)});
        return result;
    }

    @Parameterized.Parameter
    public ASN1Encodable commonName;

    @Test
    public void signWithNationalCertificate() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
        keyGen.initialize(1024, new SecureRandom());
        Date validityBeginDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
        long add = (1L * 365L * 24L * 60L * 60L * 1000L);  //1 year
        Date validityEndDate = new Date(System.currentTimeMillis() + add);
        KeyPair keyPair = keyGen.generateKeyPair();


        X509Certificate certWithNationalSymbols;
        {
            //generate certificate with national symbols in DN
            X500NameBuilder x500NameBuilder = new X500NameBuilder();
            AttributeTypeAndValue attr = new AttributeTypeAndValue(RFC4519Style.cn, commonName);
            x500NameBuilder.addRDN(attr);
            X500Name dn = x500NameBuilder.build();
            X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
                    dn, // issuer authority
                    BigInteger.valueOf(new Random().nextInt()), //serial number of certificate
                    validityBeginDate, // start of validity
                    validityEndDate, //end of certificate validity
                    dn, // subject name of certificate
                    keyPair.getPublic()); // public key of certificate
            // key usage restrictions
            builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign
                    | KeyUsage.digitalSignature | KeyUsage.keyEncipherment
                    | KeyUsage.dataEncipherment | KeyUsage.cRLSign));
            builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
            certWithNationalSymbols = new JcaX509CertificateConverter().getCertificate(builder
                    .build(new JcaContentSignerBuilder("SHA256withRSA").setProvider(BouncyCastleProvider.PROVIDER_NAME).
                            build(keyPair.getPrivate())));
        }


        XadesSigner signer = new XadesBesSigningProfile(new DirectKeyingDataProvider(certWithNationalSymbols, keyPair.getPrivate())).newSigner();
        Document doc1 = getTestDocument();
        Element elemToSign = doc1.getDocumentElement();
        DataObjectDesc obj1 = new DataObjectReference('#' + elemToSign.getAttribute("Id")).withTransform(new EnvelopedSignatureTransform());
        SignedDataObjects signDataObject = new SignedDataObjects(obj1);
        signer.sign(signDataObject, doc1.getDocumentElement());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        outputDOM(doc1, baos);
        String str = new String(baos.toByteArray());
        //expected without parsing exception
        Document doc = parseDocument(new ByteArrayInputStream(baos.toByteArray()));

    }

}