Você está na página 1de 24

UNIVERSIDADE DO MINHO

LICENCIATURA EM ENGENHARIA E GESTÃO DE SISTEMAS DE INFORMAÇÃO

PROJETO 2022/2023:
FUNDAMENTOS DE SISTEMAS DISTRIBUÍDOS
PL1 – Grupo 4

3ª ENTREGA
Relatório de Código

A97468 A95144 A95822

Guilherme José de Sousa Inês Capa de Barros Maria Beatriz Garcez Morais
Barbosa
PresencesServer
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

public class PresencesServer {


private static Presences presences = new Presences(120); //timeout
de 2 minutos
private static ArrayList<AtendePedidoTCP> listaUtilizadores = new
ArrayList<>();
private static ArrayList<String> dezMensagensMaisRecentes = new
ArrayList<>();

static int DEFAULT_PORT = 2000;

public ArrayList<AtendePedidoTCP> getListaUtilizadores() {


return listaUtilizadores;
}

public void setListaUtilizadores(ArrayList<AtendePedidoTCP>


listaUtilizadores) {
this.listaUtilizadores = listaUtilizadores;
}

public ArrayList<String> getDezMensagensMaisRecentes() {


return dezMensagensMaisRecentes;
}

public void setDezMensagensMaisRecentes(ArrayList<String>


dezMensagensMaisRecentes) {
this.dezMensagensMaisRecentes = dezMensagensMaisRecentes;
}

public PresencesServer(Presences presences) {


this.presences = presences;
}

public Presences getPresences() {


return presences;
}

public void setPresences(Presences presences) {


this.presences = presences;
}

public static void iniciar() throws IOException {


Scanner scanner = new Scanner(System.in);

System.out.println("Qual é a porta que deseja operar?");


DEFAULT_PORT = scanner.nextInt();

System.out.println("Qual é o valor do parametro


SESSION_TIMEOUT que deseja?");
int timeOut = scanner.nextInt();

ServerSocket servidor = null;

Presences presences = new Presences(timeOut);


PresencesServer presencesServer = new
PresencesServer(presences);

servidor = new ServerSocket(DEFAULT_PORT);


System.out.println("Servidor a' espera de ligacoes no porto "
+ DEFAULT_PORT);
iniciarLoop();

while (true) {
try {
Socket ligacao = servidor.accept();
AtendePedidoTCP atendePedidoTCP = new
AtendePedidoTCP(ligacao, presencesServer);
atendePedidoTCP.start();
listaUtilizadores.add(atendePedidoTCP);//adiciona um
novo utilizador à lista de atendePedidoTCP

} catch (IOException e) {
System.out.println("Erro na execucao do servidor: " +
e);
System.exit(1);
}

}
}

private static void iniciarLoop() {


Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
sessionTimeOut();
} catch (IOException e) {
e.printStackTrace();
}

}
}, 1 * 1000, 10 * 1000); //repete-se a cada segundo
}

public static void sessionTimeOut() throws IOException {


Vector<String> utilizadoresAtivos =
presences.getNicknameList();
ArrayList<AtendePedidoTCP> utilizadoresInativos = new
ArrayList<>();

for (AtendePedidoTCP atendePedidoTCP : listaUtilizadores) {


if
(!utilizadoresAtivos.contains(atendePedidoTCP.getNickname())) {
//utilizadores que nao estiverem na lista dos ativos, mas estiverem
na listaUtilizadores
utilizadoresInativos.add(atendePedidoTCP);
//adicionamos à lista de utilizadores inativos
}
}
for (AtendePedidoTCP a : utilizadoresInativos) {
a.getOut().println("SESSION_TIME_OUT");
a.getIn().close();
a.getOut().close();
a.getLigacao().close();
listaUtilizadores.remove(a); //removemos da lista de todos
os utilizadores
}
}

public static void main(String[] args) {


try {
iniciar();
} catch (IOException e) {
e.printStackTrace();
}
}

}
Presences
import java.security.PublicKey;
import java.util.Base64;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

