/*****************************************************************
SEGURETAT COMPUTACIONAL - Curs 2004 - 2005
Pr?ctica 2: Criptografia de Clau P?blica
Joan Ametller Esquerra (Joan.Ametller@uab.es)
*****************************************************************/

// 666 203037

import java.security.*;
import java.security.spec.*;
import java.io.*;
import java.math.BigInteger;
import java.security.interfaces.* ;

public class FileSigner {
  
  // implicite NULL constructor
  /*
    public FileSigner()
     {
     }
     */

  final static int buffersize=8*8;

  private int size = 0;

  private Signature initSig(String alg) throws NoSuchAlgorithmException {

	Signature mySignature = null ;

	if ( alg=="DSA" ) {
		//System.out.println("initialising SHA1withDSA");
	  	mySignature = Signature.getInstance("SHA1withDSA");
	}
	else {
		  if ( alg=="RSA" ) {
			//System.out.println("initialising SHA1withRSA");
	  		mySignature = Signature.getInstance("SHA1withRSA");
		  }
		  else { System.out.println("algoo not supported"); }
	}

	return mySignature;
  }

  private byte[] readFile(String fname) throws IOException {

	File file = new File(fname);
        InputStream is = new FileInputStream(file);
    
        long length = file.length();
        byte[] bytes = new byte[(int)length];
    
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
               && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
            offset += numRead;
        }
    
        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file "+file.getName());
        }
        is.close();
	return bytes ;
  }

  private void readFileintoSignature(String fname, Signature sig) throws Exception {
        InputStream is = new FileInputStream(new File(fname));
	byte[] buffer = new byte[buffersize];

	int numread=0;
	while ( (numread=is.read(buffer,0,buffersize)) >= 0 ) {
		if ( numread < buffersize ) {
			byte[] temp = new byte[numread];
			for (int i=0;i<numread;i++) temp[i]=buffer[i];
			sig.update(temp);
		}
		else {
			sig.update(buffer);
		}
	};
  }

  private void writeFile(String fname, byte[] bytes ) throws IOException {
        OutputStream os = new FileOutputStream(new File(fname));
	os.write(bytes);
	os.close();
  }
   
  public void generaClaus(String algorisme, int bits) 
    throws NoSuchAlgorithmException{

    	    this.size = bits ;
	    KeyPairGenerator kp = KeyPairGenerator.getInstance(algorisme);
	    kp.initialize(bits);
	    this._keyPair = kp.generateKeyPair() ; // getPublic ; getPrivate()
    
  }
  
  public void signaFitxer(String fname, String fsig, String alg) throws Exception{

	Signature mySignature = initSig(alg) ;

	mySignature.initSign(_keyPair.getPrivate());


    	readFileintoSignature(fname,mySignature);
	writeFile(fsig,mySignature.sign());
  }
  
  public boolean verificaFitxer(String fitxer, String fitxersignatura, String alg) throws Exception{

	Signature mySignature = initSig(alg) ;

	mySignature.initVerify(_keyPair.getPublic());


    	readFileintoSignature(fitxer,mySignature);
	return 	mySignature.verify(readFile(fitxersignatura));
  }
  
  public void guardaClaus(String arxiuPublica, String arxiuPrivada) throws Exception{    
	  PrivateKey pk = _keyPair.getPrivate() ;
	  PublicKey puk = _keyPair.getPublic();

	  writeKey(arxiuPublica, puk);
	  writeKey(arxiuPrivada, pk);


  }

  public void carregaClaus(String arxiuPublica, String arxiuPrivada, String alg) throws Exception{
	  // implementation distinto. Typo de algorithmos tambien guardado en fitchero
	  // por eso el tercero argumento sera ignorado

	  if (alg != null) throw new Exception("lee commentario en implementation de carregaCaulaus");

	  PublicKey puk2 =  (PublicKey)readKey(arxiuPublica,0);
	  PrivateKey pk2 =  (PrivateKey)readKey(arxiuPrivada,1);

	  this._keyPair = new KeyPair(puk2,pk2);
  }

  private void writeKey(String filename, Key key ) throws Exception{    

	byte[] t = key.getEncoded();
	String algo =  key.getAlgorithm();
	byte[] b = (algo+"\n").getBytes();

	FileOutputStream fos = new FileOutputStream(new File(filename)) ;
	fos.write(b);
	fos.write(t);
	fos.close();

  }

  private Key readKey(String filename, int priv  ) throws Exception{
	  
	  FileInputStream fis = new FileInputStream(new File(filename));

	  java.util.Vector v = new java.util.Vector();
	  java.util.Vector v2 = new java.util.Vector();

	  int c = fis.read();

	  while ( c != -1 && (byte)c != ("\n".getBytes())[0] ) {
		  v.add(new Byte( (byte)c ) );
		  c=fis.read();
	  }

	  c = fis.read();

	  while ( c != -1 ) {
		  v2.add(new Byte( (byte)c ) );
		  c=fis.read();
	  }

	  fis.close();

	  byte[] b = new byte[v.size()];
	  for (int i=0; i<v.size(); i++) b[i] = ((Byte)v.elementAt(i)).byteValue() ;
	  String algo  =  new String(b);

	  byte[] b2 = new byte[v2.size()];
	  for (int i=0; i<v2.size(); i++) b2[i] = ((Byte)v2.elementAt(i)).byteValue() ;


	  KeyFactory kf = KeyFactory.getInstance(algo);


	  if (priv == 0 ) { // public
		  X509EncodedKeySpec spec = new X509EncodedKeySpec(b2);
		  return kf.generatePublic(spec);
	  }
	  else { // private
		  PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b2);
		  return kf.generatePrivate(spec);
	  }

  }

  private KeyPair _keyPair;
}





