miércoles, 12 de agosto de 2020

Certificados en Java (2). Configurar Tomcat (tambien en Eclipse) https autentificacion cliente certificado. Tarjeta, Smartcard cliente

1. Configurar Tomcat  para https

Segun Namecheap  podemos instalar en Tomcat certificados .jks y tembién .p12 (pkcs12)

1.a con certificado jks

Supongamos que tenemos un certificado p12 llamado mykeystore.jks con contraseña "mypassword", entonces:

Modificamos el conector en el fichero server.xml de la carpeta conf de tomcat y para el puerto 8443 para que quede así

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- Define a SSL HTTP/1.1 Connector on port 8443
        This connector uses the JSSE configuration, when using APR, the
        connector should be using the OpenSSL style configuration
        described in the APR documentation -->
    <Connector port="8443" 
              protocol="HTTP/1.1"
              SSLEnabled="true"
              maxThreads="150" 
              scheme="https" 
              secure="true"
              clientAuth="false" <!-- true to read client certificate -->
              sslProtocol="TLS" <!--"TLSv1.2" -->
              keyAlias="tomcat" <!-- Alias cuando se crea mykeystore,jks -->
	      keystoreFile="/home/ximo/mykeystore.jks"
	      keystorePass="mypassword" 
              
    /> 

Y ahora vamos al directorio "bin" del tomcat y paramos y rearrancamos el servicio

./shutdown.sh
./startup.sh

1.b con certificado pem

Supongamos que hemos obtenido de LetsEncrypt, estos certificados que estan en la carpeta  /etc/letsencrypt/live/midominio.es
  • cert.pem (certificado "solo")
  • chain.pem (cadena de certificados del root)
  • fullchain.pem (certificado "solo" + cadena de certificados del root. Equivale a los 2 ficheros anteriores juntos)
  • privkey.pem (clave privada)
Modificamos el conector en el fichero server.xml de la carpeta conf de tomcat y para el puerto 19443 para que quede así

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 <Connector port="19443"
      protocol="HTTP/1.1"
      SSLEnabled="true"
      maxThreads="150"
      scheme="https"
      secure="true"
      sslProtocol="TLS"

      clientAuth="true"
      keyAlias="tomcat"


      SSLCertificateFile="/etc/letsencrypt/live/midominio.es/cert.pem"
      SSLCertificateChainFile="/etc/letsencrypt/live/midominio.es/chain.pem"
      SSLCertificateKeyFile="/etc/letsencrypt/live/midominio.es/privkey.pem"
   />

  
Como vemos,:
  1.  No nos ha hecho falta el fullchain.pem
  2. Tampoco nos ha hecho falta ninguna contraseña
  3. Estamos trabajando en un puerto https adicional 19443 (junto con el anterior 8443) sin ningún problema

Y ahora vamos al directorio "bin" del tomcat y paramos y rearrancamos el servicio

./shutdown.sh
./startup.sh


2. Obtener un certificado jks versión fácil


En un blog anterior hemos creeado una entidad certificadora. 


Para ello hay que seguir las instrucciones de Mkyong :

Nos situamos en el directorio /home/ximo/mycerts y creamos un keystore con un certificado auto firmado:

keytool -genkey -alias tomcat -keyalg RSA -keystore mykeystore.jks

OJO: El alias tomcat del mykeystore es el que se debe de indicar en el "keyAlias" del web.xml del tomcat
nos pide una nueva contraseña para el nuevo keystore por ejemplo "mypassword"

podemos listar el contenido del keystore con

keytool -list -v -keystore mykeystore.jks



3. Obtener un certificado jks con un certificado emitido por nuestra entidad certificadora.

Aprovechamos todos los pasos anteriores y generamos el Certificate Signing Request CSR. Para ello volvemos al directorio  /home/ximo/mycerts y tecleamos

keytool -certreq -alias tomcat1 -file mytomcat.csr -keystore mykeystore.jks