public class Presences {


int time_Out;

public Presences(int time_Out) {


this.time_Out = time_Out;
}

private static Hashtable<String, IPInfo> presentIPs = new


Hashtable<String, IPInfo>();
private static int cont = 0;

public Vector<String> getPresences(String IPAddress, String


nickname, boolean rmi, PublicKey publicKey) {
long actualTime = new Date().getTime();
cont = cont + 1;

//Assume-se que o IP e valido!!!!!


synchronized (this) {
if (presentIPs.containsKey(IPAddress)) {
IPInfo newIp = presentIPs.get(IPAddress);
newIp.setLastSeen(actualTime);
} else {
IPInfo newIP = new IPInfo(IPAddress, actualTime,
nickname, rmi, publicKey);
presentIPs.put(IPAddress, newIP);
}
}
return getNicknameList();
}

public Vector<String> getNicknameList() {


Vector<String> result = new Vector<String>();

for (IPInfo element : presentIPs.values()) {


if (!element.timeOutPassed(time_Out * 1000)) {
result.add(element.getNickname()); //enquanto os 2
minutos nao passarem, o utilizador continua a aparecer online
}

}
return result;
}

public Vector<String> getNicknameIpRmiPublicKey() {


Vector<String> result = new Vector<String>();
for (IPInfo element : presentIPs.values()) {
if (!element.timeOutPassed(time_Out * 1000)) {
//passar a publicKey para String
String publicKeyString =
Base64.getEncoder().encodeToString(element.getPublicKey().getEncoded()
);
result.add(element.getNickname() + " " +
element.getIP() + " " + element.getRmi() + " " + publicKeyString);
//enquanto os 2 minutos nao passarem, o utilizador continua a aparecer
online
}
}
return result;
}
}
IPInfo

