Diferencia entre revisiones de «Técnicas de programación segura con Java»

De MediaWiki
Ir a la navegación Ir a la búsqueda
Línea 138: Línea 138:
  
 
== Criptografía de clave públia ==
 
== Criptografía de clave públia ==
 +
 +
= Xeneración e uso de certificados dixitais =
 +
 +
= Xeneración e uso de firma dixital =
  
 
= Comunicacións seguras con JSSE =
 
= Comunicacións seguras con JSSE =
 +
JSSE é unha implementación de TLS/SSL en Java. As clases de JSSE están no paquete javax.net.ssl.
 +
 +
SSLServerSocket é filla de ServerSocket, e engade unha capa de encriptación baseada en TLS/SSL, de xeito que os datos que se transmiten en datagramas de TCP van cifrados. Para ela existe unha clase de tipo factory, SSLServerSocketFactory.
 +
 +
JSSE implementa diversas versións de TLS/SSL, e diversas opcións destes protocolos. Para sacar partido de todas elas, basta con que cada parte teña a súa clave privada e a clave pública da outra parte. Pero se non se precisa autenticación de cliente, bastaría con xenerar o par de claves para el servidor.
 +
 +
Pódese xenerar o par de claves pública e privada para cada parte con ferramentas de openssl; pero tambén con keygen, unha ferramenta propia de Java.
 +
 +
En cada parte (cliente e servidor) utilizaranse dous almacéns de claves (''keystore''). Os ''keystores'' xestionanse tamén con keygen.
 +
 +
* En un keystore para el propio certificado se almacena el propio certificado con la clave privada, generado con la opción -genkey, para el algoritmo RSA (–keyalg RSA), protegido por contraseña especificada con la opción -storepass. Después, se exporta a un chero con -exportkey e -file.
 +
 +
* Dado que los certificados anteriores son autofirmados, para que se admitan por la otra parte, debe establecerse su emisor como una CA o autoridad de certificación. En otras palabras, su certificado, que contiene su clave pública, debe importarse ( -importcert) a un keystore para certificados de autoridades de certificación o emisores de confianza. Esto se hace con la opción –trustcacerts (abreviatura de trusted CA certificates o certificados de AC de confianza).
 +
 +
O seguiente script hace todo esto para ambas partes. Pero, para el uso más habitual, en el que no se realiza validación de cliente, solo son necesarias las operaciones en negrita. Es decir, la generación en un keystore para el servidor de un certicado digital con su clave pública más la clave privada correspondiente, y la importación del certicado con la clave pública en un keystore para el cliente.
 +
 +
<pre>
 +
mkdir certificados
 +
mkdir certificados/servidor
 +
mkdir certificados/cliente
 +
 +
# Xenerar e exportar certificados de servidor e cliente
 +
keytool -genkey -alias certServ -keyalg RSA -keystore keystoreServ -storepass pwdkeystoreserv
 +
keytool -exportcert -alias certServ -keystore keystoreServ -storepass pwdkeystoreserv -file certificados/servidor/servidor.cer
 +
keytool -genkey -alias certCli -keyalg RSA -keystore keystoreCli -storepass pwdkeystorecli
 +
keytool -exportcert -alias certCli -keystore keystoreCli -storepass pwdkeystorecli -file certificados/cliente/cliente.cer
 +
 +
# Importar coma certificados de confianza certificado de servidor para cliente e de cliente para servidor
 +
keytool -importcert -trustcacerts -alias certConfCli -file certificados/servidor/servidor.cer -keystore keystoreCliCertConf -storepass pwdkeystoreconfcli
 +
keytool -importcert -trustcacerts -alias certConfServ -file certificados/cliente/cliente.cer -keystore keystoreServCertConf -storepass pwdkeystoreconfserv
 +
 +
</pre>
 +
 +
== Exemplo de eco con TLS/SSL ==
 +
Amósanse as diferenzas (mínimas) entre os programas cliente e servidor de eco:
 +
* o desenvolto anteriomente para TCP
 +
* o programa equivalente para TLS/SSL, que fai uso das clases de JSSE.
 +
 +