Observar que el alias el mismo que hemos creado antes (tomcat)  y nos ha creado el fichero mytomcat.csr


OJO: Para obtener cada una de las DNS, debemos hacer este comando:

nslookup 192.168.2.2

siendo 182.168.2.2 la dirección IP del servidor que queremos crear el certificado, y nos devuelve:

ws.ximodante.es
otro.ximodante.es

Y con esta información de los DNS creamos el fichero de configuración siguiente (mytomcat.conf):

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = 192.168.2.2
DNS.2 = localhost --> SOLO PARA PUEBAS en PRE
DNS.3 = otro.ximodante.es
DNS.4 = ws.ximodante.es

Por tanto debeis cambiar a la IP que realmente está usando el servidor 

Ahora generamos el certificado

openssl x509 -req -in mytomcat.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out mytomcat.crt -days 825 -sha256 -extfile mytomcat.conf
 
Hay que tener en cuenta que debemos disponer de los certificados de nuestra entidad certificadora (myCA.key y myCA.pem) obtenidos en el post anterior.

Finalmente importamos el certificado generado usando el mismo alias "tomcat" a nuestro almacén de claves.

keytool -import -alias tomcat1 -file mytomcat.crt -keystore mykeysore.jks

Y rearrancamos el servicio de Tomcat como hemos hecho antes:


./shutdown.sh
./startup.sh

Y si entramos en el navegador se quejará, salvo que le indiquemos al navegador que confie en nuestra entidad certificadora.

4. Autentificación en el servidor con certificado de cliente.


Si queremos que el cliente nos muestre su certificado, hay que indicarle en la línea 11 del fragmento anterior de servers.xml:

clientAuth="true" 

Con lo que nos pide el certificado de cliente y podemos autenticarnos en el servidor mediante certificado.

5. Autentificación en el servidor con tarjeta criptográfica (en cliente).

Cuando se intenta acceder con tarjeta criptográfica en el cliente, nos da este error en el cliente:
SSL_ERROR_BAD_CERT_ALERT

Supongo que tal vez haya que indicar un truststore que contenga el certificado raíz.
Parece ser que tomcat utiliza el alacen de certificados de java.

Se va a copiar el fichero keystore  java_path/lib/security/cacerts a una carpeta de pruebas y con el programa keystore explorer, añadimos los certificados raiz de la etidad certificadora de la tarjeta obtenidas de la ACCV 

En concreto añadimos el certificado raiz, los 110 y 120

y vamos a modificar el server.xml para que tenga un truststore


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- Define a SSL HTTP/1.1 Connector on port 8443
        This connector uses the JSSE configuration, when using APR, the
        connector should be using the OpenSSL style configuration
        described in the APR documentation -->
    <Connector port="8443" 
              protocol="HTTP/1.1"
              SSLEnabled="true"
              maxThreads="150" 
              scheme="https" 
              secure="true"
              clientAuth="false" <!-- true to read client certificate -->
              sslProtocol="TLS" <!--"TLSv1.2" -->
              keyAlias="tomcat" <!-- Alias cuando se crea mykeystore,jks -->
	      keystoreFile="/home/ximo/mykeystore.jks"
	      keystorePass="mypassword" 
              
    /> 



6. Configurar el tomcat de Eclipse.



"If you've already created the server, you can edit the server.xml template it copies. If you use the project explorer, It is under Other Projects->Servers->Tomcat Server Name->server.xml"

Así de fácil localizamos el server.xml y lo configuramos como si fuera un tomcat normal.








lunes, 3 de agosto de 2020

La API de JPA 2.1 (VII) Copiar entidades de una base de datos a otra

1. Introducción 

Este problema parece sencillo pero no lo es, sobre todo cuando una entidad tiene colecciones y además los ID son generados automáticamente:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

