/*
 * 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.utils;

import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.stores.bean.KeyStoreType;
import com.scytl.cryptolib.asymmetric.service.AsymmetricService;
import com.scytl.cryptolib.certificates.cryptoapi.CryptoAPIX509Certificate;
import com.scytl.cryptolib.certificates.service.CertificatesService;
import com.scytl.cryptolib.stores.service.StoresService;
import com.scytl.products.ov.constants.CertificatePropertiesConstants;
import com.scytl.products.ov.datapacks.beans.CertificateParameters;
import com.scytl.products.ov.datapacks.beans.CredentialProperties;
import com.scytl.products.ov.datapacks.generators.CertificateDataBuilder;
import com.scytl.products.ov.datapacks.generators.X509CertificateGenerator;

public class CryptoUtils {

    /**
     * Non-public constructor
     */
	private CryptoUtils() {
    }
    
	public static KeyPair generateKeyPairOfType(CertificateParameters.Type certificateType) {
        AsymmetricService asymmetricService;
        try {
            asymmetricService = new AsymmetricService();
        } catch (GeneralCryptoLibException e) {
            throw new IllegalStateException("Exception while trying to create an AsymmetricService", e);
        }
        switch (certificateType) {
        case SIGN:
            return asymmetricService.getKeyPairForSigning();
        case ENCRYPTION:
            return asymmetricService.getKeyPairForEncryption();
        default:
            throw new IllegalStateException("The provided certificate type is invalid");
        }
    }

    public static X509CertificateGenerator createCertificateGenerator() {
        CertificatesService certificatesService;
        try {
            certificatesService = new CertificatesService();
        } catch (GeneralCryptoLibException e) {
            throw new IllegalStateException("Exception while trying to create a CertificatesService", e);
        }
        CertificateDataBuilder certificateDataBuilder = new CertificateDataBuilder();

        return new X509CertificateGenerator(certificatesService, certificateDataBuilder);
    }

    public static KeyStore createP12AndAddKey(final KeyPair keyPair, final CredentialProperties credentialProperties,
            final CryptoAPIX509Certificate serviceCert, final String subjectName, final char[] password,
            Certificate[] certificateChain) {

        KeyStore keyStore = createEmptyKeystore();

        addToKeystore(keyStore, keyPair, credentialProperties, serviceCert, subjectName, password, certificateChain);

        return keyStore;
    }

    public static KeyStore createEmptyKeystore() {

        StoresService storesService;
        try {
            storesService = new StoresService();
        } catch (GeneralCryptoLibException e) {
            throw new IllegalStateException("Exception while trying to create a StoresService: " + e.getMessage(), e);
        }

        KeyStore keyStore = null;
        try {
            keyStore = storesService.createKeyStore(KeyStoreType.PKCS12);
        } catch (GeneralCryptoLibException e) {
            throw new IllegalStateException("An error occurred while creating a p12", e);
        }

        return keyStore;
    }

    public static void addToKeystore(KeyStore keyStore, final KeyPair keyPair,
            final CredentialProperties credentialProperties, final CryptoAPIX509Certificate serviceCert,
            final String subjectName, final char[] password, Certificate[] parentCertificateChain) {

        final Certificate[] certificateChainIncludingNewLeaf = new Certificate[parentCertificateChain.length + 1];

        certificateChainIncludingNewLeaf[0] = serviceCert.getCertificate();
        for (int i = 0; i < parentCertificateChain.length; i++) {
            certificateChainIncludingNewLeaf[i + 1] = parentCertificateChain[i];
        }

        try {
            keyStore.setKeyEntry(credentialProperties.getAlias().get(CertificatePropertiesConstants.PRIVATE_KEY_ALIAS),
                keyPair.getPrivate(), password, certificateChainIncludingNewLeaf);
        } catch (KeyStoreException e) {
            throw new IllegalStateException("An error occurred while adding a key to the p12 of " + subjectName, e);
        }
    }
}
