O objectivo principal desta aula é o de escolher/instalar o ambiente de desenvolvimento Java que será utilizado durante o curso.
Como actividade de programação (para experimentar o ambiente escolhido), deve desenvolver uma pequena aplicação que leia um ficheiro de texto (nome do ficheiro passado como argumento), e escreva para stdout
o seu conteúdo com todas as letras maiúsculas.
Pretende-se cifrar o conteudo de um ficheiro. Para tal far-se-á uso da funcionalidade oferecida pela JCA/JCE, em particular a implementação de cifras simétricas.
O objectivo é então o de definir um pequeno programa Java que permita cifrar/decifrar um ficheiro utilizando uma cifra simétrica (e.g. RC4). A sua forma de utilização pode ser análoga a:
prog -genkey <keyfile> prog -enc <keyfile> <infile> <outfile> prog -dec <keyfile> <infile> <outfile>
Sugestões:
openssl enc -d -rc4 -in <infile> -out <outfile> -K <chave>
Algumas classes relevantes:
As classes Cliente, Servidor e TServidorimplementam uma aplicação que permite a um número arbitrário de clientes comunicar com
um servidor que escuta num dado port (e.g. 4567). O servidor atribui um número de ordem a cada cliente, e simplesmente faz o dump do texto enviado por cada cliente (prefixando cada linha com o respectivo número de ordem).
Quando um cliente fecha a ligação, o servidor assinala o facto (e.g. imprimindo [n]
, onde n é o número do cliente).
Exemplo da execução do servidor (que comunica com 3 clientes):
$ java Servidor 1 : daskj djdhs slfghfjs askj 1 : asdkdh fdhss 1 : sjd 2 : iidhs 2 : asdjhf sdga 2 : sadjjd d dhhsj 3 : djsh 1 : sh dh d d 3 : jdhd kasjdh as 2 : dsaj dasjh 3 : asdj dhdhsjsh [3] 2 : sjdh 1 : dhgd ss [1] 2 : djdj [2]
Pretende-se nesta aula modificar as respectivas classes por forma a garantir a confidencialidade nas comunicações estabelecidas. Escolha para o efeito a cifra/modo que considerar mais apropriado.
Algumas classes relevantes:
Pretende-se nesta aula experimentar o impacto da escolha da cifra/modo na comunicação entre o cliente/servidor. Para tal é conveniente reforçar a natureza interactiva da comunicação modificando os ciclos de leitura/escrita para operarem sobre um byte de cada vez:
Cliente:
CipherOutputStream cos = ... int test; while((test=System.in.read())!=-1) { cos.write((byte)test); cos.flush(); }Servidor:
CipherInputStream cis = ... int test; while ((test=cis.read()) != -1) { System.out.print((char) test); }
Experimente agora as seguintes cifras (e modos) e verifique qual o respectivo impacto nas questões de buffering e sincronização:
RC4
AES/CBC/NoPadding
AES/CBC/PKCS5Padding
AES/CFB8/PKCS5Padding
AES/CFB8/NoPadding
AES/CFB/NoPadding
Procure explicar a diferenças detectadas na execução da aplicação.
Obs: Note que em muitos dos modos sugeridos necessita de considerar um IV. Considere para o efeito que o IV é gerado pelo cliente e enviado em claro para o servidor (no início da comunicação).
Algumas classes relevantes (para além das já estudadas...):
O objectivo desta aula é implementar o acordo de chaves Diffie-Hellman. Algumas sugestões para atacar o problema:
BigInteger
e codifique cada passo do protocolo explicitamente. Pode começar por utilizar os seguintes parâmetros para o grupo (P tem 1024 bit):
P = 99494096650139337106186933977618513974146274831566768179581759037259788798151499814653951492724365471316253651463342255785311748602922458795201382445323499931625451272600173180136123245441204133515800495917242011863558721723303661523372572477211620144038809673692512025566673746993593384600667047373692203583 G = 44157404837960328768872680677686802650999163226766694797650810379076416463147265401084491113667624054557335394761604876882446924929840681990106974314935015501571333024773172440352475358750668213444607353872754650805031912866692119819377041901642732455911509867728218394542745330014071040326856846990119719675
AlgorithmParameterGenerator
.
BigInteger
, pode-se fazer uso da classe KeyAgreement
.
Novas classes:
Nesta aula pretende-se concluir o protocolo de acordo de chaves Diffie-Hellman. Sugere-se que agora façam exclusivamente uso das funcionalidades oferecidas pela JCA, nomeadamente:
AlgorithmParameterGenerator
para gerar os parâmetros P
e G
do algoritmo;
KeyPairGenerator
para gerar os pares de chaves ( (x, g^x) e (y,_g^y_) para cada um dos intervenientes);
KeyAgreement
que implementa o protocolo propriamente dito.
APIs:
Pretende-se complementar o programa com o acordo de chaves Diffie-Hellman para incluir a funcionalidade do protocolo Station to Station. Recorde que nesse protocolo é adicionado uma troca de assinaturas (cifrada com a chave de sessão negociada), i.e.:
Um requisito adicional neste protocolo é a manipulação de pares de chaves de cifras assimétricas (e.g. RSA). Para tal deve produzir um pequeno programa que gere os pares de chaves para cada um dos intervenientes e os guarde em ficheiros que serão lidos pela aplicação Cliente/Servidor.
Novas Classes:
Pretende-se certificar as chaves públicas utilizadas no protocolo Station-to-Station com base em certificados X509. Para tal, disponibiliza-se:
A utilização de certificados pressupõe a sua validação. O Java disponibiliza uma API específica que deverá utilizar para o efeito (documentação abaixo). Para facilitar esse estudo recomenda-se o estudo/adaptação de um programa de exemplo que verifica a validade de uma cadeia de certificação: ValidateCertPath.java. Utilizando esse programa, podemos verificar a validade do certificado do servidor através da linha de comando:
java ValidateCertPath ca.crt servidor.crt
Uma segunda questão que surgirá neste trabalho é a manipulação dos formatos das chaves: as chaves privadas correspondentes aos certificados fornecidos estão codificadas num formato standard PKCS8. Para se converter esse formato num objecto Java apropriado terá de se utilizar uma instância da classe KeyFactory
. O fragmento de código que se apresenta ilustra esse processo:
byte[] encodedKey; // read from file PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
Classes requeridas:
Outra documentação relevante:
Nesta sessão iremos fazer uso do openssl para construir uma pequena autoridade de certificação.
O processo de emissão de certificados X509 passa pelos seguintes passos:
Para cada um destes passos iremos fazer uso dos comandos respectivos do openssl.
Geração de chaves:
openssl genrsa -out grupoXPTO.key
Para utilizar a chave privada no Java é conveniente converter o seu formato para PKCS8
openssl pkcs8 -topk8 -nocrypt -in grupoXPTO.key -outform der -out grupoXptoPrivKey.der
Geração do pedido de certificado:
openssl req -new -key grupoXPTO.key -out grupoXPTO.csr
Emissão do certificado: A emissão de certificados é normalmente realizada com o auxílio de scripts que invocam o comando openssl com os argumentos apropriados. Existem duas scripts normalmente utilizadas para este efeito:
ca.key
e ca.crt
.
Produção de PKCS12: Para certas utilizações (e.g. browsers, leitores de email, etc.) é conveniente encapsularmos o certificado e respectiva chave privada num PKCS12.
openssl pkcs12 -export -chain -CAfile cacert.pem -name GrupoXPTO -aes128 -inkey grupoXPTO.key -in grupoXPTO.crt -out GrupoXPTO.p12
Verificação dos certificados:
openssl verify -CAfile cacert.pem cert1.crt cert2.crt ...
utilizando este Certificado da autoridade de certificação de raiz.
Apontadores úteis: