Package edu.drexel.cs544.mcmuc.actions

Source Code of edu.drexel.cs544.mcmuc.actions.Runner

package edu.drexel.cs544.mcmuc.actions;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.json.JSONException;
import org.json.JSONObject;

import edu.drexel.cs544.mcmuc.channels.Channel;
import edu.drexel.cs544.mcmuc.channels.Controller;
import edu.drexel.cs544.mcmuc.channels.Room;
import edu.drexel.cs544.mcmuc.util.Certificate;
import edu.drexel.cs544.mcmuc.util.JSON;

/**
* A chat room message action must carry a "from" field, which is a nickname that identifies
* the sender, and a "body" field that carries the user's input. Messages can also optionally
* contain a "to" value, for which clients can choose to hide from the user messages not
* meant for them. Messages can also be optionally encrypted with the public key certificate
* specified by "key". Using "to" and "key" together allows clients to avoid checking the "key"
* against their own keystore, as they will know the messages was not directed at them.
*
* The possible JSON formats are:
* {'action':'message','from':'<from>','body':'<body>'}
* {'action':'message','from':'<from>','body':'<body>','to','<to>'}
* {'action':'message','from':'<from>','body':'<body>','key','<key>'}
* {'action':'message','from':'<from>','body':'<body>','to','<to>','key','<key>'}
*
*/
public class Message extends Action implements JSON {

    public static final String action = "message";
    private String from;
    private String body;
    private String to;
    private Boolean hasTo;
    private Certificate key;
    private Boolean hasKey;

    /**
     * Initializes the Message class. If either to or key are null, then hasTo() or hasKey() will return
     * false, respectively.
     *
     * @param from String from
     * @param body String body
     * @param to String to
     * @param key Certificate key
     */
    private void init(String from, String body, String to, Certificate key, boolean encrypt) {
        this.from = from;
        this.body = body;
        if (to != null) {
            this.to = to;
            this.hasTo = true;
        } else
            this.hasTo = false;
        if (key != null) {
            this.key = key;
            this.hasKey = true;
            if(encrypt)
              encryptBody(key);
        } else
            this.hasKey = false;
    }

    /**
     * Calls init() to create message with a from and body but not to or certificate
     *
     * @param from String from
     * @param body String body
     */
    public Message(String from, String body) {
        init(from, body, null, null, false);
    }

    /**
     * Calls init() to create message with a from, body, and to but no certificate
     *
     * @param from String from
     * @param body String body
     * @param to String to
     */
    public Message(String from, String body, String to) {
        init(from, body, to, null, false);
    }

    /**
     * Calls init() to create message with a from, body, and certificate but no to
     *
     * @param from String from
     * @param body String body
     * @param key Certificate key
     */
    public Message(String from, String body, Certificate key) {
        init(from, body, null, key, true);
    }

    /**
     * Calls init() to create message with a from, body, to, and certificate
     *
     * @param from String from
     * @param body String body
     * @param to String to
     * @param key Certificate key
     */
    public Message(String from, String body, String to, Certificate key) {
        init(from, body, to, key, true);
    }

    /**
     * Decrypts the body of the message, using the provided private key certificate of the user
     * that received the message. Only X.509 certificates using RSA privates keys are currently supported.
     *
     * @param Private Certificate for the receiver of the message.
     */
    public void decryptBody(Certificate PrivateKey) {
        InputStream inStream = new ByteArrayInputStream(PrivateKey.getCertificate());
        try {
            byte[] keyBytes = new byte[inStream.available()];
            inStream.read(keyBytes);

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);

            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            byte[] textBytes = cipher.doFinal(Certificate.stringToBytes(body));
            this.body = new String(textBytes);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

    }

    /**
     * Encrypts the body of the message, using the provided public key certificate of the intended
     * recipient. Only X.509 certificates using RSA public keys are currently supported.
     *
     * @param PublicKey Certificate for the message receipent's public key
     */
    public void encryptBody(Certificate PublicKey) {
        InputStream inStream = new ByteArrayInputStream(PublicKey.getCertificate());
        CertificateFactory cf;
        try {
            cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
            RSAPublicKey key = (RSAPublicKey) cert.getPublicKey();

            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            byte[] cipherText = cipher.doFinal(this.body.getBytes());
            this.body = Certificate.bytesToString(cipherText);

        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
    }

    /**
     * Deserializes JSON into a Message object. If the JSON has to or key key/value pairs, the object's
     * to or key attributes are filled in and hasTo() or hasKey() return true; otherwise they return false.
     *
     * @param json the JSONObject
     */
    public Message(JSONObject json) {
        super(json, Message.action);
        try {
            String from = json.getString("from");
            String body = json.getString("body");

            String to = null;
            Certificate key = null;

            if (json.has("to"))
                to = json.getString("to");

            if (json.has("key"))
                key = new Certificate(json.getJSONObject("key"));

            init(from, body, to, key, false);

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * Accessor for message's from field
     *
     * @return String from
     */
    public String getFrom() {
        return from;
    }

    /**
     * Accessor for message's body field
     *
     * @return String body
     */
    public String getBody() {
        return body;
    }

    /**
     * Accessor for message's (optional) to field
     *
     * @return String to (null if hasTo() returns false)
     */
    public String getTo() {
        return to;
    }

    /**
     * Determine if message's optional to field is filled
     *
     * @return true if message has 'to' address, false otherwise
     */
    public Boolean hasTo() {
        return hasTo;
    }

    /**
     * Accessor for message's Certificate key field
     *
     * @return Certificate key (null if hasKey() returns false)
     */
    public Certificate getKey() {
        return key;
    }

    /**
     * Determine if message's optional key field is filled
     *
     * @return true if message has Certificate key, false otherwise
     */
    public Boolean hasKey() {
        return hasKey;
    }

    /**
     * Serializes the Message object to JSON. The to and key key/value pairs are only included if
     * the object was created with a to or key attribute.
     */
    @Override
    public JSONObject toJSON() {
        JSONObject json = new JSONObject();

        try {
            json.put("action", "message");
            json.put("uid", uid);
            json.put("from", from);
            json.put("body", body);
            if (hasTo)
                json.put("to", to);
            if (hasKey)
                json.put("key", key.toJSON());
        } catch (JSONException e) {

        }

        return json;
    }

    /**
     * Display the message to the user and forward it on the channel
     */
    public void process(Channel channel) {
        class Runner implements Runnable {
            Message message;
            Channel channel;

            Runner(Message m, Channel c) {
                channel = c;
                message = m;
            }

            public void run() {
                if (message.hasKey()) {
                    Room r = (Room) channel;
                    Certificate privateKey = r.getKeyPairs().get(message.getKey());
                    if (privateKey != null)
                        message.decryptBody(privateKey);
                }
                if (message.hasTo()) {
                    Room r = (Room) channel;
                    if (r.getUserName().equals(message.getTo()))
                        Controller.getInstance().output(message.getFrom() + "@" + ((Room) channel).getName() + " (private): " + message.getBody() + " (" + message.getUID() + ")");
                } else
                    Controller.getInstance().output(message.getFrom() + "@" + ((Room) channel).getName() + ": " + message.getBody() + " (" + message.getUID() + ")");
                channel.send(message);
            }
        }
        Thread t = new Thread(new Runner(this, channel));
        t.start();
    }
}
TOP

Related Classes of edu.drexel.cs544.mcmuc.actions.Runner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.