package org.aoc.applet.servidor.sample;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;

import lib.org.bouncycastle.asn1.cms.AttributeTable;

import org.catcert.AddBCProvider;
import org.catcert.crypto.keyStoreImpl.CompositeKeyStore;
import org.catcert.crypto.keyStoreImpl.pkcs12.PKCS12KeyStore;
import org.catcert.crypto.keyStoreImpl.pkcs12.PKCS12KeyStoreException;
import org.catcert.crypto.signImpl.CMSSignatureGeneration;
import org.catcert.crypto.signImpl.CMSSignatureGenerationException;
import org.catcert.crypto.signImpl.PDFSignatureGeneration;
import org.catcert.crypto.signImpl.PDFSignatureGenerationException;
import org.catcert.crypto.signImpl.XMLdsigGeneration;
import org.catcert.crypto.signImpl.XMLdsigGenerationException;
import org.catcert.params.AppletParams;
import org.catcert.params.PdfSignatureParams;
import org.catcert.utils.AppletConstants;

/**
 * Classe d'exemple per a generar signatures en servidor amb els jars
 * de l'applet de signatura. 
 * 
 * Aquest codi es distribueix només a mode d'exemple.
 *  
 * @author aciffone
 *
 */
public class SignatureSamples {

	private static String PKCS12 = "/org/aoc/applet/servidor/resources/CIPISR-1.p12";
	private static char[] PKCS12Password = "1234".toCharArray();
	private static Logger log = Logger.getLogger(SignatureSamples.class.getName());

	// main amb les crides als diferents mètodes per a generar signatures
	public static void main(String args[]) throws Exception {
		
		// generem signatura de tipus XAdES-BES detached
		SignatureSamples sampler = new SignatureSamples();
		log.info("Generem signatura XAdES-BES Detached");
		byte[] xadesBes = sampler.generaSignaturaXAdES_BES();
		// desem a disc la signatura generada
		sampler.saveFile(xadesBes, "xades_bes_detached.xml");
		
		// generem signatura de tipus XAdES-BES detached usant com 
		// a algoritme de hash SHA256
		log.info("Generem signatura XAdES-BES Detached AMB SHA256");
		byte[] xadesBesSha256 = sampler.generaSignaturaXAdES_BES_SHA256();
		// desem a disc la signatura generada
		sampler.saveFile(xadesBesSha256, "xades_bes_detached_SHA256.xml");

		// generem una signatura de tipus CAdES-T detached
		log.info("Generem signatura CAdES-T Detached");
		byte[] cadesT = sampler.generaSignaturaCAdES_T();
		sampler.saveFile(cadesT, "cades_t_detached.bin");
		
		// generem una signatura de tipus CAdES-T detached
		log.info("Generem signatura CAdES-T Detached utilitzant el SHA256 com a algoritme de hash");
		cadesT = sampler.generaSignaturaCAdES_T_SHA256();
		sampler.saveFile(cadesT, "cades_t_detached_sha256.bin");

		// generem una signatura de tipus PDF
		log.info("Generem signatura PDF CAdES-BES");
		byte[] pdfCadesBes = sampler.generaSignaturaPDFCAdESBESIncrustada();
		sampler.saveFile(pdfCadesBes, "pdf_cades_bes.pdf");
			
		// generem una signatura de tipus XAdES-BES detached sobre el
		// hash d'un document precalculat
		log.info("Generem signatura XAdES-BES detached hash");
		byte[] xadesBesHash = sampler.generaSignaturaXAdES_BES_docHash();
		sampler.saveFile(xadesBesHash, "xades_bes_detached_hash.xml");
		
		// generem una signatura de tipus CAdES-BES detached sobre el
		// hash d'un document precalculat
		log.info("Generem signatura CAdES-BES detached hash");
		byte[] cadesBesHash = sampler.generaSignaturaCAdES_BES_detachedHash();
		sampler.saveFile(cadesBesHash, "cades_bes_detached_hash.bin");
		
	}

	/**
	 * Exemple per a la generació d'una signatura XAdES_BES Detached
	 * @throws PKCS12KeyStoreException 
	 * @throws XMLdsigGenerationException 
	 * @throws UnsupportedEncodingException 
	 * @throws KeyStoreException 
	 */
	public byte[] generaSignaturaXAdES_BES() throws PKCS12KeyStoreException, XMLdsigGenerationException, UnsupportedEncodingException, KeyStoreException{

		// Step 1: document a signar
		byte[] docToSign = "<doc>XML A SIGNAR</doc>".getBytes("UTF-8");
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();
			
		// Step 3: Parametrització de la signatura
		// setegem el mode de signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.XAdES_BES_detached);
		// opcionalment a través del applet params es poden setejar
		// altres paràmetres de funcionament (veure documentació)

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		XMLdsigGeneration xmlSigner = new XMLdsigGeneration(cKS, alias, PKCS12Password);
		// invoquem la signatura passant el document a signar i els paràmetres
		return xmlSigner.sign(docToSign, appletParams);
	}