2. Proceso

  
  1. Ejecutar una consulta y obtener una lista (colección) de objetos de la entidad a copiar
  2. Para cada instancia:
    2.1 Se crea un objeto igual al de la instancia
    2.1 Mediante recursividad, se copian todos todos los campos del objeto y se asigna NULL al Id propio y de cada una de las colecciones que tenga en el objeto copia.
    2.3 Hacer un persist del nuevo objeto en la nueva EntityManager (EntityManager.persist(obj))

Al recorrer todos los campos del objeto y los campos de los objetos anidados conseguimos que se haya cargado completamente el objeto (pues el "Lazy Loading", solo carga lo que se necesita. 

El objeto original NO DEBE ESTAR DETACHED, pues no haria el "lazy loading" mientras el objeto no està "detached"), 

Para cada instancia (que llamamos "obj") se hará:

1
2
3
T copy=obj; //Define a copy object
ReflectionUtilsEdu.setSameValueToAllNestedBaseFields(copy,"id",null); //Copy by reflection to force lazy Loading
em.persist(copy); //Save the copy

3. Función para hacer nulls los Ids

En esta función le pasamos de parámetros:
  1. El objeto a modificar el atributo recursivamente
  2. Nombre del atributo
  3. Valor del atributo
Si el nombre del atributo le indicamos "id" y al valor "null" entonces hay que tener en cuenta que cualquier campo que se llame id se va a hacer nulo, indepenedienntemente que esté en dicho objeto o en cualquiera de sus colecciones!!!

Aqui le pasamos el objeto y el nombre del campo y valor
Este és el código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public static Object ObjectCloneAndsetSameValueToAllNestedBaseFields(Object obj,String fldName,Object value) throws Exception {
		
	if (obj==null) return null;
		
	//Create the clone object
	Object clone=obj.getClass().getDeclaredConstructor().newInstance();

	//Get all fields
	List<Field> lFlds= new ArrayList<>(Arrays.asList(FieldUtils.getAllFields(obj.getClass())));
		
	for (Field fld:lFlds) {
			
		// 1. The field value has to be changed
		if (fld.getName().equals(fldName)) FieldUtils.writeField(fld, clone, value, true);
			
		// 2. The field  is a descendant of Base
		else if (Base.class.isAssignableFrom(fld.getType())) {
			Object myObjA=FieldUtils.readField(fld, obj, true);
			Object myObjB=ObjectCloneAndsetSameValueToAllNestedBaseFields(myObjA,fldName, value);
			FieldUtils.writeField(fld, clone, myObjB, true);
			
		// 3. the field is a Collection: let's copy the collection
		}else if (Collection.class.isAssignableFrom(fld.getType())) {
			Collection<?> collA=(Collection<?>)FieldUtils.readField(fld, obj, true);
			// Presume collections as Lists !!!!!!!! May fail if Sets or other Collections type !!!!!!
			Collection<Object> collB=new ArrayList<Object>(); 
			if (collA!=null) {
				Iterator<?> iter=collA.iterator();
				Object myObjA=iter.next();
				Object myObjB=ObjectCloneAndsetSameValueToAllNestedBaseFields(myObjA,fldName, value);
				collB.add(myObjB);
			}
			FieldUtils.writeField(fld, clone, collB, true);
			
		// 4. If the class is serializable, let's use SerializationUtils from Apache	
		}else if (Serializable.class.isAssignableFrom(fld.getType())) {
			Serializable myObjA=(Serializable) FieldUtils.readField(fld, obj, true);
			Serializable myObjB=SerializationUtils.clone(myObjA);
			FieldUtils.writeField(fld, clone, myObjA, true);
		
		// 5. A primitive type or not serializable is assigned without cloning	
		}else if (ClassUtils.isPrimitiveOrWrapper(fld.getType()) 
			  || !Serializable.class.isAssignableFrom(fld.getType())){
			Object myObjA=FieldUtils.readField(fld, obj, true);
			FieldUtils.writeField(fld, clone, myObjA, true);
			
		// 5. Throw exception for other fields
		} else {
			throw new Exception("EDU: The object " + fld.getName() + "(" +fld.getType() + ") is not clonable");
		}
	}
	return clone;
}