No programa servidor para TLS/SSL con JSSE créase un socket de servidor da clase SSLServerSocket a partir dunha instancia de SSLServerSocketFactory. Antes hai que asignar valor a determinadas propiedades do sistema javax.net.ssl, en concreto:
 +
* os nomes dos keystores ou almacenes de claves
 +
* os contrasinais para acceder a eles.
 +
 +
So se usa un keystore que conten a clave privada do servidor. A SSLServerSocketFactory obtense con SSLServerSocketFactory.getDefault(), pero podense utilizar diversos constructores con diversos parámetros para utilizar as diferentes opcións de TLS/SSL.
 +
 +
{| class="wikitable"
 +
!+ Cambios en servidor baseado en TCP para utilizar TLS/SSL
 +
! Servidor para TCP
 +
! Servidor para TLS/SSL (con JSSE)
 +
|-
 +
| package socketstcp_servidoreco;
 +
| package jsse_servidoreco;
 +
|-
 +
|
 +
| import javax.net.ssl.SSLServerSocketFactory; public class SocketsTCP_ServidorEco { public class JSSE_ServidorEco {
 +
|-
 +
|
 +
| System.setProperty(“javax.net.ssl.keyStore”,”keystoreServ”);
 +
System.setProperty(“javax.net.ssl.keyStorePassword”,”(pwd) “);
 +
//System.setProperty(“javax.net.ssl.trustStore”,”keystoreServCertConf”);
 +
//System.setProperty(“javax.net.ssl.trustStorePassword”,”(pwd)”);
 +
|-
 +
| try (ServerSocket socketServidor = new ServerSocket(numPuerto)) {
 +
| SSLServerSocketFactory sfactSSL = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); try (ServerSocket socketServidor = sfactSSL.createServerSocket(numPuerto)) {
 +
|}
 +
 +
Entre os programas clientes as diferenzas son análogas. Con JSSE crease un socket de comunicación da SSLSocket a partir dunha instancia de SSLSocketFactory. Antes hai que asignar valor a determinadas propiedades del sistema javax.net.ssl:
 +
* os nombres dos keystores ou almacenes de claves
 +
* os contrasinais para acceder a eles.
 +
 +
Solo se usa, en reciprocidade co programa de servidor, un keystore que conten a clave pública do servidor. A SSLSocketFactory obtense con SSLSocketFactory.getDefault(), pero se poden utilizar diversos constructores con diferentes parámetros para utilizar aa opcións de TLS/SSL.
 +
 +
{| class="wikitable"
 +
|+ Cambios en cliente baseado en TCP para utilizar TLS/SSL
 +
! Cliente para TCP
 +
! Cliente para TLS/SSL (con JSSE)
 +
|-
 +
| package socketstcp_clienteeco;
 +
| package jsse_clienteeco;
 +
|-
 +
|
 +
| import javax.net.ssl.SSLSocketFactory;
 +
|-
 +
| public class SocketsTCP_ClienteEco {
 +
| public class JSSE_ClienteEco {
 +
|-
 +
|
 +
|
 +
// System.setProperty(“javax.net.ssl.keyStore”,”keystoreCli”);
 +
// System.setProperty(“javax.net.ssl.keyStorePassword”,”(pwd)”);
 +
System.setProperty(“javax.net.ssl.trustStore”, “keystoreCliCertConf”);
 +
System.setProperty (“javax.net.ssl.trustStorePassword”, “(pwd)”);
 +
|-
 +
| try (Socket socketComunicacion = new Socket(nomHost, numPuerto)) {
 +
| SSLSocketFactory sfactSSL = (SSLSocketFactory) SSLSocketFactory.getDefault(); try (Socket socketComunicacion = sfactSSL.createSocket(nomHost, numPuerto)) {
 +
|}
 +
 +
Se se fixera autenticación do cliente debería utilizarse no cliente o ''keystore'' para a súa clave privada; e no servidor, un keystore para incluir o seu certificado coma un certificado de confianza, ou no seu defecto certificados raíces ou intermedios que permitiran validalo.
  
 
= Control de acceso con JAAS =
 
= Control de acceso con JAAS =

Revisión del 20:16 2 oct 2021

Introdución

Java inclúe un conxunto de API que permite garantizar a seguridade de comunicacións, mediante o uso de criptografía e o control de acceso.

Criptografía con JCA

JCA (Java Chriptography Architecture) é unha arquitectura baseada en proveedores que proporciona un conxunto de API para todo o relacionado con criptografía de clave privada e pública:

  • Firma dixital
  • Hash ou digest de mensaxes
  • Certificados dixitais
  • Encriptación simétrica e asimétrica
  • Xeneración e gestión de claves
  • Xeneración de números aleatorios

JCA é unha arquitectura estándar de Java, parte do JDK, que proporciona un marco ou framework no que se integran servizos criptográficos implementados por diferentes proveedores de servizos criptográfifios (ou CSP, cryptographic service provider).

Inclúe paquetes coma:

  • java. security
  • javax.crypto
  • javax.crypto.spec
  • javax.crypto.interfaces

Os principios de deseño desta arquitectura son:

  • Independencia de implementación. Os servizos de seguridade son proporcionados por proveedores enlazados coa plataforma Java mediante interfaces estándares. Unha aplicación pode utilizar servizos proporcionados por múltiples proveedores independentes.
  • Interoperabilidade. Os servicios proporcionados por distintos proveedores son compatibles entre eles. Existen clases que proporcionan un tipo determinado de servizo, e a diferentes algoritmos para este tipo de servizo. Estas se denominan engine classes e son as seguintes:
Clase Descripción
MessageDigest Para funcións hash ou digest
KeyGenerator Para xeneración e xestión de claves para criptografía simétrica
KeyPairGenerator Para xeneración e xestión de pares de claves para criptografía asimétrica ou de clave pública
Cipher Para algoritmos de cifrado de datos
Signature Para algoritmos de sinatura dixital
TAREFA. Obtén os proveedores disponibles no JDK que estés utilizando co método getProviders() da clase Security. Compara cos que aparecen no ficheiro de configuración do JDK, no ficheiro de configuración do JDK conf/security/java.security. Para cada un amosa o seu nome e outra información relevante.
Obtén despois todos os servizos proporcionados polo proveedor. Amosa para cada un o seu tipo e o nome do algoritmo. Presta atención os distintos tipos de servizos. Identifica os algoritmos que recoñezas, e localiza algún algoritmo proporcionado por máis dun proveedor.

Explican as capacidades de JCA nos siguientes apartados:

  • funcións de hash ou digest
  • criptografía de clave simétrica ou privada (comenzando pola xeneración de claves)
  • criptografía de clave asimétrica ou de clave pública (comezando pola xeneración de pares de claves)
  • certificados dixitais

Resúmenes de mensajes (funcións de hash ou digest)

JCA conten implementacións para os principales algoritmos de hash ou digest. Estos están disponibles mediante a engine classes MessageDigest.

Existen distintos algoritmos de hash: MD5, SHA-1, SHA2 y SHA-3.

NOTA. MD5 e SHA-1 non se consideran seguros na actualidade para criptografía.

Para xenerar un hash con JCA procedese así:

1. Créase un obxecto de clase MessageDigest co método estático getInstance() da mesma clase, especificando o nome do algoritmo. Opcionalmente pódese especificar o nome do proveedor.

2. Engádense datos co método update(). Pódese engadir un byte ou un array de tipo byte[]. Esto se pode facer repetidamente para ir engadindo novos datos.

3. Obtense o valor de hash co método digest().

4. Se quixeramos calcular un novo hash invocaríase ó método reset().

Exemplo

O seguinte programa de exemplo realiza o hash SHA-2 de 256 bits dunha cadea de caracteres introducida como parámetros de líña de comando.

 1 package generacionhash;
 2 import java.security.MessageDigest;
 3 import java.security.NoSuchAlgorithmException;
 4 public class GeneracionHash {
 5   public static void main(String[] args) {
 6     if (args.length < 1) {
 7       System.out.println(
 8         “Introducir cadea de caracteres para calcular hash”);
 9       return;
10     }
11     
12     String cadena = args[0];
13     for(int i=1; i<args.length; i++) {
14       cadena += “ “+args[i];
15     }
16     MessageDigest md;
17     try {
18       byte[] bytes = cadena.getBytes();
19       md = MessageDigest.getInstance(“SHA-256”);
20       md.update(bytes);
21       byte[] hash = md.digest();
22       System.out.printf(“Cadea: [%s]\nHash: [%s]\n.”,
23         cadena, valorHexadecimal(hash));
24     } catch (NoSuchAlgorithmException e) {
25       System.out.println(“Non disponible algoritmo de hash”);
26     }
27   }
28   static String valorHexadecimal(byte[] bytes) {
29     String result = “”;
30     for (byte b : bytes) {
31       result += String.format(String.format(“%x”, b));
32     }
33     return result;
34   }
35 }

Pódese comparar o resultado co obtido con comandos en linux.

Por exemplo o xeito de obter en Linux o hash SHA-2 de 256 bits para a cadena “Probando cadea” é

$ echo -n “Probando cadea”
sha256sum da73486a45ca51ab22c9046b908a8150d21988d6b346f6c67fcd2fb4bc62e8ea
Nota.
A opción -n é importante para eliminar o retorno de carro
Tarefa.
Probar este programa para a mesma cadea de caracteres e cotexa a súa saída coa de un dos comandos de Linux: md5sum, sha1sum, sha224sum, sha256sum, etc.

Xeneración e xestión de claves

Desde unha perspectiva práctica das aplicacións en Java que deben utilizar as claves, caben dúas consideracións: 1. O valor en sí das claves.

Se se trata de criptografía de clave privada, hai unha soa clave e se pode elixir libremente dentro do posible conxunto de valores posibles, delimitado fundamentalmente pola lonxitude da clave, que é distinta para cada algoritmo de cifrado de clave simétrica.

Se se trata de clave pública, hai un par de claves relacionadas e a súa xeneración é máis complexa, debido á relación matemática que existe entre elas. As claves para algoritmos de cifrado adoitan xenerarse mediante métodos específicos de determinadas clases que implementan algoritmos de xeneración de claves.

2. O seu almacenamento no sistema. As claves almacenaranse en ficheiros. Unha colección de claves pódese almacenar en repositorios especiales denominados keyrings (chaveros).

Criptografía de clave privada

Criptografía de clave públia

Xeneración e uso de certificados dixitais

Xeneración e uso de firma dixital

Comunicacións seguras con JSSE

JSSE é unha implementación de TLS/SSL en Java. As clases de JSSE están no paquete javax.net.ssl.

SSLServerSocket é filla de ServerSocket, e engade unha capa de encriptación baseada en TLS/SSL, de xeito que os datos que se transmiten en datagramas de TCP van cifrados. Para ela existe unha clase de tipo factory, SSLServerSocketFactory.

JSSE implementa diversas versións de TLS/SSL, e diversas opcións destes protocolos. Para sacar partido de todas elas, basta con que cada parte teña a súa clave privada e a clave pública da outra parte. Pero se non se precisa autenticación de cliente, bastaría con xenerar o par de claves para el servidor.

Pódese xenerar o par de claves pública e privada para cada parte con ferramentas de openssl; pero tambén con keygen, unha ferramenta propia de Java.

En cada parte (cliente e servidor) utilizaranse dous almacéns de claves (keystore). Os keystores xestionanse tamén con keygen.

  • En un keystore para el propio certificado se almacena el propio certificado con la clave privada, generado con la opción -genkey, para el algoritmo RSA (–keyalg RSA), protegido por contraseña especificada con la opción -storepass. Después, se exporta a un chero con -exportkey e -file.
  • Dado que los certificados anteriores son autofirmados, para que se admitan por la otra parte, debe establecerse su emisor como una CA o autoridad de certificación. En otras palabras, su certificado, que contiene su clave pública, debe importarse ( -importcert) a un keystore para certificados de autoridades de certificación o emisores de confianza. Esto se hace con la opción –trustcacerts (abreviatura de trusted CA certificates o certificados de AC de confianza).

O seguiente script hace todo esto para ambas partes. Pero, para el uso más habitual, en el que no se realiza validación de cliente, solo son necesarias las operaciones en negrita. Es decir, la generación en un keystore para el servidor de un certicado digital con su clave pública más la clave privada correspondiente, y la importación del certicado con la clave pública en un keystore para el cliente.

mkdir certificados 
mkdir certificados/servidor 
mkdir certificados/cliente 

# Xenerar e exportar certificados de servidor e cliente
keytool -genkey -alias certServ -keyalg RSA -keystore keystoreServ -storepass pwdkeystoreserv
keytool -exportcert -alias certServ -keystore keystoreServ -storepass pwdkeystoreserv -file certificados/servidor/servidor.cer 
keytool -genkey -alias certCli -keyalg RSA -keystore keystoreCli -storepass pwdkeystorecli 
keytool -exportcert -alias certCli -keystore keystoreCli -storepass pwdkeystorecli -file certificados/cliente/cliente.cer 

# Importar coma certificados de confianza certificado de servidor para cliente e de cliente para servidor 
keytool -importcert -trustcacerts -alias certConfCli -file certificados/servidor/servidor.cer -keystore keystoreCliCertConf -storepass pwdkeystoreconfcli 
keytool -importcert -trustcacerts -alias certConfServ -file certificados/cliente/cliente.cer -keystore keystoreServCertConf -storepass pwdkeystoreconfserv

Exemplo de eco con TLS/SSL

Amósanse as diferenzas (mínimas) entre os programas cliente e servidor de eco:

  • o desenvolto anteriomente para TCP
  • o programa equivalente para TLS/SSL, que fai uso das clases de JSSE.

No programa servidor para TLS/SSL con JSSE créase un socket de servidor da clase SSLServerSocket a partir dunha instancia de SSLServerSocketFactory. Antes hai que asignar valor a determinadas propiedades do sistema javax.net.ssl, en concreto:

  • os nomes dos keystores ou almacenes de claves
  • os contrasinais para acceder a eles.

So se usa un keystore que conten a clave privada do servidor. A SSLServerSocketFactory obtense con SSLServerSocketFactory.getDefault(), pero podense utilizar diversos constructores con diversos parámetros para utilizar as diferentes opcións de TLS/SSL.

+ Cambios en servidor baseado en TCP para utilizar TLS/SSL Servidor para TCP Servidor para TLS/SSL (con JSSE)
package socketstcp_servidoreco; package jsse_servidoreco;
import javax.net.ssl.SSLServerSocketFactory; public class SocketsTCP_ServidorEco { public class JSSE_ServidorEco {
System.setProperty(“javax.net.ssl.keyStore”,”keystoreServ”);

System.setProperty(“javax.net.ssl.keyStorePassword”,”(pwd) “); //System.setProperty(“javax.net.ssl.trustStore”,”keystoreServCertConf”); //System.setProperty(“javax.net.ssl.trustStorePassword”,”(pwd)”);

try (ServerSocket socketServidor = new ServerSocket(numPuerto)) { SSLServerSocketFactory sfactSSL = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); try (ServerSocket socketServidor = sfactSSL.createServerSocket(numPuerto)) {

Entre os programas clientes as diferenzas son análogas. Con JSSE crease un socket de comunicación da SSLSocket a partir dunha instancia de SSLSocketFactory. Antes hai que asignar valor a determinadas propiedades del sistema javax.net.ssl:

  • os nombres dos keystores ou almacenes de claves
  • os contrasinais para acceder a eles.

Solo se usa, en reciprocidade co programa de servidor, un keystore que conten a clave pública do servidor. A SSLSocketFactory obtense con SSLSocketFactory.getDefault(), pero se poden utilizar diversos constructores con diferentes parámetros para utilizar aa opcións de TLS/SSL.

Cambios en cliente baseado en TCP para utilizar TLS/SSL
Cliente para TCP Cliente para TLS/SSL (con JSSE)
package socketstcp_clienteeco; package jsse_clienteeco;
import javax.net.ssl.SSLSocketFactory;
public class SocketsTCP_ClienteEco { public class JSSE_ClienteEco {
// System.setProperty(“javax.net.ssl.keyStore”,”keystoreCli”);
// System.setProperty(“javax.net.ssl.keyStorePassword”,”(pwd)”);
System.setProperty(“javax.net.ssl.trustStore”, “keystoreCliCertConf”);
System.setProperty (“javax.net.ssl.trustStorePassword”, “(pwd)”);
try (Socket socketComunicacion = new Socket(nomHost, numPuerto)) { SSLSocketFactory sfactSSL = (SSLSocketFactory) SSLSocketFactory.getDefault(); try (Socket socketComunicacion = sfactSSL.createSocket(nomHost, numPuerto)) {

Se se fixera autenticación do cliente debería utilizarse no cliente o keystore para a súa clave privada; e no servidor, un keystore para incluir o seu certificado coma un certificado de confianza, ou no seu defecto certificados raíces ou intermedios que permitiran validalo.

Control de acceso con JAAS

JAAS (Java Authentication and Authorization Service, servicio de autorización y autenticación de Java) es una interfaz que pennite a las aplicaciones Java acceder a servicios de control de autenticación y acceso. Pódese utllizar para dous propósitos:

  1. para a autenticación de usuarios: para detenninar de forma fiable e segura quén está executando o noso código Java
  2. para a autorización de usuarios: para asegurarse de que quen o executa ten os permisos necesarios para realizar as accións

Están involucradas as seguientes clases e interfaces:

  • LoginContext, contexto de inicio de sesión: inicia e xestiona o proceso de autenticación mediante a creación dun Subject.
    • A autenticación faise chamando ó método login()
  • LoginModule, módulo de conexión: é a interfaz que debe implementarse para definir os mecanismos de autenticación na aplicación. Encárgase de validar os datos nun proceso de autenticación. Debense implementar os seguintes métodos:
    • initialize()
    • login()
    • commit()
    • abort()
    • logout()
  • Subject: clase que representa a un ente autenticable dentro da aplicación (entidade,usuario, sistema)
  • Principal: clase que representa os atributos que posee cada Subject recuperado una vez que se efectua o ingreso á aplicación. Un Subject puede contener varios principales.
  • CallBackHandler: interfaz que se debe implementar cando se precisa recibir do usuario a información para a autenticación; encárgase da interacción co usuario para obter os datos de autenticación. Débese desarrollar o método handle() para implementala.

As clases e interfaces principais de JAAS están disponibles nos seguintes paquetes:

  • javax.security.auth.*: contén as clases de base e interfaces para os mecanismos de autenticación e autorización
    • javax.security.auth.callback.*: contén as clases e interfaces para definir as credenciais de autenticación da aplicación
    • javax.security.auth.login.*: contén as clases para entrar e sair dun dominio de aplicación
    • javax.security.auth.spi.*: contén interfaces para un proveedor de JAAS para implementar módulos JAAS

Autenticación

Autorización

Resumo

  • JCA (Java Chriptography Architecure) é unha arquitectura de Java para criptografía.
  • JSSE (Java Secure Socket Extension) é unha implementación en Java de TLS/SSL.
  • JAAS (Java Authentication and Authorization Service) proporciona
    • autenticación de usuarios que executan código de Java
    • autorización de usuarios para realizar determinadas accións

Ampliación

Actividades propostas

Xenera o resumo dun ficheiro

O ficheiro débese elixir do disco duro coa clase JFileChooser. O resumo se debe amosar en hexadecimal. Utiliza os algoritmos SHA e MD5 e comproba os resúmenes xenerados

Creación de sockets seguros

Crea unha conexión sobre un socket servidor seguro e que atenderá ata catro conexións de clientes que se identificarán cun certificado válido. O servidor agarda as conexións; cada cliente que se conecta recibe unha mensaxe e a continuación envíalle un saúdo.

Pista

Autenticación e autorización

Elabora unha aplicación que nos pida o nome de usuario e contrasinal desde unha ventana de diálogo. A autenticación farase mediante Jaas, onde os usuarios Xosé, Lois e Alba terán acceso. A continuación, haberá dous botóns:

  • amosar o número de ficheiros que hai no directorio onde se está executando o programa (poden facelo Xosé, Lois e Alba)
  • crear un ficheiro que conteña o nome dos ficheiros que hai no directorio onde se está executando o programa (só pode facelo Xosé)

Referencias e créditos