import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
import java.util.*;

public class Simetric
{

   private final String algorithm = "DESede";
   private final int keysize = 168 ;
   private final String pbealgo = "PBEWithMD5AndDES";
   private final int pbecount = 20;
   private final int saltsize = 8;
	     
   public SecretKey generaClau() throws Exception
     {
	     KeyGenerator kg = KeyGenerator.getInstance(algorithm);
	     kg.init(keysize);
	     SecretKey sk = kg.generateKey();
	     this._sk = sk ;
	     // parece raro, porque retornar la clave?
	     return sk;
     }
   
   public void guardaClau(String fname) throws Exception
     {
	     FileOutputStream fos = new FileOutputStream(new File(fname));
	     fos.write(_sk.getEncoded());
	     fos.close();
     }
   
   public void guardaClauPBE(String fname, String password) throws Exception
     {	     


    	byte[] salt = new byte[saltsize];

	java.util.Random rand = new java.util.Random();
	rand.nextBytes(salt);

	SecretKey pbeKey = getPBESecKey(password);
    	PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, pbecount);

    	Cipher pbeCipher = Cipher.getInstance(pbealgo);
    	pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

	FileOutputStream fos = new FileOutputStream(new File(fname));
	fos.write(salt);
	fos.write(pbeCipher.doFinal(_sk.getEncoded()));
	fos.close();
     }
   
   public SecretKey llegeixClauPBE(String fname, String password) throws Exception
     {	     
	File f = new File(fname);

	FileInputStream fis = new FileInputStream(f);
    	byte[] salt = new byte[saltsize];
	fis.read(salt);

	SecretKey pbeKey = getPBESecKey(password);
    	PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, pbecount);

    	Cipher pbeCipher = Cipher.getInstance(pbealgo);
    	pbeCipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);

	byte[] keymat = new byte[(int)f.length()-saltsize];
	fis.read(keymat);
	fis.close();

	DESedeKeySpec spec = new DESedeKeySpec(pbeCipher.doFinal(keymat));
	SecretKeyFactory fac = SecretKeyFactory.getInstance(algorithm);
	SecretKey sk = fac.generateSecret(spec);
	this._sk = sk ;

	return  sk;
     }

   private SecretKey getPBESecKey(String password) throws Exception
   {
    	PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
    	SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbealgo);
    	SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
	return pbeKey;
   }


   public SecretKey llegeixClau(String fname) throws Exception
     {	     
	File f = new File(fname);
	if (f.length() > Integer.MAX_VALUE) throw new Exception("only small keys supported");
	byte[] buffer = new byte[(int)f.length()];

	FileInputStream fis = new FileInputStream(f);
	fis.read(buffer);
	fis.close();

	DESedeKeySpec spec = new DESedeKeySpec(buffer);
	SecretKeyFactory fac = SecretKeyFactory.getInstance(algorithm);

	SecretKey sk = fac.generateSecret(spec);
	this._sk = sk ;

	return  sk;
     }
   

   public void xifra(String forigen, String fdesti) throws Exception
   {
	   Cipher c = Cipher.getInstance(algorithm);
	   c.init(Cipher.ENCRYPT_MODE,_sk);
	   processCipher(c,forigen,fdesti);
   }
   public void desxifra(String forigen, String fdesti) throws Exception
   {
	   Cipher c = Cipher.getInstance(algorithm);
	   c.init(Cipher.DECRYPT_MODE,_sk);
	   processCipher(c,forigen,fdesti);
   }

   private void processCipher(Cipher c, String infname, String offname) throws Exception
   {
	int buffersize = c.getBlockSize();
	FileInputStream is = new FileInputStream(new File(infname));
	FileOutputStream os = new FileOutputStream(new File(offname));

	byte[] buffer = new byte[buffersize];

	int numread=0;
	while ( (numread=is.read(buffer,0,buffersize)) >= 0 ) {
		if ( numread < buffersize ) {
			for (int i=numread;i<buffersize;i++) buffer[i]=0;
		}
		os.write(c.update(buffer));
	};

	os.write(c.doFinal());
	is.close();
	os.close();
   }


   private SecretKey _sk;
}