class IPInfo {
private String nickname; //guardar o nickname também
private String ip;
private long lastSeen;
private boolean rmi;
private PublicKey publicKey;

public IPInfo(String ip, long lastSeen, String nickname, boolean


rmi, PublicKey publicKey) {
this.ip = ip;
this.lastSeen = lastSeen;
this.nickname = nickname;
this.rmi = rmi;
this.publicKey = publicKey;
}

public PublicKey getPublicKey() {


return publicKey;
}

public void setPublicKey(PublicKey publicKey) {


this.publicKey = publicKey;
}

public String getIP() {


return this.ip;
}

public void setLastSeen(long time) {


this.lastSeen = time;
}

public String getNickname() {


return nickname;
}

public boolean getRmi() {


return rmi;
}

public void setNickname(String nickname) {


this.nickname = nickname;
}

public boolean timeOutPassed(int timeout) {


boolean result = false;
long timePassedSinceLastSeen = new Date().getTime() -
this.lastSeen;
if (timePassedSinceLastSeen >= timeout) result = true;
return result;
}
}
AtendePedidoTCP
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class AtendePedidoTCP extends Thread {

private BufferedReader in;


private PrintWriter out;
private String nickname;
private Socket ligacao;
private PresencesServer presencesServer;
private boolean rmi;
private PublicKey publicKey;

public BufferedReader getIn() {


return in;
}

public void setIn(BufferedReader in) {


this.in = in;
}

public PrintWriter getOut() {


return out;
}

public void setOut(PrintWriter out) {


this.out = out;
}

public void setNickname(String nickname) {


this.nickname = nickname;
}

public Socket getLigacao() {


return ligacao;
}

public void setLigacao(Socket ligacao) {


this.ligacao = ligacao;
}

public AtendePedidoTCP(Socket Ligacao, PresencesServer


presencesServer) throws IOException { //inicializar as componentes do
AtendePedidoTCP
this.in = new BufferedReader(new
InputStreamReader(Ligacao.getInputStream()));
this.out = new PrintWriter(Ligacao.getOutputStream(), true);
this.ligacao = Ligacao;
this.presencesServer = presencesServer;
}

public String getNickname() {


return nickname;
}

public void run() {


String pedido;
try {
while (ligacao.isConnected()) {
pedido = in.readLine(); //lê o pedido
if (pedido != null) {
verificarTipomensagem(pedido); //filtra o pedido
por tipo
}
}
} catch (IOException e) {
//throw new RuntimeException(e);
}
}

private void verificarTipomensagem(String mensagemPortratar)


throws IOException {
String tipoMensagem, mensagem;
String[] sp = mensagemPortratar.split(":"); // dividir as
string num array de strings
tipoMensagem = sp[0]; //separar o cabeçalho do nickname

int indexOfSpace = mensagemPortratar.indexOf(':'); // buscar


o index do primeiro :
mensagem = mensagemPortratar.substring(indexOfSpace + 1); //
buscar a string depois do primeiro :
System.out.println(mensagem);
responderTipoPedido(tipoMensagem, mensagem); //responder ao
pedido do utilizador conforme o tipo de pedido feito
}

public void responderTipoPedido(String tipoPedido, String


mensagem) throws IOException { //temos dois tipos de mensagem
switch (tipoPedido) {
case "SESSION_UPDATE_REQUEST":
sessionUpdateRequest(mensagem);
break;
case "AGENT_POST":
agentPost(nickname + ":" + mensagem); // agent post é
para enviar a mensagem para o feed na estrutura "nickname:post"
break;
}
}

private void atualizarPresencas() {

presencesServer.getPresences().getPresences(String.valueOf(ligacao.get
InetAddress()), nickname, rmi, publicKey); //atualizamos com a publick
key
System.out.println(ligacao.getInetAddress());
}

//adiciona a mensagem à lista das 10 mensagens mais recentes


private void agentPost(String mensagem) throws IOException {
adicionarMensagensMaisRecentes(mensagem);
atualizarPresencas(); //cada vez que ele faz um AgentPost
atualizamos o tempo.
enviarSessionUpdateParaTodosUtilizadores();
}

private void sessionUpdateRequest(String mensagem) throws


IOException {
String[] sp = mensagem.split(":"); // dividir as string num
array de strings

int indexOfSpace = mensagem.indexOf(':');

this.rmi = Boolean.valueOf(sp[1]);
//fazer a mesma cena para ir buscar a public key

//Recebemos a string publickey


String publicKeyString = sp[2];

try {
//Vamos ter que apartir de uma string, obter os bytes e
posteriormente a chave publica do cliente.
KeyFactory factory = KeyFactory.getInstance("RSA",
"SunRsaSign");
byte[] decodedBytes =
Base64.getDecoder().decode(publicKeyString);
publicKey = (PublicKey) factory.generatePublic(new
X509EncodedKeySpec(decodedBytes));

System.out.println(publicKey);

} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}

if (nickname == null) {
nickname = sp[0];
; //se ele nao tiver nickname, entao o nickname é igual ao
que ele mandou para o servidor
}
atualizarPresencas(); //cada vez que ele faz um
sessionUpdateRequest atualizamos o tempo.
enviarSessionUpdateParaTodosUtilizadores(); //passo 9
}

private void adicionarMensagensMaisRecentes(String mensagem) {


presencesServer.getDezMensagensMaisRecentes().add(mensagem);
}

private void enviarSessionUpdateParaTodosUtilizadores() {


for (AtendePedidoTCP atendePedidoTCP :
presencesServer.getListaUtilizadores()) { //vamos buscar todos os
utilizadores online
atendePedidoTCP.out.println("SESSION_UPDATE");
atendePedidoTCP.sessionUpdate(); //para cada utilizador,
enviamos um SESSION_UPDATE
}
}