	public byte[] generaSignaturaCAdES_T() throws PKCS12KeyStoreException, UnsupportedEncodingException, KeyStoreException, CMSSignatureGenerationException{

		// per a fer signatures de tipus CMS es necessari carregar el proveidor de BC
		AddBCProvider.load();
		
		// Step 1: document a signar
		byte[] docToSign = "DOCUMENT A SIGNAR".getBytes("UTF-8");
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();

		// Step 3: Parametrització de la signatura
		// setegem el mode de signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.CAdES_T_detached);
		// opcionalment a través del applet params es poden setejar
		// altres paràmetres de funcionament (veure documentació)
		
		// indica si el resultat el volem en b64
		boolean isB64 = false;
		// attributs signats, per si disposem d'algun atribut signat (null en la majoria de casos)
		AttributeTable signedAttributes = null;

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		CMSSignatureGeneration cmsSigner = new CMSSignatureGeneration(cKS, alias, PKCS12Password);
		// invoquem la signatura passant el document a signar i els paràmetres
		return cmsSigner.sign(docToSign, appletParams, signedAttributes, isB64);
	}
	
	public byte[] generaSignaturaCAdES_T_SHA256() throws Exception {

		// per a fer signatures de tipus CMS es necessari carregar el proveidor de BC
		AddBCProvider.load();
		
		// Step 1: document a signar
		byte[] docToSign = "DADES A SIGNAR".getBytes("UTF-8");
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();

		// Step 3: Parametrització de la signatura
		// setegem el mode de signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.CAdES_T_detached);
		// setegem l'algoritme de hash a utilitzar per aplicar sobre les dades a signar
		appletParams.setHashAlgorithm(String.valueOf(AppletConstants.SHA256));
		
		// opcionalment a través del applet params es poden setejar
		// altres paràmetres de funcionament (veure documentació)
		
		// indica si el resultat el volem en b64
		boolean isB64 = false;
		// attributs signats, per si disposem d'algun atribut signat (null en la majoria de casos)
		AttributeTable signedAttributes = null;

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		CMSSignatureGeneration cmsSigner = new CMSSignatureGeneration(cKS, alias, PKCS12Password);
		// invoquem la signatura passant el document a signar i els paràmetres
		return cmsSigner.sign(docToSign, appletParams, signedAttributes, isB64);
	}

	public byte[] generaSignaturaPDFCAdESBESIncrustada() throws IOException, PKCS12KeyStoreException, KeyStoreException, PDFSignatureGenerationException{
		
		// per a fer signatures de tipus CMS/CAdES es necessari carregar el proveidor de BC
		AddBCProvider.load();
		
		// recuperem el document a signar
		InputStream is = SignatureSamples.class.getResourceAsStream("/org/aoc/applet/servidor/resources/sample.pdf");
		byte[] docToSign = new byte[is.available()];
		is.read(docToSign);
		is.close();

		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();
	
		// Step 3: Parametrització de la signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.CAdES_BES_in_PDF);
		
		// recuperem els paràmetres de signatura de PDF
		PdfSignatureParams pdfSignParams = appletParams.getPdfSignatureParams();
		pdfSignParams.setReason("Signatura a servidor de proves");
		pdfSignParams.setLocation("BCN");
	
		// Step 4: Generació signatura
		return PDFSignatureGeneration.sign(docToSign, appletParams, cKS, alias, PKCS12Password, null);

	}
	
	/**
	 * Exemple per a la generació d'una signatura XAdES_BES Detached sobre un hash
	 * @throws PKCS12KeyStoreException 
	 * @throws XMLdsigGenerationException 
	 * @throws UnsupportedEncodingException 
	 * @throws KeyStoreException 
	 * @throws NoSuchAlgorithmException 
	 */
	public byte[] generaSignaturaXAdES_BES_docHash() throws PKCS12KeyStoreException, XMLdsigGenerationException, UnsupportedEncodingException, KeyStoreException, NoSuchAlgorithmException{

		// Step 1: document a signar
		byte[] someData = "documentHashASignar".getBytes("UTF-8");
		MessageDigest md = MessageDigest.getInstance(AppletConstants.SHA1ID);
		byte[] hashToSign = md.digest(someData);
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();
		
		
		// Step 3: Parametrització de la signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.XAdES_BES_detached_hash);

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		XMLdsigGeneration xmlSigner = new XMLdsigGeneration(cKS, alias, PKCS12Password);
		// invoquem la signatura passant el document a signar i els paràmetres
		return xmlSigner.sign(hashToSign, appletParams);
	}
	
