Sessões laboratoriais de CA (2006/2007)
Aula 1 - Arquitectura Cliente-Servidor
Pretende-se construir uma plataforma que permita 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).
Algumas classes relevantes:
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]
Aula 2 - Cifra de ficheiro utilizando JCA/JCE
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 dois pequenos programas Java: Um primeiro que cifra um ficheiro (e.g.
texto_limpo.txt
) por intermédio de uma cifra simétrica (e.g. cifra sequêncial
RC4). Um segundo que decifra o criptograma obtido para recuperar o texto limpo.
Para simplificar vamos utilizar uma chave secreta fixa, definida no código na forma de um array de bytes, e convertida numa chave secreta adequada utilizando a classe
SecretKeySpec.
É também interessante verificar que o criptograma gerado é compatível com outras plataformas que implementam a mesma cifra. O comando seguinte utiliza o
openssl para decifrar um ficheiro cifrado com
RC4 (a chave tem de ser fornecida em hexadecimal).
openssl enc -d -rc4 -in <criptograma> -out <ficheiro resultado> -K <chave> -iv 0
Algumas classes relevantes:
Outra documentação relevante:
Aula 3 - Canal seguro entre Cliente/Servidor
Pretende-se completar/modificar o código realizado nas aulas anteriores por forma a que o Cliente comunique de forma segura (confidêncial) com o Servidor. Para tal deve-se utilizar uma cifra por blocos (e.g.
AES) num modo de funcionamento apropriado.
Observações:
- A escolha do modo deve ser adequado à natureza da conexão entre o cliente e o servidor. Em particular, deve-se tomar em atenção as questões de buffering e sincronização...
- Se se optar por uma Stream "interactiva", pode-se optar por
CipherOutputStream
e CipherInputStream
com uma cifra num modo "sequêncial" (obs.: experimente utilizar um modo por blocos para ter percepção sobre os problemas dessa solução...)
- Se optar por transmissão assíncrona de objectos, por optar por utilizar
SealedObject
sobre uma ObjectStream
. Nesse caso pode utilizar um modo por blocos (e.g. CBC
).
- Em qualquer dos casos, terá sempre de tratar da inicialização e transmissão do Initialization Vector.
- Estando nós a utilizar uma cifra simétrica, temos de garantir que cliente/servidor acordam na chave utilizada. Para simplificar, podemos admitir que guardamos a chave num ficheiro (gerada por um pequeno programa com base na funcionalidade da classe KeyGenerator) que é disponibilizado a ambos os programas. Adiante, dicutiremos formas mais apropriadas de estabelecer o acordo de chaves entre as partes...
Algumas classes relevantes (para além das já estudadas...):
Aula 4 - Canal seguro entre Cliente/Servidor (continuação)
Pretende-se:
- (Se for o caso...) Concluir a implementação da aula anterior;
- Implementar estratégia alternativa (
CipherStream
vs. SealedObject
).
Aula 5 - Acordo de chaves Diffie-Hellman
O objectivo desta aula é adicionar à aplicação Cliente/Servidor suporte para o protocolo de acordo de chaves
Diffie-Hellman. Para tal deverá ser conveniente estudar/adaptar o programa de exemplo apresentado em
JCE Reference Guide (Appendix F).
Novas classes:
Aula 6 - Implementação do protocolo Station to Station
Pretende-se:
- (Se for o caso...) Concluir a implementação da aula anterior (acordo de chaves Diffie Hellman);
- Complementar o programa de acordo de chaves para incluir a funcionalidade do protocolo Station to Station. Recorde que nesse protocolo é adicionado uma traca de assinaturas (cifrada com a chave de sessão negociada), i.e.:
- Alice -> Bob : g^x
- Alice <- Bob : g^y, EK(SB(g^y, g^x))
- Alice -> Bob : EK(SA(g^x, g^y))
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:
Aula 7 - Garantias de Integridade
Tendo já abordado os aspectos de
confidencialidade e
autenticidade na comunicação
Cliente/Servidor, falta-nos considerar a
integridade da mensagem transmitida (i.e. garantir que não é possível manipular/alterar a mensagem enquanto em trânsito). Para tal devemos fazer uso de (H)MACs (classe
javax.crypto.Mac).
Obs.: Uma sugestão para integrar a utilização de
MACs na aplicação existente consiste em seguir a estratégia adoptada em
CipherStream ou
SealedObject: a de encapsular a sua funcionalidade numa classe responsável pela comunicação...
Aula 8 - Utilização de Certificados em Java
Pretende-se certificar as chaves públicas utilizadas no protocolo
Station-to-Station com base em certificados X509. Para tal, disponibiliza-se:
- Certificado de chave pública do Servidor: servidor.crt
- Chave privada do Servidor (codificada em PKCS8): servidor.der
- Certificado de chave pública do Cliente: cliente.crt
- Chave privada do Cliente (codificada em PKCS8): cliente.der
- Certificado auto-assinado da autoridade de certificação: ca.crt
Para facilitar o estudo da manipulação de certificados X509, disponibiliza-se um programa que verifica a validade de uma cadeia de certificação:
ValidateCertPath.java.
Classes requeridas:
Outra documentação relevante:
Aula 9 - Geração de Certificados
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:
- - O utilizador gera um par de chaves;
- - O utilizador produz um "pedido de certificado" que contém a sua identificação e a sua chave pública, que é assinado com a sua chave privada (o que certifica que quem solicita o certificado está na posse da respectiva chave privada);
- - A autoridade de certificação valida os dados contidos no certificado e, no caso de certificar positivamente esses dados, emite o certificado de chave pública correspondente.
- - O certificado é enviado ao utilizador e, eventualmente, publicado por outros meios (e.g. serviço de directoria).
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
penssl 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.pl - distribuída com o "openssl".
- sign.sh - distribuída com o "mod_ssl". Espera encontrar ficheiros
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: