Escolar Documentos
Profissional Documentos
Cultura Documentos
3ª ENTREGA
Relatório do Projeto
Guilherme José de Sousa Inês Capa de Barros Maria Beatriz Garcez Morais
Barbosa
1. Implementação:
PresencesServer
Começamos por pedir a porta na qual o servidor deseja operar para que qualquer conexão seja
estabelecida. Pedimos também que seja indicado um valor para o parâmetro SESSION_TIMEOUT,
inicializamos as Presences passando como parâmetro o valor inserido e inicializamos o servidor com a porta
que ele previamente selecionou.
O servidor está sempre aberto a aceitar ligações vindas de clientes. Cada vez que temos uma nova
ligação de um cliente ao servidor, criamos e adicionamos uma instância do atendePedidoTCP (passamos
como parâmetro a ligação socket e o Servidor que lhe deu origem) à lista de utilizadores.
De modo que o servidor esteja atualizado e atualize os clientes em relação ao número de
conectados, vamos ter um temporizador que a cada segundo compara os utilizadores que estão ativos
com os utilizadores que estão presentes na lista static que contém todos os objetos do tipo
atendePedidoTCP.
Percorremos a lista static e por cada utilizador que estiver nessa lista, mas não estiver na dos
ativos, adicionamos à lista dos utilizadores inativos.
Por fim, para cada elemento da lista dos inativos enviamos um “SESSION_TIME_OUT”, fechamos
a ligação com o servidor e removemos da lista static de utilizadores.
AtendePedidoTCP
Esta classe gere a ligação de cada cliente com o servidor e é instanciada no PresencesServer cada
vez que uma nova ligação é efetuada.
Quando o cliente envia um pedido, este está contido numa só string que contem o tipo de pedido e o
respetivo conteúdo separada por “:”, que é lida pelo atendePedidoTCP o qual os vai separar. Consoante o
cabeçalho da mensagem que pode ser "SESSION_UPDATE_REQUEST" ou "AGENT_POST" o servidor terá
um comportamento diferente:
• Se for do tipo "AGENT_POST", significa que foi uma mensagem que o cliente enviou e vai guardá-
la nas 10 mensagens mais recentes.
• Se for do tipo "SESSION_UPDATE_REQUEST": A string que recebemos devemos parti-la para
obtermos o nickname, o respetivo valor do RMI e a publicKey. Como a publicKey é recebida em
Base64, é necessário voltar a transformá-la no tipo PublicKey. Posteriormente adicionamos estes
valores à lista de utilizadores. Se o AtendePedidoTCP não tiver nickname, ou seja, se ainda não se
tiver conectado, então este passa a ser igual ao nickname enviado no pedido do cliente.
Esta classe serve para guardar a informação relativa ao cliente: IP, nickname, regista quando foi a
última vez que este efetuou um pedido ao servidor, regista também se este deseja comunicar
remotamente(RMI) e a publicKey. O método TimeOutPassed() consiste em verificar a diferença do tempo
atual e da última vez que o cliente foi visto. Se este tempo for maior do que o tempo estipulado para o
“TIME_OUT”, então o cliente será visto como inativo.
Presences
Criamos uma hashtable para incluir os dados dos clientes, sendo a chave o IP e para cada chave
tem associado um objeto da classe IPInfo. Estes dados contém a informação relativa aos IP’s, os
respetivos nicknames, a data mais recente na qual o cliente foi visto, ou seja, a última vez que efetuou
um pedido ao servidor, se este deseja comunicar remotamente(RMI) e a publicKey.
No método getNickNameList() começamos por percorrer a lista de objetos IPInfo e para cada
elemento vamos verificar se dentro do tempo estipulado como tempo de “TIME_OUT” o cliente realizou
algum pedido, se não tiver realizado não é adicionado novamente à lista, caso contrário o nome dele é
adicionado novamente e o cronometro é reiniciado.
ConfiguracoesCliente
Esta interface será usada com o intuito de receber informações sobre como será estabelecida a
conexão e com quem. Para isso pedimos ao cliente que insira o endereço ao qual se pretende conectar, a
porta do respetivo endereço e, se este pretender comunicar remotamente(RMI), deve selecionar a
checkbox. No caso do cliente não inserir nem o endereço nem a porta aos quais se deseja conectar, será
utilizada a informação default. E se não selecionar a checkbox, o cliente não irá comunicar remotamente
com outros. De seguida inicializamos a interface cliente.
InterfaceCliente
Começamos por inicializar a interface do cliente. Decidimos o modelo para cada uma das tabelas
e criamos o painel que representa toda a nossa interface. É nesta interface que o cliente se pode conectar
com o servidor, para isso criamos um botão “Conectar” que estabelece a ligação com o servidor através do
método iniciar(). Este método cria o Socket, o canal de comunicação com o servidor, que serve para entrada
(BufferedReader) e saída (PrintWriter) de dados, cria também o registo RMI e gera o par de chaves(pública
e privada) de cada cliente. O cliente deve inserir um nickname válido, ou seja, que não seja nulo e,
posteriormente, iniciamos o método iniciarLoop() que nos irá permitirá realizar um
"SESSION_UPDATE_REQUEST” a cada 120 segundos. No "SESSION_UPDATE_REQUEST” enviamos para o
servidor o nickname e no caso deste pretender comunicar através de mensagens privadas, enviamos
também o RMI (true/false) e a respetiva publicKey em Base64.
No método criarRegisto() criamos o registo RMI na porta 1099 e criamos um objeto
remoto(passamos o modelo da tabela, o nickname e um HashMap que contem associados os nicknames
às respetivas publicKeys). Para esse registo, em localhost, precisa de criar um serviço com o nome
“PrivateMessaging” que depois é associado ao objeto remoto.
Para recebermos as mensagens que o servidor envia, criamos uma thread que está sempre em
execução enquanto houver ligação entre o cliente e o servidor. Esta thread é responsável por ver se a
mensagem recebida é do tipo "SESSION_UPDATE”. Caso seja, prepara o cliente para este receber a lista
dos utilizadores presentes e as 10 mensagens mais recentes. A ordem pela qual é recebida a informação é
a mesma pela qual é enviada pelo servidor.
No método separarNicknameIpRmi (int linha, int coluna) vamos buscar a linha selecionada da
tabela das Presenças e separamos o nickname do Ip da informação relativa ao RMI(true/false). Fazemos
verificações não permitindo que o cliente envie mensagens para si próprio e obrigando-o a selecionar um
dos clientes que pretende comunicar remotamente.
Adicionamos à tabela das mensagens privadas uma nova linha com a seguinte informação: Enviou
uma mensagem segura para:" + nicknameParaQuemVamosEnviar + ": " + mensagemEnviar. De seguida,
acedemos ao registo do IP ao qual vamos enviar a mensagem e fazemos um lookup com o SERVICE_NAME
(“PrivateMessaging”) e obtemos o objeto remoto. Através deste escrevemos a mensagem que
pretendemos enviar.
EnviarMensagem
No método sendMessage (String name, String message) adicionamos uma linha à tabela das
mensagens privadas com o nome do cliente que a envia e o respetivo conteúdo. Retornamos o nickname
de quem recebe a mensagem como pedido no enunciado.
No método sendMessageSecure(String name, String message, String signature), através do
nickname do cliente, vamos buscar a respetiva publicKey contida na lista listaNickPublickey para podermos
decifrar o sumário recebido. Passamos o sumário para bytes e depois deciframos com a publicKey.
Posto isto, criamos o sumário da mensagem utilizando o mesmo algoritmo. Comparando os sumários é
possível concluir se existiram alterações no conteúdo da mensagem. No caso dos sumários serem iguais,
ou seja, da mensagem não ter sido alterada, esta é exibida.
PrivateMessaging
Esta interface serve para declarar o método sendMessage(String name, String message), podendo
ser executado pelo stub. E serve também para declarar o método sendMessageSecure(String name, String
message, String signature).
Q1.
A solução a implementar numa situação em que se pretenda manter confidencial o conteúdo das
mensagens, é a criptografia com Chave Assimétrica. Esta baseia-se na ideia de usar uma das duas chaves
geradas para encriptação e outra para fazer a desencriptação. Conforme o pretendido pelas entidades, o
uso das chaves vai variar.
Uma entidade gera um par de chaves (pública e privada) e distribui a chave pública abertamente
para que todos os interessados em enviar uma mensagem privada, o possam fazer. Se uma entidade
desejar enviar uma mensagem confidencial, deve encriptá-la usando essa chave pública do respetivo
recetor.
Q2.
A rede permite a comunicação e a troca de mensagens entre diversas entidades e esta apresenta
várias vantagens tais como a emissão ou receção de documentos, o acesso ou a disponibilização de
informações confidenciais, entre outros. No entanto, esta não é um meio totalmente seguro, uma vez que
pode ser usado para cometer atividades maliciosas por parte de pessoas, logo se for pretendido a troca
segura de informação é necessária a utilização dos certificados digitais.
Os certificados digitais atestam a associação entre uma chave pública e um indivíduo ou entidade,
garantindo a integridade, a autenticidade e a confidencialidade aquando da troca de mensagens, evitando
que uma entidade terceira possa interferir.
Uma vez que estes certificados são emitidos por uma Autoridade de Certificação de Confiança,
conseguimos garantir que a entidade a quem nos queremos dirigir é fidedigna e que as mensagens que
recebemos também o são.