
import javax.crypto.SealedObject;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.math.BigInteger;

/**
 * Created by IntelliJ IDEA.
 * User: Johnson Gong
 * Date: 2006-3-11
 * Time: 4:15:20
 * To change this template use File | Settings | File Templates.
 */
public class CA_RD {
    public static void main(String args[]) {
        try {
            String RLfile = "revocationlist";
            String CAaddress = "localhost";
            int CAport;

            if (args.length > 0) {
                CAport = Integer.parseInt(args[0]);
            } else {
                FileReader fr = new FileReader("config");
                BufferedReader in = new BufferedReader(fr);
                in.readLine();
                in.readLine();
                CAport = Integer.parseInt(in.readLine());
                in.close();
                fr.close();
            }

            ServerSocket srvr = new ServerSocket(CAport);

            //String badwords[] = load_badwords();

            //You'll need to read "ca.priv"

            for (; ;) {
                Socket skt = srvr.accept();
                System.out.print("[incoming message...]\n");

                ObjectInputStream objin = new ObjectInputStream(skt.getInputStream());
                //byte[] encryptedAESKey = new byte[128];

                //objin.read(encryptedAESKey);
                Squeal_RD sql = (Squeal_RD) objin.readObject();

                System.out.println("Finish reading object");
                objin.close();

                //TODO:
                //1. Read sender's public key (e.g. "alice.pub")
                //2. Decrypt and verify message
                //3. If message is banned then append sender to revocation list
                //   Should check if sender is already on list.
                //   This list must be signed by the CA!

                String sender = sql.getSender();
                BigInteger sig = sql.getSig();
                String badWord = sql.getBadWord();




                //Read sender's public key
                BigInteger[] sender_PublicRDKey = Helper.readPublicKey_RD(sender);
                BigInteger N = sender_PublicRDKey[0];
                BigInteger a = sender_PublicRDKey[1];

                //Verify sender's signature
                MessageDigest md  = MessageDigest.getInstance("SHA1");
                BigInteger hi = new BigInteger(md.digest(badWord.getBytes()));
                hi = getPositiveValue(hi);
                hi = hi.mod(N);
                hi = getPrime(hi,N);


                if (sig.modPow(hi, N).compareTo(a) != 0) {
                    System.out.println("Signature not verified");
                    System.exit(0);
                }


                //Decrypt and verify message
                if (Helper.is_banned(badWord)) {
                    System.out.print("[banned message detected. updating revocation list...]\n");

                    if (!Helper.verifyRevocationList()) {
                        ClearRL.clear();
                    }

                    FileInputStream fis = new FileInputStream("revocationlist");
                    ObjectInputStream ois = new ObjectInputStream(fis);
                    SignedObject signedRL = (SignedObject) ois.readObject();
                    ois.close();
                    fis.close();

                    String[] rl = (String[]) signedRL.getObject();

                    String newrl[] = new String[rl.length + 1];
                    int i;
                    for (i = 0; i < rl.length; i++) newrl[i] = rl[i];
                    newrl[i] = sender;

                    Signature signature = Signature.getInstance("SHA1withRSA");
                    PrivateKey ca_PrivateKey = Helper.readPrivateKey("ca");
                    SignedObject signedNewList = new SignedObject(newrl, ca_PrivateKey, signature);
                    FileOutputStream fos = new FileOutputStream(Helper.R_LIST);
                    ObjectOutputStream objout = new ObjectOutputStream(fos);
                    objout.writeObject(signedNewList);
                    objout.close();
                    fos.close();
                }

                //System.out.print("[end of message]\n");
                skt.close();
            }

            //Note: ideally there should be a more graceful way to stop the server
            //and then we'd call srvr.close();

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e);
            //System.out.print("Error\n");
        }
    }

    private static String[] load_badwords() {
        try {
            FileInputStream fis = new FileInputStream("banned");
            ObjectInputStream ois = new ObjectInputStream(fis);
            String badwords[] = (String[]) ois.readObject();
            ois.close();
            fis.close();
            return badwords;
        } catch (Exception e) {
            System.out.print("Can't read banned words\n");
            String empty[] = new String[0];
            return empty;
        }
    }

    private static boolean is_banned(String message, String badwords[]) {
        int i;
        int n = badwords.length;
        int index;
        for (i = 0; i < n; i++) {
            index = message.indexOf(badwords[i]);
            if (index >= 0) return true;
        }
        return false;
    }

    private static BigInteger getPrime(BigInteger n, BigInteger N) {
        BigInteger two = BigInteger.ONE.add(BigInteger.ONE);
        if (n.mod(new BigInteger("2")).compareTo(BigInteger.ZERO) == 0) {
            n = n.add(BigInteger.ONE).mod(N);
        }
        while (!n.isProbablePrime(CERTAINTY)) {
            n = n.add(two).mod(N);
        }
        return n;
    }

    private static BigInteger getPositiveValue(BigInteger n) {
        if (n.compareTo(BigInteger.ZERO) == -1) {
            return n.negate();
        } else
            return n;
    }
    private static final int CERTAINTY = 30;
}