private void sessionUpdate() {


int tamanhoUtilizadores =
(presencesServer.getPresences().getNicknameIpRmiPublicKey().size());
//size = numero de utilizadores que existem
out.println(Integer.toString(tamanhoUtilizadores)); //mandar o
numero de utilizadores para o utilizador a receber

for (int i = 0; i <


presencesServer.getPresences().getNicknameIpRmiPublicKey().size();
i++) {

out.println(presencesServer.getPresences().getNicknameIpRmiPublicKey()
.get(i)); //enviar o nickname de cada cliente
}

int tamanhoMensagens =
presencesServer.getDezMensagensMaisRecentes().size();
if (tamanhoMensagens <= 10) {
out.println(Integer.toString(tamanhoMensagens));
for (int i = 0; i < tamanhoMensagens; i++) {

out.println(presencesServer.getDezMensagensMaisRecentes().get(i));
}

} else {
out.println(Integer.toString(10));
int inicio =
presencesServer.getDezMensagensMaisRecentes().size() - 10; //vemos a
primeira mensagem a enviar
int fim =
presencesServer.getDezMensagensMaisRecentes().size(); //
vemos o fim

for (int i = inicio; i < fim; i++) { //for


para percorrer as mais recentes

out.println(presencesServer.getDezMensagensMaisRecentes().get(i));
}
}
}

}
InterfaceCliente
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.Timer;

public class InterfaceCliente extends JFrame {


private JLabel nicknameBtn;
private JTextField usernameTf;
private JButton connectBtn;
private JButton disconnectBtn;
private JButton sendBtn;
private JTable table1;
private JTable table2;
private JScrollPane tabelaPresencas;
private JButton verPresençasButton;
private javax.swing.JPanel JPanel;
private JTextField tfSendMessage;
private JScrollPane tabelaMensagens;
private JTable table3;
private JTextField tfSendPrivateMessage;
private JButton sendPrivateMessageBtn;
private JScrollPane tabelaMensagensPrivadas;
private JButton sendButton;
private JTextArea taSendMessage;

private DefaultTableModel model = new DefaultTableModel();


private DefaultTableModel model2 = new DefaultTableModel();
private DefaultTableModel model3 = new DefaultTableModel();

static String SERVICE_NAME = "PrivateMessaging";


static int DEFAULT_PORT = 2000;
static String DEFAULT_HOST = "127.0.0.1";
private BufferedReader in;
private PrintWriter out;
private Socket ligacao;
private Scanner scanner;
private String nickname;
private Timer timer = new Timer();
private boolean rmi = true;
private PublicKey publicKey;
private PrivateKey privateKey;
private HashMap<String, PublicKey> listaNickPublicKey = new
HashMap<>();
private ArrayList<String> arrayList = new ArrayList<>();

private static InterfaceCliente interfaceCliente;

public InterfaceCliente(String enderecoIP, String porta, boolean


rmi) throws IOException { //inicializar a nossa interface do cliente
this.rmi = rmi;
//panel representa toda a nossa interface
setContentPane(JPanel);
setTitle("Forum");
setSize(700, 700);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
tfSendMessage.setUI(new HintTextFieldUI("Enviar para todos ",
true));

if (rmi == false) {
table3.setVisible(false);
sendPrivateMessageBtn.setVisible(false);
tfSendPrivateMessage.setVisible(false);
sendButton.setVisible(false);
} else {
table3.setModel(model3);
model3.addColumn("Mensagens Privadas: ");
}

table1.setModel(model);
model.addColumn("Mensagens"); //cabeçalho da tabela de
mensagens

table2.setModel(model2);
model2.addColumn("Utilizadores Online:"); //cabeçalho da
tabela de utilizadores online

if (!enderecoIP.isEmpty() && !porta.isEmpty()) {


DEFAULT_HOST = enderecoIP;
DEFAULT_PORT = Integer.parseInt(porta);
}

connectBtn.addActionListener(new ActionListener() { //botao


para se conectar ao chat
@Override
public void actionPerformed(ActionEvent e) {
nickname = usernameTf.getText();
try {
iniciar(true);
} catch (IOException ex) {
throw new RuntimeException(ex);
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}

if (usernameTf.getText().isBlank()) { //erro se se
tentar conectar com o campo do nickname vazio
JOptionPane.showMessageDialog(null, "Escolha um
nickname!", "ERRO!", JOptionPane.ERROR_MESSAGE);
} else {

inicarLoop(nickname, timer);
receberMensagem();
}
}
});

sendBtn.addActionListener(new ActionListener() { //botao para


enviar mensagens para o chat
@Override
public void actionPerformed(ActionEvent e) {
String mensagem = tfSendMessage.getText();
enviarMensagem(mensagem); //enviar mensagem para o
servidor
tfSendMessage.setText(""); //mete o text field vazio
depois de enviarmos a mensagem
}
});

sendPrivateMessageBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int coluna = 0;
int linha = table2.getSelectedRow();

if (linha == -1) {
JOptionPane.showMessageDialog(null, "Selecione um
utilizador para enviar mensagem privada!", "ERRO!",
JOptionPane.ERROR_MESSAGE);
} else {
separarNicknameIpRmi(linha, coluna, true); //
temos de separar os argumentos dos clientes.
String mensagemPorTratar =
model2.getValueAt(linha, coluna).toString();
String[] sp = mensagemPorTratar.split(" "); //
dividir as string num array de strings
String nick = sp[0]; //nickame
tfSendPrivateMessage.setUI(new
HintTextFieldUI("Enviar para " + nick, true));
}
}
});
sendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int coluna = 0;
int linha = table2.getSelectedRow();

