//package IM;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.*;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.math.BigInteger;

/**
 * Created by IntelliJ IDEA.
 * User: Johnson Gong
 * Date: 2006-3-6
 * Time: 16:33:48
 * To change this template use File | Settings | File Templates.
 */
public class Helper {
    public final static  byte[] iv = {(byte) 0xcb, (byte) 0x53, (byte) 0x03, (byte) 0x0f,
                                            (byte) 0xe0, (byte) 0x79, (byte) 0x9d, (byte) 0xdc,
                                            (byte) 0x80, (byte) 0xa9, (byte) 0x83, (byte) 0xf1,
                                            (byte) 0x03, (byte) 0xb6, (byte) 0x59, (byte) 0x83};
    public final static String R_LIST = "revocationlist";
    public final static String RSA_A = "RSA";
    public final static String AES_A = "AES/CBC/PKCS5Padding";
    //public final static String AES_A = "AES";
    public final static String RSA_SIGN = "SHA1withRSA";

    public static PublicKey readPublicKey(String username) throws BadCertificateException {
        try {

            FileInputStream fis = new FileInputStream(username + ".pub");
            ObjectInputStream ois = new ObjectInputStream(fis);

            SignedObject certificate = (SignedObject) ois.readObject();
            UserKeyPair pair = (UserKeyPair)certificate.getObject();
            PublicKey publicKey = pair.getPublicKey();
            String realUsername = pair.getUsername();


            //Get CA's public key
            PublicKey ca_PublicKey = Helper.readCAPublicKey();

            Signature sig = Signature.getInstance(Helper.RSA_SIGN);

            if (!certificate.verify(ca_PublicKey, sig))
                throw (new BadCertificateException(username + "has bad certificate"));
            else{
                if (!username.equals(realUsername)){
                    throw (new BadCertificateException("Username doesn't match"));
                }
                else
                    return publicKey;
            }
                //return (PublicKey) signedPublicKey.getObject();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (SignatureException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.out.println(e);
        }
        return null;
    }

    public static PublicKey readCAPublicKey() {
        PublicKey ca = null;
        try {
            FileInputStream fis = new FileInputStream("ca.pub");
            ObjectInputStream ois = new ObjectInputStream(fis);
            ca = (PublicKey) ois.readObject();
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
            System.out.println("Exception happens when trying to read CA's public key");
        }
        return ca;
    }

