//package br.com.bradseg.cripto;   
/*

# wordfile: "e:/batch/java/crypto/wfJava.TXT"

*/

import java.io.File;
import java.io.FileWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.*;
import javax.crypto.KeyGenerator;
//import javax.crypto.spec.SecretKeySpec; 
//import java.security.AlgorithmParameters; 
import javax.crypto.spec.*;
import java.lang.String.*; 

public class Crypto {

public static final int HASH_TYPE_MD5 = 1;
public static final int HASH_TYPE_SHA1 = 2; 

/** The hexadecimal digits "0" through "f". */
public static char[] NIBBLE = {
                    '0', '1', '2', '3', '4', '5', '6', '7',
                    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
                };  
                                  
public static final String hexString(byte[] buf)
{
     StringBuffer sb = new StringBuffer();
     for (int j=0; j<buf.length; j++) {
         sb.append(NIBBLE[(buf[j]>>>4)&15]);
         sb.append(NIBBLE[ buf[j]     &15]);
     } 
     //System.out.println( sb.length);
     return sb.toString();
} 

public static byte[] calcHash(byte[] data, int algorithmType) 
throws  NoSuchAlgorithmException 
{
	MessageDigest md = null;
	if (HASH_TYPE_MD5 == algorithmType)
	md = MessageDigest.getInstance("MD5");
	else if (HASH_TYPE_SHA1 == algorithmType)
	md = MessageDigest.getInstance("SHA-1");
	else
	throw new NoSuchAlgorithmException();
	
	md.update(data);
	byte bufferSaida[] = md.digest();
	
	return bufferSaida;
}

public final byte[] crypt(String data, String key, int algorithmType) 
 throws Exception 
{ 	
	byte[] hashKey = null;
	Cipher cipher = null;
	int keySize = 24; // 3DES key size
	
	hashKey = calcHash(key.getBytes("ISO-8859-1"), HASH_TYPE_SHA1); 
	System.out.println( "Password Hash=" + hexString(hashKey));	
	
	byte[] derivedKey = cryptDeriveKey(hashKey, keySize, HASH_TYPE_SHA1);  
	System.out.println( "Derived Key " + hexString(derivedKey)); 
	addParityBit(derivedKey);
	System.out.println( "Derived Key " + hexString(derivedKey));
	// addParityBit(derivedKey) 
	addParityBit(derivedKey) ; 
	
	byte[] reverseDK = new byte[ derivedKey.length ];
	// reverse the derivedKey
	for (int i = 0; i < derivedKey.length; i++) {
		reverseDK[i] = derivedKey[  derivedKey.length - 1 -i ] ;
	}  

	byte[] sessionKey = calcHash(reverseDK, HASH_TYPE_SHA1); 
	
	System.out.println( "ReverseDK " + hexString(reverseDK));
	System.out.println( "SessionKey Hash=" + hexString(sessionKey));	
		
	SecretKey secretKeySpec = new SecretKeySpec(derivedKey,0, keySize, "DESede"); // DESede  
	//Key raw = secretKeySpec;
	cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");          //DESede/CBC/PKCS5Padding
	
	byte[] iv = new byte[8]; //cipher.getIV(); 	
	// Preenche os buffers
	Arrays.fill( iv, (byte)0x00);  
	IvParameterSpec spec = new IvParameterSpec(iv);	
	
	//IvParameterSpec spec = new IvParameterSpec(new byte[] {0,0,0,0,0,0,0,0});
	
	System.out.println(secretKeySpec.getFormat());  
	
	//Key araw = secretKeySpec.getEncoded();
    
	cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec  , spec);  //secretKeySpec
	byte[] cryptData = cipher.doFinal(data.getBytes("ISO-8859-1"));
   
	return cryptData;
} 

private static byte[] cryptDeriveKey(byte[] key, int size, int algorithmType) 
 throws Exception 
{
	byte[] buffer1  = new byte[64];
	byte[] buffer2  = new byte[64];
	
	// Preenche os buffers
	Arrays.fill(buffer1, (byte)0x36);
	Arrays.fill(buffer2, (byte)0x5C);   
	
	// Faz o XOR da chave com os buffers
	for (int i = 0; i < key.length; i++) {
		buffer1[i] ^= key[i]; 
		buffer2[i] ^= key[i]; 
	}
	
	// Calcula o hash utilizando o mesmo algoritmo utilizado na chave passada
	try {
	buffer1 = calcHash(buffer1, algorithmType);
	buffer2 = calcHash(buffer2, algorithmType);
	} catch (NoSuchAlgorithmException e) {
	throw new Exception(e);
	}
	
	// Transporta os bytes dos buffers para a chave final até o tamanho desejado
	byte[] derivedKey = new byte[size];
	for (int i = 0; i < size; i++) {
	if(i < buffer1.length)
	derivedKey[i] = buffer1[i];
	else 
	derivedKey[i] = buffer2[i - buffer1.length]; 
	}
	
	return derivedKey;
} 

/* Add parity to a key */
public final void addParityBit(byte[] key) {
       for (int i=0; i<key.length; i++) {
               key[i] = (byte)parity(key[i]);
       }
}       

public final int parity(int n) {
       int d = (n &= 0xFE);
       while (d != 0) {
               d &= d-1;
               n ^= 1;
       }
       return n^1;             /* Odd */
} 

public static void main(String[] args) 
{  
	
    String message = "test password"; 
    String sdata = "The book is on the table";
    if (args.length > 0) message = args[0];   
    
    byte[] ar1 = message.getBytes();
    System.out.println(ar1.length);     
  
 	 Crypto dk1 = new Crypto(); 
 	 byte[] aout = null;
 	 try {
 	 aout = dk1.crypt(sdata, message, HASH_TYPE_SHA1); 
 	 System.out.println( "len of out=" +  aout.length);
 	 System.out.println( "cryptData=" + hexString(aout));	 	   
 	 
 	 } catch (Exception e) {  
 	 System.out.println("error");
 	 }
 	 //byte[] ar2 = null; 
 	 
}

}
  
/*
13
Password Hash=2ceb02a85f6d4de6c28b2e59fda886d526dafb0d
Derived Key 8f6c6e3e9bea9847d277f61a99a3a90be0d574e610a1d52c
Derived Key 8f6d6e3e9bea9846d376f71a98a2a80be0d575e610a1d52c
ReverseDK 2cd5a110e675d5e00ba8a2981af776d34698ea9b3e6e6d8f
SessionKey Hash=1293d712c2cdd2791d432fce58c433ca3141aa77
RAW
len of out=32
cryptData=50163bd5d902d3b8c254060f4418da4558d47cb6ab92ec31753a8818d48d312e
*/