if (linha == -1) {
JOptionPane.showMessageDialog(null, "Selecione um
utilizador para enviar mensagem privada!", "ERRO!",
JOptionPane.ERROR_MESSAGE);
} else {
separarNicknameIpRmi(linha, coluna, false); //
temos de separar os argumentos dos clientes.
String mensagemPorTratar =
model2.getValueAt(linha, coluna).toString();
String[] sp = mensagemPorTratar.split(" "); //
dividir as string num array de strings
String nick = sp[0]; //nickame
tfSendPrivateMessage.setUI(new
HintTextFieldUI("Enviar para " + nick, true));
}

}
});
}

//vamos separar o nickname do ip e do rmi. (Esta tudo junto na


tabela)
private void separarNicknameIpRmi(int linha, int coluna, boolean
secure) {
String mensagemPorTratar = model2.getValueAt(linha,
coluna).toString();
String nickname, ip;
boolean rmi;

String[] sp = mensagemPorTratar.split(" "); // dividir as


string num array de strings
nickname = sp[0]; //nickame

int indexOfSpace = mensagemPorTratar.indexOf(" "); // buscar


o index do primeiro :
mensagemPorTratar = mensagemPorTratar.substring(indexOfSpace +
1); // buscar a string depois do primeiro :

sp = mensagemPorTratar.split(" ");
ip = sp[0].substring(1);
indexOfSpace = mensagemPorTratar.indexOf(" ");
rmi = Boolean.valueOf(mensagemPorTratar.substring(indexOfSpace
+ 1));

if (nickname.equalsIgnoreCase(this.nickname)) { //ele não pode


mandar mensagens para si proprio
JOptionPane.showMessageDialog(null, "Não pode enviar
mensagem para si próprio!", "ERRO!", JOptionPane.ERROR_MESSAGE);
} else if (rmi == false) { //não podemos enviar mensagem para
quem não aceita receber mensagens.
JOptionPane.showMessageDialog(null, "O utilizador não
aceita mensagens privadas!", "ERRO!", JOptionPane.ERROR_MESSAGE);
} else {
String mensagemEnviar = tfSendPrivateMessage.getText();
if (secure == true) {
enviarMensagemPrivadaSegura(nickname, ip,
mensagemEnviar);
} else {
enviarMensagemPrivada(nickname, ip, mensagemEnviar);
}

System.out.println(ip);
}
}

private void enviarMensagemPrivada(String


nicknameParaQuemVamosEnviar, String ip, String mensagemEnviar) {
try {
PrivateMessaging privateMessaging = (PrivateMessaging)
LocateRegistry.getRegistry(ip).lookup(SERVICE_NAME); //acedemos à
interface de quem vai receber

String clienteQueRecebeu =
privateMessaging.sendMessage(nickname, mensagemEnviar);// escrever na
tabela para quem vamos enviar a mensagem a enviar
//adicionamos diretamente na tabela o que vamos enviar

model3.addRow(new String[]{"Enviou para:" +


nicknameParaQuemVamosEnviar + ": " + mensagemEnviar});
tfSendPrivateMessage.setText("");

} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}

private void enviarMensagemPrivadaSegura(String


nicknameParaQuemVamosEnviar, String ip, String mensagemEnviar) {
try {
//Criar o Digest
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(mensagemEnviar.getBytes()); //passamos os bytes
do que escrevemos para o messagedigest
byte[] digest = md.digest();
//Creating a Cipher object
Cipher cipher = Cipher.getInstance("RSA");
//Initializing a Cipher object
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
//Adding data to the cipher
cipher.update(digest);
//Encrypting the data
byte[] cipherText = cipher.doFinal();

String encodedString =
Base64.getEncoder().encodeToString(cipherText); //passar o sumario
enctiptado para uma string atraves do base64
PrivateMessaging privateMessaging = (PrivateMessaging)
LocateRegistry.getRegistry(ip).lookup(SERVICE_NAME); //acedemos à
interface de quem vai receber
String clienteQueRecebeu =
privateMessaging.sendMessageSecure(nickname, mensagemEnviar,
encodedString);// escrever na tabela para quem vamos enviar a mensagem
a enviar

model3.addRow(new String[]{"Enviou uma mensagem segura


para:" + nicknameParaQuemVamosEnviar + ": " + mensagemEnviar});
tfSendPrivateMessage.setText("");
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}

private void inicarLoop(String nickname, Timer timer) {


timer.schedule(new TimerTask() {
@Override
public void run() {
try {
//removemos para que nao tenhamos repetições
quando executamos o metodo
model.getDataVector().removeAllElements();
//remove todos os elementos da tabela das mensagens
model2.getDataVector().removeAllElements();
//remove todos os elementos da tabela dos utilizadores online
sessionUpdateRequest(nickname, rmi, publicKey);
//executa o metodo
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, 120 * 1000); //repete a cada 120 segundos
}

public void iniciar(boolean primeiraVez) throws IOException,


NoSuchAlgorithmException { //botao para iniciar as componentes todas
InetAddress serverAdress =
InetAddress.getByName(DEFAULT_HOST);
ligacao = new Socket(serverAdress, DEFAULT_PORT);
in = new BufferedReader(new
InputStreamReader(ligacao.getInputStream()));
out = new PrintWriter(ligacao.getOutputStream(), true);
criarRegisto();
if (primeiraVez == true) {
generarChaves();
}

private void generarChaves() throws NoSuchAlgorithmException {


//Creating KeyPair generator object
KeyPairGenerator keyPairGen =
KeyPairGenerator.getInstance("RSA");

//Initializing the KeyPairGenerator


keyPairGen.initialize(2048); // valor é indiferente

//Generate the pair of keys


KeyPair pair = keyPairGen.generateKeyPair();

//Getting the public key from the key pair


publicKey = pair.getPublic();
privateKey = pair.getPrivate();

System.out.println(nickname.toUpperCase(Locale.ROOT) + "\n" +
publicKey);
}

private void criarRegisto() throws RemoteException,


MalformedURLException {
String ip = ligacao.getInetAddress().toString().substring(1);
try {
LocateRegistry.createRegistry(1099);
EnviarMensagem enviarReceberMensagens = new
EnviarMensagem(model3, nickname, listaNickPublicKey);
LocateRegistry.getRegistry("127.0.0.1",
1099).rebind(SERVICE_NAME, enviarReceberMensagens);
} catch (Exception e) {
System.err.println("Erro");
}

public void sessionUpdateRequest(String nickname, boolean rmi,


PublicKey publicKey) throws IOException {
//Vamos converter a chave publica para String para ser enviada
pelo socket.(Vamos esperar que a string não tenha ":" ehehehe)
String publicKeyString =
Base64.getEncoder().encodeToString(publicKey.getEncoded());
out.println("SESSION_UPDATE_REQUEST:" + nickname + ":" + rmi +
":" + publicKeyString); //envia para o servidor o nickname do
utilizador quando ele se conecta
}

public void enviarMensagem(String mensagemEnviar) {


out.println("AGENT_POST:" + mensagemEnviar); //envia para o
servidor a mensagem que o utilizador pretende enviar
}

public void receberMensagem() { //se a mensagem recebida for


session_update, remove todos os elementos da tabela das mensagens e da
tabela dos utilizadores online
new Thread(new Runnable() {
@Override
public void run() {
String mensagemRecebida;

while (ligacao.isConnected()) {
try {
mensagemRecebida = in.readLine();
if (mensagemRecebida.equals("SESSION_UPDATE"))
{
model.getDataVector().removeAllElements();

model2.getDataVector().removeAllElements();
receberSessionUpdate();

} else if
(mensagemRecebida.equals("SESSION_TIME_OUT")) {
in.close();
out.close();
ligacao.close();
timer.cancel();
model.getDataVector().removeAllElements();

model2.getDataVector().removeAllElements();
model.fireTableDataChanged();
model2.fireTableDataChanged();

int dialogButton =
JOptionPane.showConfirmDialog(null, "Gostaria de se reconectar? ",
"voce foi desconectado", JOptionPane.YES_NO_OPTION);
if (dialogButton ==
JOptionPane.YES_OPTION) {
iniciar(false);
Timer timer2 = new Timer(); //
inicializamos o timer que vai fazer o SESSION_Update_request a cada
20s
inicarLoop(nickname, timer2);
receberMensagem();

} else {
dispose();
}

break;
} else {

}
} catch (IOException | NoSuchAlgorithmException e)
{
System.out.println("Erro ao comunicar com o
servidor ");
}
}

}
}).start();

private void receberSessionUpdate() {


listaNickPublicKey.clear(); //sempre que recebermos um
SessionUpdate damos clear ao cenas.
int tamanhoPresencas = 0;
try {
tamanhoPresencas = Integer.parseInt(in.readLine());

} catch (IOException e) {
throw new RuntimeException(e);
}
//percorre o array de presenças e imprime o nickname dos
utilizadores online / o metodo conectar(...) retorna um array c os
nicknames todos
for (int i = 0; i < tamanhoPresencas; i++) {
try {
String mensagem = in.readLine(); // ler o que vêm do
socket

String[] splitMensagem = mensagem.split(" ");


//dividir em array de strings
String nick = splitMensagem[0]; // o [0] é o nickname
String publicKeyString = splitMensagem[3]; // [3] é a
publicKey

atualizarNicknamePublicKey(nick, publicKeyString);

model2.addRow(new String[]{splitMensagem[0] + " " +


splitMensagem[1] + " " + splitMensagem[2]}); //listar as infos dos
clientes

} catch (IOException e) {
throw new RuntimeException(e);
}
}
int tamanhoMensagens = 0;
try {
tamanhoMensagens = Integer.parseInt(in.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < tamanhoMensagens; i++) {
try {
model.addRow(new String[]{in.readLine()}); //listar na
tabela cada mensagem
} catch (IOException e) {
throw new RuntimeException(e);
}
}
connectBtn.setEnabled(false);
}

private void atualizarNicknamePublicKey(String nickname, String


publickey) {
KeyFactory factory = null;
PublicKey publicKeyy = null;
try {
factory = KeyFactory.getInstance("RSA", "SunRsaSign");
//algoritmo para recriar a chave publica
byte[] decodedBytes =
Base64.getDecoder().decode(publickey); // passar de String
base64 para bytes
publicKeyy = (PublicKey) factory.generatePublic(new
X509EncodedKeySpec(decodedBytes)); //construir a chave publica a
partir dos bytes usando o algoritmo
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
listaNickPublicKey.put(nickname, publicKeyy); //adicionar o
nickname, e a respetiva chave publica do cliente ao hashmap
}
}
ConfiguracoesCliente
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

public class ConfiguracoesCliente extends JFrame {


private JButton button1;
private JPanel panel1;
private JTextField tfPorta;
private JTextField tfEndereco;
private JCheckBox checkBox1;

public ConfiguracoesCliente() {
setTitle("Configurações do Cliente");
setSize(700, 300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setContentPane(panel1);

tfPorta.setUI(new HintTextFieldUI("Porta", true));


tfEndereco.setUI(new HintTextFieldUI("Endereço", true));

button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {

String enderecoIP = tfEndereco.getText();


String porta = tfPorta.getText();
InterfaceCliente interfaceCliente = new
InterfaceCliente(enderecoIP, porta, checkBox1.isSelected());
dispose();

} catch (IOException ex) {


ex.printStackTrace();
}

}
});
}

public static void main(String[] args) {


ConfiguracoesCliente configuracoesCliente = new
ConfiguracoesCliente();
configuracoesCliente.setVisible(true);
}
}
PrivateMessaging
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface PrivateMessaging extends Remote{


String sendMessage(String name, String message) throws
RemoteException;
String sendMessageSecure(String name, String message, String
signature) throws RemoteException;
}
EnviarMensagem
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.swing.table.DefaultTableModel;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;

public class EnviarMensagem extends UnicastRemoteObject implements


PrivateMessaging {
private DefaultTableModel tableModel;
private String nickname;
private HashMap<String, PublicKey> listaNickPublicKey = new
HashMap<>();

public EnviarMensagem(DefaultTableModel defaultTableModel, String


nickname, HashMap<String, PublicKey> listaNickPublicKey) throws
RemoteException {
super();
this.tableModel = defaultTableModel;
this.nickname = nickname;
this.listaNickPublicKey = listaNickPublicKey;
}

@Override
public String sendMessage(String name, String message) throws
RemoteException {
tableModel.addRow(new String[]{name + ": " + message});
return nickname; // temos de retornar quem recebe a mensagem
}

public String sendMessageSecure(String name, String message,


String signature) throws RemoteException {
//Temos o name, temos de arranjar forma de ir buscar a key
PublicKey publicKey = listaNickPublicKey.get(name); // para
acedermos à chave publica do gajo que enviou o nome :

//decifrar o sumario que recebemos //Vamos receber a


assinatura em base64 -> passamos paa bytes -> deciframos com a chave
publica -> E obtemos o sumario
byte[] decodedBytes = Base64.getDecoder().decode(signature);
Cipher cipher = null;
byte[] decipheredDigest = new byte[0];
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
//Decrypting the text
decipheredDigest = cipher.doFinal(decodedBytes);
} 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();
}
// temos de criar o sumario por nos e comparar//Vamos receber
a mensagem e vamos fazer o sumario dela
MessageDigest md = null;
byte[] digest = new byte[0];
try {
md = MessageDigest.getInstance("SHA-256");
md.update(message.getBytes()); //passamos os bytes do que
escrevemos para o messagedigest
digest = md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}

//Comparamos os sumarios para verificar a integridade da


mensagem
if (Arrays.equals(decipheredDigest, digest)) {
System.out.println(true); //não houve alteração nenhuma
tableModel.addRow(new String[]{"(Secure) " + name + ": " +
message});
} else {
System.out.println(false); //houve alteração
}

return nickname;
}

Você também pode gostar