    public static PrivateKey readPrivateKey(String username) {
        try {

            FileInputStream fis = new FileInputStream(username + ".priv");
            ObjectInputStream ois = new ObjectInputStream(fis);
            PrivateKey privateKey = (PrivateKey) ois.readObject();
            return privateKey;
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
        return null;

    }


    public static BigInteger[] readPrivateKey_RD(String username) {
        try {
            FileInputStream fis = new FileInputStream(username + ".rdpriv");
            ObjectInputStream ois = new ObjectInputStream(fis);
            BigInteger p = (BigInteger)ois.readObject();
            BigInteger q = (BigInteger)ois.readObject();
            BigInteger[] result = new BigInteger[2];
            result[0] = p;
            result[1] = q;
            return result;
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
        return null;

    }

    public static BigInteger[] readPublicKey_RD(String username) throws BadCertificateException {
        try {

            FileInputStream fis = new FileInputStream(username + ".rdpublic");
            ObjectInputStream ois = new ObjectInputStream(fis);

            SignedObject certificate = (SignedObject) ois.readObject();
            UserKeyPair_RD pair = (UserKeyPair_RD) certificate.getObject();
            BigInteger[] publicKey = pair.getPublicKey();
            String realUsername = pair.getUsername();


            //Get CA's public key
            PublicKey ca_PublicKey = Helper.readCAPublicKey();

            Signature sig = Signature.getInstance(Helper.RSA_SIGN);

            if (!certificate.verify(ca_PublicKey, sig))
                throw (new BadCertificateException(username + "has bad certificate"));
            else {
                if (!username.equals(realUsername)) {
                    throw (new BadCertificateException("Username doesn't match"));
                } else
                    return publicKey;
            }
            //return (PublicKey) signedPublicKey.getObject();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (SignatureException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println(e);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.out.println(e);
        }
        return null;
    }


    public static boolean verifyRevocationList() {
        try {

            PublicKey ca_PublicKey = Helper.readCAPublicKey();
            FileInputStream fis = new FileInputStream("revocationlist");
            ObjectInputStream ois = new ObjectInputStream(fis);
            SignedObject rlSign = (SignedObject) ois.readObject();
            Signature signature = Signature.getInstance(Helper.RSA_SIGN);
            if (!rlSign.verify(ca_PublicKey, signature)) {
                throw (new BadRevocationListException("Revocation List Corrupted"));
            } else
                return true;
        } catch (BadRevocationListException e) {
            e.printStackTrace();
            System.out.println(e);
            return false;
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        }
        return false;

    }
    public static boolean isUserRevoked(String username){
        boolean is_revoked = false;
        try {
            if (!Helper.verifyRevocationList()) {
                return false;
            }
            FileInputStream fis = new FileInputStream("revocationlist");
            ObjectInputStream ois = new ObjectInputStream(fis);
            SignedObject signedList = (SignedObject)ois.readObject();
            String rl[] = (String[]) signedList.getObject();
            ois.close();
            fis.close();
            for (int i = 0; i < rl.length; i++) {
                if (rl[i].equals(username)) {
                    is_revoked = true;
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e);
            System.out.print("Error\n");
        }
        return is_revoked;

    }

    public static boolean is_banned(String message){//, String badwords[]) {

        ArrayList words = new ArrayList();

        //Break message into words
        StringTokenizer st = new StringTokenizer(message);
        while (st.hasMoreTokens()) {
            words.add(st.nextToken());
        }

        byte[][] bannedWordsHash = new byte[1][1];
        try{
            //Read bannedWordsHash from file
            FileInputStream fis = new FileInputStream(Helper.BANNED_WORDS);
            ObjectInputStream ois = new ObjectInputStream(fis);
            bannedWordsHash = (byte[][])ois.readObject();
        }
        catch(Exception e){
            e.printStackTrace();
            System.err.println("Cannot read bad words");
        }

        try{

            //Hash every word in the message, and check
            MessageDigest md = MessageDigest.getInstance("SHA1");
            for (int i = 0 ; i < words.size(); i++){
                byte[] hashedWd = md.digest(((String) words.get(i)).getBytes());               

                //traverse the bannedWordsHash
                for (int j = 0; j < bannedWordsHash.length; j++){
                    if (compareByteArray(bannedWordsHash[j],hashedWd)){
                        return true;
                    }
                }
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }

        return false;
    }

    public static int is_banned_RD(String message) {//, String badwords[]) {

        ArrayList words = new ArrayList();

        //Break message into words
        StringTokenizer st = new StringTokenizer(message);
        while (st.hasMoreTokens()) {
            words.add(st.nextToken());
        }

        byte[][] bannedWordsHash = new byte[1][1];
        try {
            //Read bannedWordsHash from file
            FileInputStream fis = new FileInputStream(Helper.BANNED_WORDS);
            ObjectInputStream ois = new ObjectInputStream(fis);
            bannedWordsHash = (byte[][]) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Cannot read bad words");
        }

        try {

            //Hash every word in the message, and check
            MessageDigest md = MessageDigest.getInstance("SHA1");
            for (int i = 0; i < words.size(); i++) {
                byte[] hashedWd = md.digest(((String) words.get(i)).getBytes());

                //traverse the bannedWordsHash
                for (int j = 0; j < bannedWordsHash.length; j++) {
                    if (compareByteArray(bannedWordsHash[j], hashedWd)) {
                        return i;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return -1;
    }

    private static boolean compareByteArray(byte[] a , byte[] b){
        if (a.length != b.length){
            return false;
        }
        for (int i = 0; i < a.length; i++){
            if (a[i] != b[i]){
                return false;
            }
        }
        return true;
    }

    public static final String BANNED_WORDS = "banned";


}
