/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.etee.crypto.examples;

import java.io.File;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import java.util.Map;

import be.fgov.ehealth.etee.crypto.decrypt.DataUnsealer;
import be.fgov.ehealth.etee.crypto.decrypt.DataUnsealerBuilder;
import be.fgov.ehealth.etee.crypto.encrypt.DataSealer;
import be.fgov.ehealth.etee.crypto.encrypt.DataSealerBuilder;
import be.fgov.ehealth.etee.crypto.encrypt.EncryptionToken;
import be.fgov.ehealth.etee.crypto.policies.EncryptionCredentials;
import be.fgov.ehealth.etee.crypto.policies.EncryptionPolicy;
import be.fgov.ehealth.etee.crypto.policies.OCSPPolicy;
import be.fgov.ehealth.etee.crypto.policies.SigningCredential;
import be.fgov.ehealth.etee.crypto.policies.SigningPolicy;
import be.fgov.ehealth.etee.crypto.utils.IoUtils;
import be.fgov.ehealth.etee.crypto.utils.KeyManager;


/**
 * Abstract superclass for the examples. It contains the common methods.
 *
 * @author jeh
 */
public class AbstractExample {

    /**
     * During initialisation, Bob creates his DataUnsealer that he can use to decrypt
     *  and verify incoming sealed data.
     * 
     * @return Bob's DataUnsealer, ready to decrypt and verify data sealed for Bob.
     */
    protected static DataUnsealer initUnsealing() {

        KeyStore trustedRootCaCertsKeystore;
        Map<String, PrivateKey> bobsDecryptionKeys;

        // 1. Create Bob's DataUnsealer
        // 1.1 Get the keystore containing the (Root) CA certificates that you trust
        trustedRootCaCertsKeystore = getTrustedCaCertificates();
    
        // 1.2 Get Bob's decryption keys
        bobsDecryptionKeys = getBobDecryptionKeys();
        
        // 1.3 Now you have all what is necessary to create Bob's DataUnsealer
        return DataUnsealerBuilder.newBuilder()
                                  .addOCSPPolicy(OCSPPolicy.NONE)
                                  .addSigningPolicy(trustedRootCaCertsKeystore, SigningPolicy.EHEALTH_CERT)
                                  .addPublicKeyPolicy(EncryptionPolicy.KNOWN_RECIPIENT,
                                      EncryptionCredentials.from(bobsDecryptionKeys))
                                  .build();
    }

    /**
     * During initialization, Alice creates a DataSealer that she can use later to protect her messages with.
     * 
     * @return Alice's DataSealer, ready to sign and encrypt data.
     */
    protected static DataSealer initSealing() {
        PrivateKey aliceAuthenticationKey;
        X509Certificate aliceAuthCertificate;
        X509Certificate aliceCaCertificate;
        DataSealer dataSealer;
    
        try {
            // 1.1. Get the sender's private authentication key for signature creation
            aliceAuthenticationKey = KeyManager.getKey(getAliceKeyStore()
                , ExampleProperties.ALICES_AUTHENTICATION_KEY_ALIAS
                , ExampleProperties.ALICES_AUTHENTICATION_KEY_PASSWORD);
            
            // 1.2. Get the sender's authentication certificate that matches the authentication key
            aliceAuthCertificate = KeyManager.getCertificate(getAliceKeyStore()
                , ExampleProperties.ALICES_AUTHENTICATION_KEY_ALIAS);
            
            // 1.3. Get the CA certificate of the Certificate Authority that issued the auth cert.
            aliceCaCertificate = ExampleProperties.getAliceCaCert();

            // 1.4 Create an instance of SigningCredential with the appropriate information
            SigningCredential signingCredential = SigningCredential.create(aliceAuthenticationKey, aliceAuthCertificate, aliceCaCertificate);

            // 1.4 Get the DataSealer for Alice
            dataSealer = DataSealerBuilder.newBuilder().
                                           addOCSPPolicy(OCSPPolicy.NONE).
                                           addSigningPolicy(SigningPolicy.EHEALTH_CERT, signingCredential).
                                           addPublicKeyPolicy(EncryptionPolicy.KNOWN_RECIPIENT).
                                           addSecretKeyPolicy(EncryptionPolicy.UNKNOWN_RECIPIENT).
                                           build();
        } catch (UnrecoverableKeyException e) {
            throw new RuntimeException(e);
        }
        
        return dataSealer;
    }

    /**
     * This method abstracts how to Bob's private decryption keys are retrieved. Usually these keys retrieved from his 
     * password protected {@link KeyStore}.
     * 
     * @return A map with bob's private decryption keys and their serial numbers.
     */
    private static Map<String, PrivateKey> getBobDecryptionKeys() {
        return ExampleProperties.getBobDecryptionKeys();
    }

    /**
     * This method abstracts how the key store containing the CA certificate trusted by Bob.
     * @return A key store containing the CA certificate trusted by Bob.
     */
    protected static KeyStore getTrustedCaCertificates() {
        return ExampleProperties.getEhealthRootCaCert();
    }


    /** @return Alice's {@link KeyStore} */
    private static KeyStore getAliceKeyStore() {
        return ExampleProperties.getAliceKeyStore();
    }

    /**
     * This method abstracts how the encryption token of the recipient is retrieved from the ETK-DEPOT web service.
     * I.e. the implementation of this method is not representative for real world application.
     * See the cookbook how to do this.
     * 
     * @return Bob's encryption token.
     */
    protected static EncryptionToken getBobsEtkFromEtkDepot() {
        return ExampleProperties.getBobEtk();
    }

    /**
     * This method abstracts how Alice's message after data sealing is transported to Bob.
     * (This is application specific.)
     * 
     * @param sealedData the sealed data
     * @param pathname path to the output file
     * @return a File containing the sealed message of Alice.
     */
    public static File writeToTransportMedium(byte[] sealedData, String pathname) {
        return IoUtils.writeToFile(sealedData, pathname);
    }

    /**
     * This method abstracts how a new KEK and KEK id is requested at the KGSS web service.
     * @return base 64 encoded KEK
     */
    protected static String getNewKekFromKgss() {
        return ExampleProperties.getBase64EncodedKek();
    }

    /**
     * This method abstracts how a new KEK and KEK id is requested at the KGSS web service.
     * @return base 64 encoded KEK ID
     */
    protected static String getNewKekIdFromKgss() {
        return ExampleProperties.getBase64EncodedKekId();
    }
}