	/**
	 * Exemple per a la generació d'una signatura CAdES_BES Detached sobre un hash
	 * @throws Exception 
	 * @throws PKCS12KeyStoreException 
	 * @throws XMLdsigGenerationException 
	 * @throws UnsupportedEncodingException 
	 * @throws KeyStoreException 
	 * @throws NoSuchAlgorithmException 
	 */
	public byte[] generaSignaturaCAdES_BES_detachedHash() throws Exception {

		// Step 1: document a signar
		byte[] someData = "documentHashASignar".getBytes("UTF-8");
		MessageDigest md = MessageDigest.getInstance(AppletConstants.SHA1ID);
		byte[] hashToSign = md.digest(someData);
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();
		
		// Step 3: Parametrització de la signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.CAdES_BES_detached_hash);
		
		// indica si el resultat el volem en b64
		boolean isB64 = false;
		// attributs signats, per si disposem d'algun atribut signat (null en la majoria de casos)
		AttributeTable signedAttributes = null;

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		CMSSignatureGeneration cmsSigner = new CMSSignatureGeneration(cKS, alias, PKCS12Password);
		
		// invoquem la signatura passant el document a signar i els paràmetres
		return cmsSigner.signHash(hashToSign, appletParams, signedAttributes, isB64);
	}
	
	/**
	 * Exemple per a la generació d'una signatura XAdES_BES Detached amb SHA256
	 * @throws PKCS12KeyStoreException 
	 * @throws XMLdsigGenerationException 
	 * @throws UnsupportedEncodingException 
	 * @throws KeyStoreException 
	 */
	public byte[] generaSignaturaXAdES_BES_SHA256() throws PKCS12KeyStoreException, XMLdsigGenerationException, UnsupportedEncodingException, KeyStoreException{

		// Step 1: document a signar
		byte[] docToSign = "<doc>XML A SIGNAR</doc>".getBytes("UTF-8");
		
		// Step 2: Clau amb la que signarem
		CompositeKeyStore cKS  = getKeyStore();
		// alias de la clau del p12
		String alias = cKS.aliases().nextElement();
			
		// Step 3: Parametrització de la signatura
		// setegem el mode de signatura
		AppletParams appletParams = new AppletParams();
		appletParams.setSignatureType(AppletConstants.XAdES_BES_detached);
		// indiquem l'algoritme de hash a utilitzar
		appletParams.setHashAlgorithm("3");
		
		// opcionalment a través del applet params es poden setejar
		// altres paràmetres de funcionament (veure documentació)

		// Step 4: Generació signatura
		// instanciem el signador amb la clau
		XMLdsigGeneration xmlSigner = new XMLdsigGeneration(cKS, alias, PKCS12Password);
		// invoquem la signatura passant el document a signar i els paràmetres
		return xmlSigner.sign(docToSign, appletParams);
	}
	
	

    /**
     * Recupera el PKCS12 que es fara servir per totes les signatures
     * @throws PKCS12KeyStoreException 
     */
	private CompositeKeyStore getKeyStore() throws PKCS12KeyStoreException{

		// recuperem el p12 com a recurs del projecte       
		URL resource = SignatureSamples.class.getResource(PKCS12);
		// keystore a partir d'un P12
		PKCS12KeyStore p12 = new PKCS12KeyStore(resource.getPath(), PKCS12Password);
		// creem un objecte compositeKeystore
		CompositeKeyStore cKS = new CompositeKeyStore();
		cKS.addKeyStore(p12);
		
		return cKS;
		
	}
	/**
	 * Desa a disc el contingut de les signatures
	 * @param content
	 * @param path
	 * @throws IOException 
	 */
	private void saveFile(byte[] content, String signName) throws IOException{
		FileOutputStream fos = null;
		try{
			// generem una carpeta al home 
			String folder = System.getProperty("user.home") + "/exemples_signatura";

			// creem la carpeta
			File dir = new File(folder);
			if (!dir.exists()) {
				dir.mkdirs();  
			}

			// desem la signatura
			fos = new FileOutputStream(folder + "/" + signName);
			fos.write(content);
			log.info("Signatura desada a: " + folder + "/" + signName);
		}catch(Exception e){
			log.log(Level.WARNING, "Problemes al desar la signatura",e);
		}finally{
			fos.close();
		}
	}

}
