/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.config.it.utils;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.certificates.factory.CryptoX509Certificate;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class KeyLoader {

    public static Certificate[] loadPEMsToChainOfCertificates(final List<String> listOfPEMFiles)
            throws GeneralSecurityException, IOException {
        Certificate[] chain = new Certificate[listOfPEMFiles.size()];
        for (int i = 0; i < listOfPEMFiles.size(); ++i) {
            X509Certificate cert = loadPublicX509(listOfPEMFiles.get(i));
            chain[i] = cert;
        }
        return chain;
    }

    public static CryptoX509Certificate convertPEMfiletoCryptoX509Certificate(final String fileLocation)
            throws CertificateException, IOException, GeneralCryptoLibException {
        X509Certificate cert = null;
        try {
            File f = new File(fileLocation);
            FileInputStream fis = new FileInputStream(f);
            BufferedInputStream bis = new BufferedInputStream(fis);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            while (bis.available() > 0) {
                cert = (X509Certificate) cf.generateCertificate(bis);
                // System.out.println("cert.toString(): " + cert.toString());
            }
            // System.out.println("cert.getPublicKey: "
            // + cert.getPublicKey().toString());
            // System.out.println("cf.getProvider: " + cf.getProvider());
            // System.out.println("cf.getType: " + cf.getType());
            CryptoX509Certificate importedCert = new CryptoX509Certificate(cert);
            return importedCert;
        } catch (FileNotFoundException ex) {
            throw new CertificateException("File:" + fileLocation + " not found");
        }
    }

    public static CryptoX509Certificate convertPEMStringtoCryptoX509Certificate(final String certString)
            throws CertificateException, GeneralCryptoLibException {

        X509Certificate cert = null;

        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream bis = new ByteArrayInputStream(certString.getBytes(StandardCharsets.UTF_8));
        while (bis.available() > 0) {
            cert = (X509Certificate) cf.generateCertificate(bis);
        }

        CryptoX509Certificate importedCert = new CryptoX509Certificate(cert);

        return importedCert;

    }

    public static List<CryptoX509Certificate> convertPEMfileStoCryptoX509CertificateS(final List<String> fileLocations)
            throws Exception {
        List<CryptoX509Certificate> certificates = new ArrayList<>();
        if (fileLocations != null && !fileLocations.isEmpty()) {
            for (String fileLocation : fileLocations) {
                certificates.add(convertPEMfiletoCryptoX509Certificate(fileLocation));
            }
        }
        return certificates;
    }

    public static X509Certificate loadPublicX509(final String fileName) throws GeneralSecurityException, IOException {
        File f = new File(fileName);
        FileInputStream fis = new FileInputStream(f);
        BufferedInputStream bis = new BufferedInputStream(fis);
        X509Certificate crt = null;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            while (bis.available() > 0) {
                crt = (X509Certificate) cf.generateCertificate(bis);
            }

        } finally {
            IOUtils.closeQuietly(bis);
            IOUtils.closeQuietly(fis);
        }
        return crt;
    }

    public static CryptoX509Certificate convertCertificateToCryptoCertificate(Certificate certificate) throws CertificateException, GeneralCryptoLibException {

        InputStream certificateInputStream = new ByteArrayInputStream(certificate.getEncoded());
        CertificateFactory _certFactory = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) _certFactory.generateCertificate(certificateInputStream);

        return new CryptoX509Certificate(cert);

    }

    public static CryptoX509Certificate[] fromCertificateArrayToCryptoCertificateArray(Certificate[] certChain) throws CertificateException, GeneralCryptoLibException {

        List<CryptoX509Certificate> cryptoX509CertificateList = new ArrayList<>();

        for (Certificate certificate : certChain) {
            CryptoX509Certificate cryptoX509Certificate = convertCertificateToCryptoCertificate(certificate);
            cryptoX509CertificateList.add(cryptoX509Certificate);
        }

        return cryptoX509CertificateList.toArray(new CryptoX509Certificate[cryptoX509CertificateList.size()]);
    }

    public static PrivateKey loadPrivateKey(final String key) throws GeneralSecurityException {

        byte[] keyBytes = Base64.decode(key);

        // generate private key
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(spec);
    }

    public static PublicKey loadPublicKey(final String key) throws GeneralSecurityException {

        byte[] keyBytes = Base64.decode(key);

        // generate private key
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

    public static SecretKey loadSecretKey(final String base64SecretKey) {

        SecretKey _key = new SecretKeySpec(ConvertString.base64StringToByte(base64SecretKey), "HmacSHA256");
        return _key;
    }
}