4. Posibles errores

1. Verificar que exista un esquema general (en posgres "public") que pueda albergar las relaciones que no entran en un esquema particular. Esto me pasó por borrar el esquema "public" en Postgres.
2. Hibernate hace un proxy del objeto, lo que significa que altera su estructura. Las colecciones las mete en una especie de saco, y és difícil averiguar si son Lists, Sets o otra estructura. En principio se ha supuesto aue son Lists, por tanto si falla , hay que rehacer el código para que acepte otra clase de estructura para las colecciones.


domingo, 2 de agosto de 2020

Maven (3) Proyectos multi-módulo. Copia de seguridad y restauración del proyecto

1. Introducción

Cuando creamos un proyecto multimódulo, es conveniente copiar solamente aquello que es importante como los archovos de configuración (pom.xml), recursos (carpetas resources), carpeta front end ... y no copiar las clases compiladas. Mas tarde el proceso de restauración debe de poder crear un proyecto en condiciones.

2. Programa shell de copia de ficheros importantes.

Veamos un shell script para linux (Ubuntu) donde se hace un tar con compresión y se excluyen los ficheros que se pueden regenerar (como las clases compiladas y otros ficherps que se pueden descargar con Maven)

Para ello hay que rellenar los parámetros del nombre del proyecto (carpeta), carpeta del workspace y carpeta donde se va a guardar el tar


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash

#--Explicación
#-------------------------------------------------------------------------------------------
# 1. Indicar project_name, workspace_folder i tar_folder
# 2. Se guardaran en la carpeta tar_folder indicando la fecha
# 3. Utilizamos las opciones cJf para guardar en formato comprimido "xy" que es mas efectivo
#-------------------------------------------------------------------------------------------

#--Parameters

project_name='MyVaadin15_02Parent'
workspace_folder='/home/eduard/Workspace202006/'
tar_folder='/home/eduard/BACKUPS/'

#--Calculations

project_file=${workspace_folder}${project_name}
my_time=`date +"%Y%m%d_%H%M"`
tar_file=${tar_folder}${project_name}"_"${my_time}".tar"


echo $tar_file
echo $project_file

#--Execution

cd ${workspace_folder}
tar -cJvf ${tar_file} --exclude='*/target' --exclude='*/.*' --exclude='*/node_modules' ${project_name}


3. Restauración

Tener en cuenta que los proyectos multi-módulo son un poco especiales a la hora de importarlos y no todos los métodos de imporación de Eclipse funcionan bien.

Para ello descomprimimos la copia de seguridad en la carpeta de nuestro WORKSPACE en Eclipse y hacemos:

File - Import - General - Projects from Folder or Archive



Y seleccionamos la carpeta del proyecto



Tenemos que seleccionar  tanto el proyecto padre como cada uno de los hijos a la vez. Una vez que Eclipse ve el proyecto padre y sus módulos hijos, tenenmos que verificar que encuentra el Java JDK correcto. Para ello hacemos

Window - Preferences - Java - Installed JREs


Ahora si queremos, se puede hacer clik_derecho sobre el proyecto (a veces no aparece la opción y hay que hacer el click derecho sobre el fichero pom.xml) y le damos Run As - Maven clean y repetimos con el  click derecho y Maven - Update Project 

4. Resolución de problemas


1. Si se ha instalado una nueva versión de Eclipse, verificar que se ha instalado Lombok!
2. Si se ha descomprimido el proyecto ebn otra carpeta, este proyecto NO SE COPIA a nuestro workspace, sino que queda vinculada, por eso hay que tener mucho cuidado.
3. Verificar que se apunta al JDK correcto Window - Preferences - Java - Installed JREs