proyecto fin de carrera -...

195
i Proyecto Fin de Carrera Ingeniería de Telecomunicación Apertura de puertas usando Códigos QR QR Door Opener Autor: Carlos Corrales Yerpes Tutor: Germán Madinabeitia Luque Dept. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2013

Upload: doanthuy

Post on 24-Aug-2018

215 views

Category:

Documents


0 download

TRANSCRIPT

i

Proyecto Fin de Carrera

Ingeniería de Telecomunicación

Apertura de puertas usando Códigos QR

QR Door Opener

Autor: Carlos Corrales Yerpes

Tutor: Germán Madinabeitia Luque

Dept. de Ingeniería Telemática

Escuela Técnica Superior de Ingeniería

Universidad de Sevilla

Sevilla, 2013

ii

Proyecto Fin de Carrera

Ingeniería de Telecomunicación

Apertura de puertas usando Códigos QR

QR Door Opener

Autor:

Carlos Corrales Yerpes

Tutor:

Germán Madinabeitia Luque

Dept. de Ingeniería Telemática

Escuela Técnica Superior de Ingeniería

Universidad de Sevilla

Sevilla, 2013

iv

Proyecto Fin de Carrera: Apertura de puertas usando Códigos QR QR Door Opener

Autor: Carlos Corrales Yerpes

Tutor: Germán Madinabeitia Luque

El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros:

Presidente:

Vocales:

Secretario:

Acuerdan otorgarle la calificación de:

Sevilla, 2013

El Secretario del Tribunal

v

A mis padres

A mi hermana

A Cristina

A mis amigos

vii

Agradecimientos

Quisiera agredecer en primer lugar a mis padres y mi hermana, por todo el apoyo que me han dado a lo largo de mi vida en todos los momentos difíciles, estando siempre a mi lado para conseguir que siempre hubiera felicidad hasta en el momento más oscuro.

También a Cristina, mi apoyo, que siempre consigue sacarme una sonrisa. Gracias por formar parte de mi vida desde hace tanto y por demostrarme cada día que será para siempre.

A todos los profesores que han pasado a lo largo de mi vida, en especial a Don Carlos Corts y Don Ginés Pastor del Colegio Salesiano de Utrera, por enseñarme que la base del éxito está en el trabajo y el esfuerzo, y en saber superar las adversidades con ingenio.

Por supuesto, a todos los profesores de la Universidad de Sevilla a cuyas clases he tenido el placer de asistir, ya que me han permitido superar límites de exigencia que creía imposibles, sobre todo a Don Francisco Javier Muñoz por esas intensísimas tardes de laboratorio y a Don Germán Madinabeitia por su dedicación y apoyo en este proyecto.

Carlos Corrales Yerpes

Ingeniería de Telecomunicaciones

Sevilla, Septiembre 2013

ix

Resumen

En la época actual, el desarrollo tecnológico permite prever que los dispositivos electrónicos llegarán a formar parte de todos los ámbitos de nuestra vida. Tanto es así que se puede llegar a pensar en una forma de acceder a domicilios y otros edificios que permita al usuario evitar el tener que ir llevando llaves, consiguiendo además mejorar varias funcionalidades a nivel de seguridad en el caso de que el usuario quiera dejar su llave de acceso de manera temporal a familiares, amigos, o cualquier persona que quiera acceder al domicilio del usuario.

Con este proyecto, se busca exactamente eso, encontrar una manera sencilla y segura de acceder a domicilios evitando el uso de llaves, simplemente usando una imagen en el punto de acceso al domicilio se podrá entrar en él, consiguiendo además que esta imagen sólo sea válida por un tiempo predeterminado y registrando los instantes de acceso, de manera que la seguridad es incrementada drásticamente respecto al uso de llaves convencionales.

En las próximas páginas, se explica como se ha llevado a cabo este proyecto, las decisiones tomadas a la hora de implementarlo, los tipos de problemas que podrían presentarse y cómo se han solucionado y una exposición en forma de ejemplo de cómo funciona el producto completo.

x

xi

Abstract

Nowadays, the technological development lets think about a future where the electronic devices will be surrounding every scope in people’s life. Thereby, a way to access to houses and other places that lets the user to avoid carrying their keys everywhere, improving some functionalities in the security level in the case that users want to borrow their keys temporarily to family, friends or whoever wants to access user’s place can be thought.

With this project, the author is just looking for that, trying to find an easy and safety way to access houses avoiding the use of keys, just using a picture in the access point to the house the users can get in, with the improve that this picture only will be valid for a specific time and storing the access time in a database, enhacing the security in a drastic way if you compare it with the use of normal keys.

In the next pages, the way this proyect has been developed, the decisions taken during the implementation, the problems that could be found and the ways to avoid them, and a step-by-step example about the way the final product works will be explained.

Índice de Figuras

xii

xii

xiii Apertura de puertas usando Códigos QR QR Door Opener

Índice

Agradecimientos vii

Resumen ix

Abstract xi

Índice xiii

Índice de Figuras xv

Índice de Tablas xvii

Glosario de Términos xix

1. Introducción 21 1.1 Definición del problema 21 1.2 Estudio del Mercado y selección de la opción más factible 21 1.3 Descripción del producto a realizar 22 1.4 Antecedentes 23

2 Base Teórica 25 2.1 Infraestructura de Clave Pública (PKI) 25

2.1.1 Modelos de confianza PKI 25 2.1.2 Certificado X.509 27

2.2 SSL/TLS 28 2.2.1 SSL Handshake 28 2.2.2 Keystore y Truststore del Keytool de Java 30

2.3 Formas de guardar contraseñas en una base de datos 31 2.3.1 Almacenamiento del Hash de las contraseñas 31 2.3.2 Almacenamiento del Hash de las contraseñas con Sal 31

3 Análisis de la aplicación 33 3.1 Cliente 33

3.1.1 Website (GUI) 33 3.1.2 Módulo de detección y lectura del código QR 34

3.2 Servidor 34 3.2.1 Servidor Web 34 3.2.2 Servidor Base de Datos 37 3.2.3 Servidor de Aplicación (Servidor QR) 40 3.2.4 Autoridad de Certificación 41

3.3 Comunicación Cliente-Servidor 41 3.3.1 Comunicación a través de la página web 42 3.3.2 Comunicación entre el módulo de lectura del código QR y el servidor de aplicación 42

4 Diseño de la aplicación 45 4.1 Herramientas usadas 45 4.2 Cliente 46

4.2.1 Website (GUI) 46 4.2.2 Detección y lectura del código QR 47

4.3 Servidor 48 4.3.1 Servidor Web 48 4.3.2 Servidor de Base de Datos 51 4.3.3 Servidor de aplicación 51 4.3.4 Autoridad de Certificación 52

4.4 Comunicación Cliente-Servidor 53 4.4.1 Comunicación Web Segura (HTTPS) 53

Índice de Figuras

xiv

xiv

4.4.2 Comunicación con la aplicación segura (Aplicación QR sobre SSL) 54

5 Posibles Ataques 55 5.1 DDoS 55 5.2 SQL Injection 56 5.3 Pérdida de Autenticación y Gestión de Sesiones 57 5.4 Fallos en la configuración de seguridad 58 5.5 Replay Attack 58 5.6 Man-In-The-Middle (MITM) Attack 59 5.7 Seguridad interna del sistema 60

6 Ejemplo Paso a Paso 63 6.1 Acceso a página web y creación de un nuevo usuario 63 6.2 Menú principal, inserción de nueva casa y solicitud de Certificado 65 6.3 Creación de Certificado Digital y envío al cliente 67 6.4 Creación de código QR de acceso, uso de éste para el acceso y comprobación de registros 71

7. Conclusiones y Trabajo Futuro 75 7.1 Conclusiones 75 7.2 Trabajo Futuro 76

8. Bibliografía 79

9. Anexo I: Código del Programa 81 9.1 Módulo de Lectura y Apertura 81

9.1.1 Clases 81 9.1.2 Librerías externas usadas 87

9.2 Servidor de Aplicación 88 9.2.1 Clases 88 9.2.2 Librerías externas usadas 98

9.3 Servidor Web 99 9.3.1 Clases 99 9.3.2 Página Web (JSPs) 151 9.3.3 Página Web (CSSs) 182 9.3.4 Librerías Externas usadas 192

9.4 Base de datos 193

xv Apertura de puertas usando Códigos QR QR Door Opener

ÍNDICE DE FIGURAS

Figura 1. Estudio sobre preferencias de usuario respecto a los distintos métodos de acceso 22

Figura 2. Estructura básica del sistema 23

Figura 3. PKI Modelo Anárquico (Copyright Kungliga Tekniska Högskolan, IK2206 2012) 26

Figura 4. Modelo de Certificado X.509 28

Figura 5. SSL Handshake con Autenticación de ambas partes usando certificado (Copyright Connect Community Portal 2010) 29

Figura 6. Generación del Master Secret (Copyright Thomas, SSL and TLS Essentials) 30

Figura 7. Diagrama de Flujo de la lectura del código QR y apertura de la puerta 34

Figura 8. Diagrama de flujo de la creación de un nuevo usuario 35

Figura 9. Diagrama de flujo del proceso de login 36

Figura 10. Diagrama de flujo de la solicitud de código QR 37

Figura 11. Estructura de la Base de Datos 40

Figura 12. Diagrama de flujo de la comprobación de código QR leído y enviado al servidor de aplicación 42

Figura 13. Ataque por Inundación de SYN (SYN Flooding Attack, Copyright Kungliga Tekniska Högskolan EP2500, Module 3 2013) 55

Figura 14. Ataque de Repetición (Replay Attack,Copyright Kungliga Tekniska Högskolan, IK2206 Lecture 7, 2012) 59

Figura 15. Ataque Man in the Middle (Copyright Kungliga Tekniska Högskolan, IK2206 Lecture 7, 2012) 60

Figura 16. Petición de Confirmación de Certificado 63

Figura 17. Página principal del sitio Web 64

Figura 18. Creación de nuevo usuario 64

Figura 19. Inserción de Nueva Casa 65

Figura 20. Opciones de Usuario 65

Figura 21. Casa insertada 66

Figura 22. Confirmación del correo electrónico para el envío del certificado digital 66

Figura 23. Correo Recibido por el Administrador de la Autoridad de Certificación 67

Figura 24. Certificado Digital de la Autoridad de Certificación 68

Figura 25. Adición del número de serie del Certificado generado al registro de la casa 69

Figura 26. Ficheros de claves y certificados generados 70

Figura 27. Correo recibido por el usuario con los certificados e instrucciones 70

Figura 28. Solicitud de QR propio 71

Figura 29. QR generado para el propio usuario 72

Figura 30. Acceso correcto usando código QR generado para el propio usuario 73

Figura 31. Acceso erróneo usando código QR generado para otro usuario 73

Figura 32. Registro de Acceso 74

Índice de Figuras

xvi

xvi

xvii Apertura de puertas usando Códigos QR QR Door Opener

ÍNDICE DE TABLAS

Tabla 1. Campos de la tabla de Usuario 38

Tabla 2. Campos de la Tabla Casas 38

Tabla 3. Campos de la tabla Permisos 39

Tabla 4. Campos de la Tabla de Códigos QR 39

Tabla 5. Campos de la Tabla Registros 40

Índice de Tablas

xviii

xviii

xix

Glosario de Términos

API Application Programming Interface

App Application

BBDD Base de Datos

CSS Cascading Style Sheets

GUI Graphical User Interface

HTTPS Hypertext Transfer Protocol Secure

IP Internet Protocol

JDBC Java DataBase Connectivity

JDK Java Development Kit

JSP JavaServer Pages

KB KiloByte

KTH Kungliga Teksniska Högskolan (Royal Institute of Technology)

LTS Long Time Support

MB MegaByte

NFC Near Field Communication

PEM Privacy Enhanced Mail

PKCS#12 Public Key Cryptography Standards #12

PKI Public Key Infrastructure

QR Code Quick Response Code

RFID Radio Frequency IDentification

RSA Rivest Shamir Adleman

SQL Structured Query Language

SSL/TLS Secure Socket Layer/Transport Layer Security

PGP Pretty Good Privacy

TCP Transmision Control Protocol

SHA Secure Hash Algorithm

21 Apertura de puertas usando Códigos QR QR Door Opener

1. INTRODUCCIÓN

n la actualidad, los avances tecnológicos se encadenan a un ritmo descomunal. Cada vez pasa menos tiempo entre una mejora y la siguiente dentro de un producto, llevando a un desarrollo que crece exponencialmente con el tiempo.

Centrándonos en los dispositivos portátiles, llama la atención el caso del teléfono móvil. Desde la aparición de la primera generación en los años 80 hasta la actualidad, en la que está empezando a implantarse la cuarta, el desarrollo de este tipo de dispositivos los ha llevado a pasar de servir para realizar llamadas a ser unos verdaderos ordenadores portátiles (el nuevo Samsung Galaxy S4 tiene un procesador de 4 núcleos a 1,9 GHz [1]).

Los nuevos smartphones tienen un sinfín de aplicaciones, cada vez más variopintas y que se adaptan a las necesidades de cualquier tipo de usuario. Casi cualquier tipo de aplicación que el usuario necesite puede ser encontrada en la red. Además, el acceso sin restricciones a las distintas webs que se pueden acceder a través de Internet permite pensar en infinidad de posibilidades para cumplir las expectativas del usuario.

Esto nos lleva a pensar en una aplicación que nos permita usar nuestro Smartphone para abrir la puerta de nuestro domicilio. En las próximas páginas, se explicará un nuevo producto desarrollado en detalle, que permitirá al usuario emplear una aplicación a la que se podrá acceder a través de una página web para abrir la puerta de su domicilio.

1.1 Definición del problema

Básicamente, el problema consiste en encontrar una forma cómoda de acceder a un domicilio sin necesidad de usar llaves. Gracias al desarrollo y expansión de Smartphones, que en Europa alcanzaron el 55% de penetración en Octubre de 2012 [2], así como de las tablets y otros dispositivos portátiles, un usuario podría acceder a su domicilio de manera electrónica, permitiéndole gestionarlo todo desde un mismo dispositivo. Por tanto, el problema está en encontrar una manera atractiva para el usuario de acceder a su domicilio de manera fácil, rápida y que le permita no tener que usar llaves. Claro está que, desde el punto de vista del usuario, este método puede parecer inseguro, ya que cualquiera que robe el dispositivo móvil podrá usarlo para acceder al domicilio. Por tanto, necesitamos implementar una fuerte seguridad para evitar que esto suceda.

1.2 Estudio del Mercado y selección de la opción más factible

Entre las distintas opciones que se podrían pensar a la hora de no usar llaves para acceder a un domicilio, tendríamos el uso de un código pin en la puerta, de una tarjeta electrónica, tanto de banda

E

“Hacer felices a los demás: no hay nada mejor ni más bello”.

- Ludwig van Beethoven -

Introducción

22

22

magnética como RFID, el uso de teléfonos móviles o el uso de identificación biométrica.

En el caso de la identificación biométrica, el uso de escáneres de huellas, patrón del iris, o imagen termal tienen aún un el elevado coste y no han sido desarrollados lo suficiente, por lo que se ha decidido eliminarlos del estudio.

Hay que comprobar por tanto el mercado para ver qué opción sería la más aceptada por parte de los usuarios entre las restantes.

En un estudio realizado por el autor en la Universidad KTH sobre este tema, cuyos resultados más relevantes pueden observarse en la Figura 1, los datos obtenidos indican que los usuarios preferían el uso de su teléfono móvil de forma que se abriera la puerta de manera automática, por geolocalización o por proximidad. Un usuario podría abrir la puerta siempre que tuviera permiso de acceso con simplemente situarse cerca de ella. Sin embargo, dado que la gran mayoría de los teléfonos móviles que actualmente hay en el mercado no poseen tecnología NFC, no es posible realizar el producto de esta manera de un modo sencillo, al menos de momento.

La segunda opción más demandada fue la de usar una aplicación en tu Smartphone para abrir la puerta. Aquí es donde podemos empezar a trabajar con este proyecto, que encajaría a la perfección con las demandas de los usuarios. Debemos usar una App en el móvil que permita abrir la puerta a la que el usuario tenga acceso.

1.3 Descripción del producto a realizar

En primer lugar, el acceso se realizará por medio de un código QR, que será solicitado por el usuario y que tendrá un tiempo de validez determinado durante su generación. El usuario debe mostrar el código QR a una cámara web a la entrada del domicilio, del cual se extraerá la información encriptada y ésta será enviada al servidor que, tras desencriptar correctamente el contenido del código QR, comprobará que coincide con los datos guardados en la base de datos y enviará una respuesta afirmativa si es correcto o negativa si no lo es, lo cual abrirá la puerta o la dejará cerrada.

En segundo lugar, tenemos la interfaz de usuario: la interfaz web, que podrá accederse desde un Smartphone, una tablet, un ordenador o cualquier otro dispositivo que permita acceso a Internet. A través de la interfaz web el usuario podrá darse de alta en el servicio, añadir nuevos domicilios a su cuenta, comprobar los registros de acceso a los distintos domicilios y solicitar la generación de un código QR. De esta manera, el usuario puede utilizar el servicio incluso sin disponer de un Smartphone, simplemente necesita otro dispositivo portátil o puede incluso acceder imprimiendo el código QR.

Figura 1. Estudio sobre preferencias de usuario respecto a los distintos métodos de acceso

23 Apertura de puertas usando Códigos QR QR Door Opener

Por último, necesitamos un módulo de seguridad, para evitar que un atacante externo pueda

generar los códigos QR y acceder al domicilio del usuario. Para ello, vamos a usar una PKI (Public Key Infrastructure), en la que cada paquete enviado sea encriptado y autenticado. La comunicación entre la cámara web del lado cliente y el servidor irá sobre SSL/TLS y el acceso a la página web se realizará mediante HTTPS.

Un diagrama de la estructura básica del sistema puede observarse en la Figura 2.

1.4 Antecedentes

Este proyecto surge de una idea en la que el autor participó durante su estancia en la universidad Kungliga Tekniska Högskolan (KTH) de Estocolmo y, al ser una idea extraída de una sesión de brainstorming (lluvia de ideas), apenas hay precedentes. Allí, en la asignatura Product Realization del profesor Mark Smith, se nos propuso formar una especie de empresa ficticia, hacer un análisis de mercado para ver qué tipo de producto prefería un cliente potencial a la hora de abrir una puerta y, con esos datos, intentar realizar un prototipo asequible. En principio conseguimos implementar algo similar a lo que este proyecto expone, pero sin llegar al nivel de profundidad aquí obtenido, ya que no incluía seguridad, la base de datos era muchísimo más simple, no había comunicación cliente-servidor (el cliente y el servidor estaban incluidos dentro del mismo equipo). Básicamente la aplicación buscaba el abrir una puerta de acceso a un hackerspace [3] [4], de manera que los usuarios potenciales pudieran acceder a él si conocían a alguien en el interior, que ejerciera como una especie de “padrino” para él en la organización. Para ello, la idea era crear una especie de red social en la

Figura 2. Estructura básica del sistema

SSL/TLS

HTTPS

Introducción

24

24

que los nuevos usuarios pudieran ver los proyectos que se estaban llevando a cabo, o añadir los suyos propios, e intentar encontrar a alguien que pudiera ejercer como padrino. Lo más importante es que cualquiera podía intentarlo, basándonos sólo en el conocimiento (o las ganas de aprender) en un campo determinado. Por tanto, al controlar el acceso al hackerspace se controlaba también quién era tu padrino y, en función del proyecto al que te adhirieras, tenías acceso a unas zonas u otras.

Dado que era una asignatura con visión empresarial que lo que buscaba era darnos una perspectiva de cuál era el proceso a la hora de realizar un producto, llegó un punto en el desarrollo de la aplicación en la que el grupo de trabajo se dio cuenta de que era imposible implementar la aplicación en menos de dos semanas con los recursos disponibles. Por tanto, lo que finalmente hacía la aplicación era añadir nuevos usuarios a la base de datos usando la página web, generar un código QR con esos datos y, al mostrar este código QR a la cámara de acceso, dar un mensaje de acceso permitido si el código era correcto. Como podréis comprobar si seguís leyendo la memoria, este proyecto llega a un nivel de profundidad mucho más avanzado, realizando un verdadero producto.

Otro proyecto bastante similar es LibeTech QR Code Door Lock [5], realizado por un grupo de estudiantes de la Universidad de Cornell (Ithaca, Nueva York) en el año 2012, en el que usaron un código QR como método para abrir las puertas de las habitaciones de un hotel. En este caso, una página web permite insertar al usuario en la base de datos, guardando un hash de sus datos y enviándole un código QR a su correo, móvil o donde el usuario pida para que pueda usarlo para abrir la puerta. De nuevo se pueden observar debilidades respecto a este proyecto, en el que se va a implementar más seguridad para evitar que un atacante pueda generar el código QR a usar.

Como se puede observar, es un proyecto bastante novedoso, no hay demasiados precedentes (de hecho, sólo es uno ya que en el otro el autor participó como un activo). En los siguientes capítulos se mostrarán al lector los detalles del producto realizado en profundidad, cómo se implementaría y las posibles mejoras para realizar un sistema completo.

25 Apertura de puertas usando Códigos QR QR Door Opener

2 BASE TEÓRICA

n este capítulo se procede a explicar los conceptos básicos teóricos necesarios para entender el funcionamiento, las aplicaciones y los protocolos usados en el proyecto. Con ello se espera que el lector no tenga problemas a la hora de entender la descripción de los elementos realizada en el resto de capítulos.

2.1 Infraestructura de Clave Pública (PKI)

Una Infraestructura de Clave Pública (de aquí en adelante PKI) está formada por una serie de componentes necesarios para la distribución de claves públicas de manera segura. Para ello, se necesita de una serie de certificados, un repositorio para la obtención de certificados, y métodos que permitan la revocación de certificados y la evaluación de la cadena de certificados desde la autoridad de certificación principal hasta el certificado que está siendo evaluado [6]. En los siguientes apartados se describirán los distintos tipos de modelos de confianza PKI, así como el modelo de certificado X509, que es el usado en este proyecto, lo que permitirá conocer a fondo cómo funcionaría la PKI creada.

2.1.1 Modelos de confianza PKI

Supongamos que tenemos dos usuarios que se quieren comunicar de manera segura entre sí, Alice y Bob. Alice tiene que obtener la clave pública de Bob para poder enviarle datos de forma encriptada, y esta clave debe ser obtenida de manera segura. En este apartado se explicarán las distintas formas que tiene Alice de obtener la clave de Bob, en función del modelo de PKI elegido [7]. En el caso de este proyecto, el modelo usado es el de Monopolio en la comunicación entre el módulo de lectura del código QR y el servidor de Aplicación, y el Oligárquico en la comunicación a través de la página Web.

2.1.1.1 Modelo de Monopolio

En este caso tendremos una sola Autoridad de Certificación para todo el mundo, todos los usuarios confiarán en ella y sólo en ella, por lo que todos los certificados serán firmados en esta autoridad y será fácil comprobar que estos certificados son válidos, sólo se necesitará conocer el certificado de esta Autoridad de Certificación. Idealmente, es el modelo más simple y sería el que se querría implementar, pero presenta dos problemas principales, que son: se tendría un único punto de fallo, es decir, al todo el mundo confiar en una única Autoridad de Certificación, si por cualquier motivo las claves de esta autoridad son comprometidas, no habría posibilidad de tener comunicaciones seguras; y una sola Autoridad de Certificación puede poner a la firma de certificados el precio que

E

Cuando alguien desea algo debe saber que corre riesgos y por eso la vida vale la pena.

- Paulo Coelho -

Base Teórica

26

26

quiera, al no tener ningún tipo de competencia. En este proyecto se ha escogido este tipo de modelo, creando una Autoridad de Certificación propia de prueba que sería la única en el sistema, de manera que no sea necesario pagar a una Autoridad de Certificación confiable por sus servicios para que firme los certificados emitidos, pero si este producto se comercializara no podría usarse este tipo de modelo.

2.1.1.2 Modelo Oligárquico

Este modelo es el usado en los navegadores actualmente, y consiste en un conjunto de Autoridades de Certificación que son preinstaladas (previo pago a los creadores del navegador) y en las cuales se confían, lo que significa que cualquier certificado recibido que esté firmado por alguna de dichas autoridades es aceptado como válido. Por supuesto, el usuario puede insertar nuevas Autoridades o eliminar las preinstaladas.

La principal ventaja de este modelo es que al tener varias Autoridades de Certificación, habrá varias posibilidades disponibles en el mercado, con la consiguiente competencia entre ellas por captar clientes y la disminución de los precios, lo cual es beneficioso para los usuarios. Sin embargo, se puede considerar este modelo incluso menos seguro que el de Monopolio, por varios motivos:

- Hay bastantes más posibilidades de que se vea comprometida una Autoridad de Certificación dentro de muchas, lo que implica que es más fácil hacer que parte de los certificados sean comprometidos.

- Las Autoridades de Certificación en las que se confía no son elegidas por el usuario, sino por los creadores de los navegadores, lo cual significa que el usuario pierde parte de control a la hora de establecer la confianza, ya que no se sabe en qué se basan los creadores de navegadores a la hora de decir si confían en una Autoridad de Certificación o no (hay algunas que sólo se basan en si esa Autoridad tiene dinero para pagar el estar preinstalada en su navegador o no).

- A la hora de añadir certificados en los que el navegador no confía, puede ser relativamente fácil para un atacante engañar al usuario y hacerle creer que su certificado es válido ya que, aunque un usuario sea capaz de ver los distintos campos de éste, los valores insertados pueden ser totalmente falsos y no habrá forma de demostrar lo contrario.

2.1.1.3 Modelo Anárquico

En este modelo, usado en PGP, no habrá ninguna Autoridad de Certificación, sino que cada usuario será responsable de añadir a su lista una serie de entidades confiables, lo que se consigue firmando el certificado de dicha entidad con su clave privada. Digamos por ejemplo que Alice firma el certificado de su amigo, y que Bob confía en Alice. En este modelo, Bob confiaría en el amigo de Alice, ya que él confía en Alice y Alice en su amigo. Pero va más allá, como puede ser comprobado en la Figura 3: supongamos ahora que el amigo de Alice confía en su propio amigo y firma su certificado. Al Bob confiar en Alice, confía en el amigo de Alice ya que ella ha firmado su certificado, y por tanto confía en el amigo del amigo de Alice, es decir, se forma una cadena de confianza en la que si un usuario confía en otro, confiará en todos los usuarios en los que confíe ese otro.

Figura 3. PKI Modelo Anárquico (Copyright Kungliga Tekniska Högskolan, IK2206 2012)

27 Apertura de puertas usando Códigos QR QR Door Opener

Este modelo elimina por completo la dependencia de Autoridades de Certificación pero no es funcional a gran escala, debido principalmente a dos motivos: el gran número de personas y el tener varios certificados por cada una llevaría a una base de datos con billones de entradas, por lo que a la hora de buscar el certificado de un usuario se hace impracticable. Además, la cadena de confianza se establece de tal manera que si Bob confía en Alice, confía en todos los usuarios en los que Alice confíe, y en todos los usuarios en los que confíen los usuarios en los que Alice confía, y así hasta el infinito. Puedes encontrar una cadena de confianza que te lleve a Carol a partir de Alice, en la que tú confías, pero ¿cómo sabe Bob que los usuarios intermedios entre Alice y Carol son de confianza? ¿Por qué debería confiar en ellos?

2.1.2 Certificado X.509

Los certificados X.509 son usados, entre otros protocolos, por SSL, por lo tanto será el tipo de certificados usado en este proyecto. Aunque este tipo de certificados tiene un formato bastante inapropiado y difícil de entender, es el tipo asumido para las PKIs e incluso el IETF eligió basar el formato estándar de certificados en X.509.

El principal problema de los certificados X.509 viene dado por los nombres, que utilizan formato X.500. En la actualidad, todos los usuarios están acostumbrados a nombres de Internet, del estilo us.es, o a nombres de email, del estilo [email protected]. Sin embargo, los nombres X.500 tendrán el siguiente formato

C=SP, O=nombre de la organización, OU=Telemática, CN=Alice

donde C significa País, O significa organización, OU significa unidad de organización, y CN es el nombre común [8] (y esto en un formato amigable para ser leído, en realidad se codifica de otra manera ilegible). Esto presenta una debilidad, ya que si, por ejemplo, un usuario intenta acceder a una página web usando HTTPS (HTTP sobre SSL/TLS), el navegador no tendrá forma de comprobar que el nombre X.500 coincide con el nombre de Internet, comprobando simplemente que el certificado esté firmado por una Autoridad de Certificación correcta. Se puede observar un posible ataque ante esta situación, el cual se expone el siguiente ejemplo: Alice quiere acceder de manera segura a https://www.bancoalice.com, pero comete un error de escritura y accede a https://www.bancalice.com. Supongamos ahora que un atacante tiene dicho dominio y que tiene un certificado firmado por una Autoridad de Certificación reconocida, que envía cuando se quiere acceder a dicho dominio. El navegador de Alice sólo comprobará que el certificado enviado esté firmado por una autoridad de confianza y, al ser así, conectará a Alice a la web del atacante de manera “segura”.

Los campos de un certificado X.509 se pueden observar en la Figura 4, entre los cuales destacan los siguientes:

- Número de serie del certificado (Certificate Serial Number): es un entero que, junto con el nombre de la Autoridad de Certificación emisora, identifica unívocamente al certificado. Aunque en principio es ilegal que haya dos certificados digitales con el mismo número de serie, un mal uso de una Autoridad de Certificación creada por un usuario puede llevar a que los haya.

- Emisor (Issuer): nombre en formato X.500 de la Autoridad de Certificación emisora del código. En el caso del certificado de una Autoridad de Certificación raíz, el emisor y el sujeto serán el mismo.

- Periodo de validez (Validity Period): este campo contiene dos partes, no antes (not before), que indica cuándo empieza a ser válido el certificado y no después (no after), que indica hasta cuándo se puede usar el certificado.

- Nombre del sujeto (Subject Name): nombre en formato X.500 de la entidad para la que se está creando el certificado, es decir, cuya clave se está firmando.

- Información de la clave pública del sujeto (Subject Public Key Information): este campo

Base Teórica

28

28

contiene dos partes, el identificador del algoritmo usado, con algunos parámetros opcionales, y la clave pública del sujeto.

- Firma Digital de la Autoridad de Certificación (Certification Authority’s Digital Signature): este campo contiene la firma digital de la Autoridad de Certificación para garantizar que el certificado es válido. La firma consistirá en un hash de todo el certificado encriptado usando la clave privada de la Autoridad de Certificación, de manera que todo el mundo pueda desencriptarla y comprobar que el hash calculado y el que hay en el certificado digital coinciden.

2.2 SSL/TLS

En esta sección se va a explicar la versión 3 del protocolo SSL y TLS. SSL/TLS permite que dos partes de una comunicación se autentiquen, establezcan la clave de sesión y que puedan comunicarse de manera segura. SSL/TLS tiene como objetivo principal el proveer de privacidad e integridad la comunicación entre dos entidades, lo que significa proteger la información transmitida entre dos extremos de una comunicación.

SSL/TLS implementa seguridad a nivel de aplicación, y funciona sobre TCP, lo cual le permite ser un protocolo mucho más simple, al no tener que preocuparse sobre pérdidas de paquetes y retransmisiones, haciendo TCP todo este trabajo.

2.2.1 SSL Handshake

Un ejemplo básico de cómo se inicia la sesión SSL y cómo se generan las claves se puede observar en la Figura 5, donde se pueden comprobar cuáles son los mensajes intercambiados, que serán explicados a continuación. Como se puede comprobar, tras la finalización del SSL handshake, la

Figura 4. Modelo de Certificado X.509

29 Apertura de puertas usando Códigos QR QR Door Opener

conexión será segura, al estar encriptada con la Master Secret que sólo conocen cliente y servidor, y que ha sido generada a partir de tres números aleatorios intercambiados durante este proceso [9].

1. Client Hello: En este primer mensaje, el cliente envía al servidor un número aleatorio, un identificador de sesión, que es opcional y que permitiría retomar una sesión abierta anteriormente, los algoritmos de cifrado que permite usar y los métodos de compresión admitidos.

2. Server Hello: con este mensaje, el servidor envía su número aleatorio al cliente, el identificador de sesión opcional para retomar una sesión previa, y la elección del algoritmo de cifrado y el método de compresión.

3. Certificate: tras el server hello, el servidor envía al usuario un mensaje que contiene uno o más certificados. Estos certificados serán comprobados por el cliente para garantizar que son válidos.

4. Client Certificate Request: el servidor pide al usuario su certificado digital para autenticarlo. 5. Client Certificate: el cliente envía al usuario su certificado digital, que contiene su clave

pública firmada por una Autoridad de Certificación. El servidor también comprobará que el certificado del cliente es correcto.

6. Client Key Exchange: el cliente genera un número aleatorio conocido como Pre-Master Secret y se lo envía al servidor encriptado con la clave pública de este, obtenida del certificado digital, de manera que sólo él pueda desencriptarlo con su clave privada y conocer así el Pre-Master Secret.

7. Switch to negociated cipher: A partir de los dos números aleatorios enviados por cada parte y del Pre-Master Secret, cliente y servidor calculan el Master Secret, y envían al otro extremo un mensaje de cambio de cifrado para usar la conexión encriptada.

8. SSL-Handshake finished: Finalmente, ambos extremos se envían un mensaje de fin del SSL handshake.

Figura 5. SSL Handshake con Autenticación de ambas partes usando certificado (Copyright Connect Community Portal 2010)

Base Teórica

30

30

Como puede observarse en la Figura 6, el Master Secret se genera a partir del Pre-Master Secret, y los números aleatorios intercambiados por cliente y servidor. Se tendrán 3 paquetes, combinaciones de estos tres valores y 3 cadenas distintas, procediendo a calcular el hash mediante el algoritmo SHA a cada uno de los paquetes. Posteriormente, se añadirá a cada uno de los 3 hashes el pre-master secret y se calculará un resumen de cada uno mediante el algoritmo MD5, los cuales se unirán para formar el Master Secret.

2.2.2 Keystore y Truststore del Keytool de Java

A la hora de usar SSL en una aplicación Java, es importante tener bien definidos un Keystore y un Truststore, almacenes de claves que permiten a la aplicación realizar correctamente el SSL handshake para poder establecer la conexión segura de manera satisfactoria.

Un keystore es un almacén de claves que contiene las que serán usadas por el usuario a la hora de autenticarse, es decir, contendrá su propio certificado digital, así como su clave privada para demostrar la autenticidad.

En cuanto a un truststore, es un almacén de claves que contiene los certificados de las entidades en las que el usuario confía. Un ejemplo podría ser el almacén de claves donde están guardados los certificados de las Autoridades de Certificación de un navegador.

En el caso de este proyecto, en la comunicación entre el módulo de lectura del código QR en el domicilio del cliente y el servidor de aplicación, el módulo de lectura tendrá un keystore que contendrá su certificado firmado por la Autoridad de Certificación del sistema y su clave privada, y un truststore que contendrá el certificado digital de la Autoridad de Certificación para poder comprobar que los certificados enviados por otras entidades, en este caso el servidor de aplicación, sean correctos. El servidor de aplicación tendrá un keystore que contendrá su certificado firmado por la Autoridad de Certificación del sistema y su clave privada, y un truststore que contendrá el certificado digital de la Autoridad de Certificación del sistema para poder comprobar que los certificados enviados por los usuarios han sido generados por dicha Autoridad de Certificación.

Figura 6. Generación del Master Secret (Copyright Thomas, SSL and TLS Essentials)

31 Apertura de puertas usando Códigos QR QR Door Opener

2.3 Formas de guardar contraseñas en una base de datos

A la hora de ofrecer acceso a ciertos servicios, es muy usual solicitar al usuario la inserción de su nombre de usuario y su contraseña como método de autenticación. Si la conexión se realiza sobre un túnel seguro, digamos HTTPS, la contraseña no podrá ser obtenida por un atacante que intente mirar la conexión. Sin embargo, si un atacante consigue acceder a la base de datos del sistema, donde están guardadas las relaciones entre usuarios y contraseñas, sí podrá obtener qué contraseña se corresponde a cada cliente. Por tanto, las contraseñas no deberán ser guardadas en claro en la base de datos, ya que cualquiera capaz de acceder a la base de datos del sistema podrá obtener las contraseñas y, por tanto, podrá hacerse pasar por el usuario a la hora de acceder al servicio.

2.3.1 Almacenamiento del Hash de las contraseñas

Una posible forma de almacenar las contraseñas en la base de datos y evitar que un atacante que sea capaz de acceder a ella las obtenga es mediante el guardado no de la contraseña en sí, sino del hash de esta. Así, aunque un atacante sea capaz de saber qué hash corresponde a cada nombre de usuario, no será capaz de conocer la contraseña necesaria para acceder al sistema, al menos directamente. Las funciones hash deben cumplir las propiedades de que sean funciones sin retorno, es decir, que a partir de la salida es imposible conocer la entrada, lo que evitara que a partir del hash se pueda obtener la contraseña; resistente a colisiones, es decir, que debe ser difícil encontrar dos entradas distintas que den la misma salida; y además deben ser tales que un cambio pequeño en la entrada suponga un cambio grande en la salida, es decir, que el mínimo cambio de un bit, cambie muchos bits en la salida.

Sin embargo, sigue existiendo la posibilidad de obtener la contraseña a partir de los hashes extraídos de la base de datos. El método usado será la computación de una serie de hash de contraseñas y la comparación posterior de esos hashes con los extraídos de la base de datos, de manera que si alguno coincide, se conocerá cuál es la contraseña correspondiente a ese hash y por tanto, se podrá acceder con las credenciales de ese usuario. De hecho, si el atacante quiere probar contraseñas usuales, no le sería necesario ni siquiera calcular los hashes de dichas contraseñas, sino que podría obtenerlos directamente a través de la red de manera gratuita, contenidos en unas tablas conocidas como Rainbow Tables [10]. Por tanto, el ataque se hace mucho más sencillo, ya que el atacante sólo debería comprobar si alguna de las entradas en la tabla donde se encuentren los hashes de las contraseñas coincide con alguno de los valores en las Rainbow Tables, obteniendo así la contraseña de la que proviene dicho hash.

2.3.2 Almacenamiento del Hash de las contraseñas con Sal

La sal son una serie de bits generados de manera aleatoria que se añaden a la contraseña antes de hacer el hash, de manera que si varios usuarios tuvieran la misma contraseña, sus entradas en la base de datos del sistema serían distintas. Esto hace que el intento de obtención de las contraseñas de usuario por parte del atacante sea más complicado, ya que conlleva la computación previa no ya de un hash por cada contraseña que puede valer para todos los usuarios del sistema, sino un hash por cada contraseña para cada usuario concreto, ya que cada usuario tendrá su propia sal. Esto provocará una necesidad de recursos mayor por parte del atacante a la hora de intentar extraer las contraseñas, ya no valdrá con usar las Rainbow Tables precomputadas, sino que deberá calcular él mismo su propia tabla de hashes para cada uno de los usuarios del sistema si quiere obtener sus contraseñas.

Este es el modo más seguro de almacenamiento de contraseñas en la base de datos y, aunque puede seguir rompiéndose, es el más usado en la actualidad, y el que se ha usado en este proyecto.

Base Teórica

32

32

33 Apertura de puertas usando Códigos QR QR Door Opener

3 ANÁLISIS DE LA APLICACIÓN

l proyecto se puede dividir en 2 partes principales e independientes en principio, pero que se comunican entre sí para permitir al sistema completo funcionar de una manera coordinada. Estas 2 partes son el cliente y el servidor, que a su vez se pueden subdividir en 2 en la parte cliente y 4 en la parte servidor, debiendo añadir además la comunicación cliente-servidor. A

continuación se explica cuáles son las funciones de cada una de las partes y cómo se realiza la comunicación entre ellas.

3.1 Cliente

Como ya hemos explicado, el cliente dispone de una página web para entrar en el servicio, la cual podrá ser accedida desde cualquier dispositivo con acceso a Internet. Las opciones disponibles en la página web serían Creación de Nuevos Usuarios, Acceso a la cuenta, Modificación de datos personales, Muestra de Casas, con generación de códigos QR de Acceso, y Vista de Registros de acceso. Cada una de estas partes será explicada en profundidad en la parte de servidor.

Además, habrá instalado un dispositivo electrónico en la puerta que se desee abrir, que contiene una cámara que se encarga de la detección y lectura del código QR mediante el uso de código Java, y del envío de los datos leídos en el código QR al servidor.

3.1.1 Website (GUI)

El sitio web constará de dos partes diferenciadas, la creación de nuevos clientes y el acceso a una cuenta creada anteriormente, que controlarán todas y cada una de las funciones que puede realizar el usuario.

En cuanto a la creación de nuevos clientes, se presentará un formulario que el usuario deberá rellenar, incluyendo los datos personales (nombre, NIF, correo electrónico y teléfono), y el nombre de usuario y contraseña para el login en esta web. Por cada nuevo usuario, el servidor generará una entrada nueva en la base de datos, cuyo índice principal será el NIF de usuario, ya que permite identificar unívocamente al usuario.

En cuanto al acceso, se pedirá el usuario y contraseña, se comprobará que ese usuario existe en la base de datos del sistema y, de ser así, se permitirá el acceso a los datos personales, a las distintas viviendas, a los registros de acceso con los QRs generados, así como la inserción de nuevos domicilios donde el usuario quiera acceder. También permitirá la generación de certificados digitales para las viviendas, de manera que puedan usarlo para identificarse, y de un código QR que se mostrará por pantalla para poder ser usado a la hora de acceder a la vivienda.

E

“La educación es el arma más poderosa que se puede usar para cambiar el mundo”

- Nelson Mandela -

Análisis de la aplicación

34

34

3.1.2 Módulo de detección y lectura del código QR

Este módulo se encarga de detectar un patrón de código QR delante de la cámara y extraer la información contenida en él, que se enviará mediante una conexión SSL/TLS encriptada para que sólo el lado servidor pueda leer el contenido real, y sólo el usuario pueda saber si la respuesta enviada por el servidor tras la comprobación es afirmativa o negativa, además de para demostrar la autenticidad del lado usuario. Para evitar el consumo continuado de energía, el programa se iniciará cuando el usuario pulse un botón, y se apagará si pasan 100 iteraciones sin leer ningún código QR, o si el código QR se ha leído correctamente. En la Figura 7 se puede observar gráficamente el diagrama de flujo de este módulo.

3.2 Servidor

En el lado servidor, tendremos cuatro servidores, tres intercomunicados entre sí, el servidor web, el servidor de la aplicación y el servidor de base de datos, y la Autoridad de Certificación. En principio, aunque son cuatro servidores distintos, se hallarán todos dentro de la misma máquina, para aprovechar los recursos disponibles, lo cual implica una mayor velocidad de interactuación entre los servidores, pero también una mayor probabilidad de que el servicio completo caiga y de sufrir un posible ataque.

3.2.1 Servidor Web

La misión del servidor web será procesar las peticiones enviadas por el cliente a la página web para realizar todas sus funcionalidades, que serán explicadas a continuación. Se comunicará con el servidor de base de datos para la autenticación de usuarios, la modificación de los datos de usuario, la muestra de las casas y su actualización y la obtención de los registros de acceso a domicilios.

Figura 7. Diagrama de Flujo de la lectura del código QR y apertura de la puerta

35 Apertura de puertas usando Códigos QR QR Door Opener

3.2.1.1 Nuevo Usuario

Cuando un nuevo usuario desee crear una cuenta de acceso, se le mostrará un formulario que deberá rellenar con sus datos personales y su usuario y clave de acceso al servicio por partida doble, para evitar errores tipográficos. Como se puede observar gráficamente en la Figura 8, el servidor web comprobará en el servidor de base de datos que no exista otro usuario con el mismo identificador, correo electrónico, número de teléfono, ni el mismo alias, y posteriormente comprobará que la contraseña insertada por partida doble coincida, pasando a insertar al nuevo usuario en el sistema si no ha habido ningún error.

3.2.1.2 Acceder a cuenta

El proceso de acceso a la cuenta conllevará la comprobación de que el nombre de usuario y la contraseña introducidos se encuentren en la base de datos, de manera que si se producen tres intentos fallidos consecutivos la cuenta quedará bloqueada y el usuario deberá ponerse en contacto con el administrador del sistema, como se puede se observar en el diagrama de flujo de la Figura 9. La contraseña no estará guardada en claro en la base de datos, sino que estará en forma de hash con sal, lo cual permitirá una mayor seguridad contra ataques que busquen extraer las contraseñas de los usuarios, aunque conllevará que cuando el usuario quiera iniciar sesión e inserte la contraseña haya que recomputar el valor del hash con la sal, lo que llevará a un mayor uso de la capacidad del sistema y a un mayor tiempo de comprobación.

3.2.1.3 Modificación de información

Una vez que el usuario esté autenticado, podrá realizar las modificaciones pertinentes en sus datos personales, tales como correo, teléfono, nombre de usuario y contraseña. Para la modificación de la contraseña, se pedirá insertar la contraseña actual para verificar que es correcta, así como la inserción de la nueva contraseña dos veces para evitar que haya errores. Por supuesto, se realizará la operación de hash de la contraseña y la sal antes de almacenar la nueva contraseña en la base de datos. A la hora de modificar correo, teléfono y nombre de usuario se comprobará que no exista ninguno coincidente en la base de datos, ya que estos valores deben identificar unívocamente al usuario. El NIF del usuario no podrá ser modificado, ya que este valor es el que identifica a un usuario realmente y además es un valor que dura de por vida.

Figura 8. Diagrama de flujo de la creación de un nuevo usuario

Análisis de la aplicación

36

36

3.2.1.4 Presentación de casas de usuario

El usuario podrá ver todas las casas a las que tiene acceso, eliminarlas, añadir nuevas, solicitar un certificado digital para dicha casa y generar códigos QR de acceso a la casa solicitada. Si alguna de las casas añadidas se encuentra ya en el servidor, el anterior usuario deberá confirmar la identidad del nuevo usuario que quiere acceder a ella, es decir, el primer usuario será una especie de administrador de ese domicilio y deberá validar a todos los nuevos usuario que quieran tener acceso a ella. Si al eliminar una casa ésta está en posesión de más de un usuario, sólo se borrará la relación entre el usuario actual y la casa, a no ser que el usuario que la borra sea el administrador, en cuyo caso se borrará todas las relaciones de usuarios a ella, dejando los datos de la casa en la base de datos para poder presentar sus registros de acceso. Cuando el usuario requiera la generación de un certificado digital, se enviará una solicitud al servidor para la creación de dicho certificado, que, tras ser creado e insertar su número de serie en el registro de la casa correspondiente en la base de datos, será enviado al usuario a su correo junto con las instrucciones de instalación.

3.2.1.5 Generación de código QR

Cuando un usuario solicite la generación de un código QR a través de la página web, se le pedirá que inserte el tiempo de validez de dicho código, permitiéndole seleccionar si la duración estará especificada en minutos, horas y días y el número de ellas (que debe ser entero). Tras esto, se

Figura 9. Diagrama de flujo del proceso de login

37 Apertura de puertas usando Códigos QR QR Door Opener

llamará al método de generación del código QR, en el que irán, en forma de cadena, los campos identificación de QR, identificación de Usuario, identificación de Casa, Fecha de Expedición y Fecha de Validez, todo ello encriptado en el formato del código QR. Se guardarán los datos de generación en la base de datos, para que pueda verificarse posteriormente si la información leída por la cámara en la puerta del domicilio es correcta y fue generada por el servidor.

Tras esto, se mostrará por pantalla el QR creado, para que el usuario pueda usarlo para acceder al domicilio. De esta manera, el usuario puede usar dicho código directamente desde su móvil o tablet, imprimirlo desde el ordenador y usarlo en la entrada de la casa, guardar la imagen, enviársela a otro usuario para que tenga acceso a su domicilio, etc.

En la Figura 10 se puede observar el diagrama de flujo correspondiente.

3.2.1.6 Presentación de registros

En la base de datos se van a almacenar, entre otras cosas, los registros de acceso y uso del código QR en el domicilio. Cada vez que un código QR sea usado en un domicilio, se guardará una nueva línea en la base de datos que indicará la hora de acceso, el identificador del código QR usado, y si el acceso ha sido válido o no, además de un código de error indicando el tipo de fallo si lo ha habido. De esta manera, si el usuario detecta algún tipo de problema en el acceso a su domicilio, puede ponerse en contacto con el administrador y saber a qué se ha debido el fallo. Además, esto supondrá más seguridad, ya que se podrá detectar cualquier tipo de anomalía en el acceso, de tal manera que si el usuario observa que alguien ha intentado acceder si su permiso, puede saber cuándo e incluso qué usuario.

En la página web, se mostrará una tabla con los distintos accesos realizados con códigos QR emitidos por el usuario, de manera que aparecerá la hora de acceso, el domicilio, el nombre del usuario emisor, el nombre del usuario que ha accedido y el error producido, si es que lo ha habido, permitiendo al usuario conocer muchísima información sobre el uso que se da a los códigos generados por él.

3.2.2 Servidor Base de Datos

Este servidor contendrá una base de datos en la que se guardará toda la información relevante a usuarios, domicilios, códigos QR generados y registros de acceso. En los siguientes subapartados se

Figura 10. Diagrama de flujo de la solicitud de código QR

Análisis de la aplicación

38

38

procede a explicar con detalles cada una de las tablas creadas en la base de datos, junto con los tipos de dato requeridos en cada campo.

3.2.2.1 Tabla de Usuarios

La tabla de usuario contendrá toda la información personal relacionada con éste, lo cual quiere decir que se incluirá en ella su identificador (NIF en este caso), su nombre, su teléfono, su correo, su nombre de usuario para acceder al servicio y su contraseña, que estará guardada en forma de hash con sal, para evitar que un atacante pueda acceder a las contraseñas si consigue acceder a la tabla en la base de datos. El uso de la sal permite evitar la obtención de la contraseña mediante el uso de Rainbow Tables. El campo identificador estará relacionado con el mismo campo en las tablas Permisos y Códigos QR. Una descripción de los distintos campos puede observarse en la Tabla 1.

Tabla 1. Campos de la tabla de Usuario

Campo Breve Descripción

idUsuario Identificador (Cadena de nueve dígitos)

Nombre Nombre de usuario (Cadena)

Teléfono Número de teléfono del usuario (Cadena)

Email Correo electrónico del usuario (Cadena)

User Nombre de usuario para la aplicación (Cadena)

Password Sal+hash de la contraseña insertada por el usuario (Cadena)

Intentos Intentos realizados por el usuario antes de bloquear la cuenta (Entero)

UltimoAcceso Fecha del último acceso del usuario al sistema (Long)

3.2.2.2 Tabla de Casas

La tabla de casas contendrá el identificador de la casa, la dirección completa, compuesta por calle y número, localidad y código postal, así como el identificador del certificado digital emitido para dicha casa. El campo identificador de casa estará relacionado con el mismo campo en las tablas Permisos y Códigos QR. Una descripción de los distintos campos puede observarse en la Tabla 2.

Tabla 2. Campos de la Tabla Casas

Campo Breve Descripción

idCasa Identificador (Entero autoincrementado)

Dirección Calle y número de la casa (Cadena)

Población Población donde se encuentra el domicilio (Cadena)

Código Postal Cadena con el código postal de la población (Entero de cinco digitos)

idCertificado Número de serie del certificado digital asociado a la casa (Cadena)

39 Apertura de puertas usando Códigos QR QR Door Opener

3.2.2.3 Tabla de Permisos

La tabla de permisos contendrá una relación entre las casas y los usuarios que tienen acceso a ellas, de manera que esta tabla permite comprobar si un usuario concreto tiene acceso a los datos de una casa determinada y si puede solicitar un código QR para acceder a ella. Los campos de identificación de casa y usuario estarán relacionados con los mismos campos en las tablas Casas y Usuarios. Una descripción de los distintos campos puede observarse en la Tabla 3.

Tabla 3. Campos de la tabla Permisos

Campo Breve Descripción

idPermiso Identificador del permiso (Entero autoincrementado)

IdUsuario Identificador del Usuario (Cadena de nueve dígitos)

IdCasa Identificador de la Casa (Entero)

Válido Indica si el permiso es válido actualmente o es anterior (Boolean)

Administrador Indica si este usuario es el administrador de la casa o no (Boolean)

3.2.2.4 Tabla de Códigos QR

La tabla de códigos QR almacenará la información contenida en dichos códigos, es decir, el identificador del QR, el identificador de Usuario, el identificador de la casa, la fecha de expedición del código y la fecha de validez del mismo. Los campos identificador de usuario e identificador de casa estarán relacionados con los mismos campos en las tablas Casas y Usuarios, mientras que el campo identificador de código QR estará relacionado con el mismo campo en la tabla Registros de Acceso. Una descripción de los distintos campos puede observarse en la Tabla 4.

Tabla 4. Campos de la Tabla de Códigos QR

Campo Breve Descripción

idCódigoQR Identificador del código QR (Entero autoincrementado)

IdUsuario Identificador del Usuario creador (Cadena de nueve dígitos)

IdUsuarioAcceso Identificador del Usuario que accede (Cadena de nueve dígitos)

IdCasa Identificador de la Casa (Entero)

Fecha de Expedición Fecha de creación del QR en milisegundos (Cadena)

Fecha de Expiración Fecha en la que caduca el código QR en milisegundos (Cadena)

3.2.2.5 Tabla de Registros de acceso

La tabla de registros de acceso contendrá la información de uso de los distintos códigos QR, con la fecha de uso del QR concreto y una especificación del error producido en el acceso (que puede ser

Análisis de la aplicación

40

40

no error en caso de acceso correcto). En campo identificador de QR estará relacionado con el mismo campo en la tabla de Códigos QR. Una descripción de los distintos campos puede verse en la Tabla 5.

Tabla 5. Campos de la Tabla Registros

Campo Breve Descripción

idRegistro Identificador del Registro de Acceso (Entero autoincrementado)

IdQR Identificador del Código QR (Entero)

IdUsuario Identificador del usuario generador del QR (Cadena de nueve dígitos)

IdCasa Identificador de la casa para la que se generó el QR (Entero)

Fecha Acceso Fecha de Uso del QR en milisegundos (Cadena)

Tipo de Error Identificador del tipo de error (Entero)

3.2.2.6 Base de Datos completa

Finalmente, se procede a mostrar en la Figura 11 cómo sería el diseño de la base de datos completa de manera gráfica para que su comprensión sea más rápida. Se pueden observar cada una de las tablas y campos, junto con el tipo de dato de cada uno de ellos.

3.2.3 Servidor de Aplicación (Servidor QR)

El servidor de aplicación se encargará del establecimiento de la conexión con el usuario y de la recepción y comprobación de la información de códigos QR enviada por el módulo de lectura en el domicilio. Se comunicará con el cliente mediante una conexión segura con SSL/TLS, en la que tanto

Figura 11. Estructura de la Base de Datos

41 Apertura de puertas usando Códigos QR QR Door Opener

cliente como servidor tendrán un certificado digital, para recibir la información que ha sido leída a través de la cámara, y con el servidor de Base de Datos para obtener la información guardada sobre el código QR y comprobar que la información recibida es correcta.

3.2.3.1 Apertura de nueva conexión

El servidor de aplicación estará escuchando en un puerto concreto, esperando a recibir solicitud de nueva conexión. Cuando la reciba por parte del usuario, abrirá un nuevo socket y trabajará con un hilo independiente por cada nueva conexión, lo que permitirá tener más de una al mismo tiempo. En cada nueva conexión, el servidor comprobará el certificado enviado por el cliente, para ver si se corresponde con el identificador de casa contenido en el código QR, de manera que este código QR sólo se pueda usar en la casa para la que ha sido creado. Por cada conexión abierta, el servidor se quedará esperando el envío de la información del código QR leído en el cliente, para proceder a la decisión de si abrir la puerta o no, comprobando dicha información en la base de datos.

3.2.3.2 Lectura de códigos QR

La información del código QR será leída a través del socket abierto. No se enviará la imagen en sí, sino el contenido de ella, la información que había encriptada en el código QR, que lógicamente no irá en claro a través de la red, sino encriptada usando la clave pública del servidor, para que sólo él pueda desencriptarla, y firmada como prueba de autenticidad, todo ello conseguido a través de la conexión SSL/TLS. Una vez desencriptada la información y verificada la autenticidad, se procede a comprobar que el código QR coincide con el que está guardado en la base de datos, comprobando si todos los campos coinciden y el tiempo de validez del código no ha finalizado. Si se cumplen todas las condiciones, se enviará una respuesta afirmativa al cliente, para que la puerta sea abierta. En caso contrario, la respuesta será negativa y la puerta permanecerá cerrada. En la Figura 12 se puede observar el diagrama de flujo explicativo de este módulo.

3.2.4 Autoridad de Certificación

La Autoridad de certificación se encargará de la creación y firmado de certificados digitales para el servidor de aplicación y para los módulos de lectura de códigos en las viviendas de los clientes. Al principio del desarrollo del proyecto, se genera el par de claves del servidor, y se crea y firma su certificado. Cada vez que un usuario solicite la creación de un certificado digital para alguna de sus casas, se generará un par de claves, se creará un certificado de cliente y se firmará, procediendo a continuación a enviarlo todo, junto con el certificado de la Autoridad de Certificación para que pueda comprobar que la firma es correcta y las instrucciones de instalación para que el módulo de lectura use dicho certificado a la hora de comunicarse con el servidor de aplicación.

3.3 Comunicación Cliente-Servidor

Habrá dos tipos de comunicación Cliente-Servidor, la realizada cuando el usuario intenta acceder a la página web y la que se produce entre el equipo donde está la cámara web de lectura del código QR y el servidor de la aplicación QR.

Análisis de la aplicación

42

42

3.3.1 Comunicación a través de la página web

Para el establecimiento de una conexión segura a la página web, se usará un certificado digital en el servidor de manera que se cree una conexión HTTPS, es decir, HTTP sobre TLS. El certificado usado será autofirmado por el servidor, de manera que no sea necesario comprarlo a una autoridad de certificación reconocida, aunque esto implica que cuando el usuario intente acceder a la web, se descargará el certificado autofirmado y se le pedirá confirmación de que lo acepta, ya que el navegador lo tomará como no seguro. Lógicamente, esto sólo se usa en el proyecto y si este producto se comercializara, se usaría un certificado firmado por una de las múltiples autoridades certificadoras reconocidas. Tras esto, se conectará al servidor web con las comunicaciones encriptadas, lo que evitará que un atacante externo pueda leer las comunicaciones o intentar suplantar la identidad de otro usuario.

3.3.2 Comunicación entre el módulo de lectura del código QR y el servidor de aplicación

En este caso se establecerá una conexión segura mediante SSL/TLS entre el cliente y el servidor. Cada una de las partes poseerá un certificado digital y su correspondiente clave privada generado por una Autoridad de Certificación que está en el propio servidor. El certificado de esta Autoridad de Certificación deberá estar tanto en el cliente como en el servidor para comprobar que la firma del certificado sea correcta, es decir, que el certificado ha sido expedido por la Autoridad de

Figura 12. Diagrama de flujo de la comprobación de código QR leído y enviado al servidor de aplicación

43 Apertura de puertas usando Códigos QR QR Door Opener

Certificación del sistema. Cuando se establece la conexión, se intercambian los certificados, se comprueban y en el lado servidor se comprueba además si el certificado digital se corresponde con el identificador de la casa que hay en el código QR leído, es decir, si el código QR se está usando en la casa para la que fue generado.

Análisis de la aplicación

44

44

45 Apertura de puertas usando Códigos QR QR Door Opener

4 DISEÑO DE LA APLICACIÓN

n este capítulo se procederá a explicar cada una de las partes presentadas en el capítulo anterior de manera detallada: cómo funciona, cómo está organizado, qué librerías, las herramientas usadas para facilitar el desarrollo, y los fallos encontrados durante el proceso así como su solución.

4.1 Herramientas usadas

El equipo usado tanto para el desarrollo del sistema como para la implementación de los servidores web, de base de datos, el servidor de la aplicación QR y la Autoridad de Certificación es un Toshiba L450, con procesador Pentium Dual-Core T4400 a 2.20GHz y 3 GB de memoria RAM.

El sistema operativo de desarrollo es Ubuntu 12.04 LTS [11], la última versión disponible al iniciar el desarrollo del proyecto. Se ha escogido una versión LTS de Ubuntu porque ofrece un soporte de larga duración, tanto en actualizaciones y seguridad como en funcionamiento general del sistema.

El lenguaje de programación usado para el desarrollo de la aplicación es Java tanto a la hora de desarrollar la aplicación como en el desarrollo del servidor web y la comunicación con la base de datos. La versión de Java usada es la 1.6.0_20, y el JDK instalado es el OpenJDK IcedTea6 1.9.10, descargado usando el gestor de paquetes Synaptic [12].

El IDE (Entorno de desarrollo) elegido es Eclipse versión 3.7.2, al que se le han añadido los paquetes Java EE Development Tools versión 3.3.2 y Web Development Tools versión 3.3.2, que permiten el desarrollo de aplicaciones web que añaden código Java en ellas, así como de otro tipo de aplicaciones Java. Se ha preferido usar un entorno de desarrollo porque permite que la escritura del código sea mucho más sencilla, ya que hace correcciones sobre la marcha, da clases por defecto con estructura definida según el tipo que se busque, y permite hacer una depuración del código de manera visual mucho más cómoda que usando una aplicación de línea de comandos.

La Base de Datos usará MySQL versión 5.5.31-0ubuntu0.12.04.2, que es gestionada de manera gráfica usando MySQL Workbench versión 5.2.38 [13], para permitir la generación y control de la base de datos de manera más cómoda para el usuario que usando la línea de comandos. Para la comunicación entre los servidores web y de aplicación y el de base de datos se ha usado el conector JDBC [14], que establece la conexión entre la aplicación Java y la Base de Datos MySQL. Gracias a este conector, podemos modificar datos, añadirlos, borrarlos y realizar consultas en la BBDD directamente desde la aplicación Java.

Apache Tomcat versión 7.0.26 [15] se usa como Servidor Web, ya que Tomcat permite el tratamiento de aplicaciones web Java en el servidor, lo que nos facilita el uso de código Java en la página web para la comunicación con el resto de servidores. Para la comunicación entre Apache y Tomcat, se usa el módulo mod_jk, ya que es el módulo recomendado por la web oficial de Apache respecto a mod_proxy [16].

E

“Un hermano puede no ser un amigo, pero un amigo será siempre un hermano”

- Benjamin Franklin -

Diseño de la aplicación

46

46

En cuanto a la creación de la página web, las partes estáticas se han programado con lenguaje HTML, pero también se usa lenguaje JSP para las partes dinámicas, lo que permite el uso de código Java a la hora de realizar las páginas y del uso de Servlets que ayuden a la comunicación con el resto de servidores. Además, se han usado hojas de estilo CSS, de manera que sea más sencillo retocar la parte gráfica de la interfaz de usuario. Los Servlets son programas Java que se interpretan en el servidor, transformando el resultado a lenguaje HTML que es lo que realmente se envía al cliente. Sus principales ventajas son que permiten portabilidad e independencia del servidor utilizado, ya que están escritos en lenguaje Java, permiten obtener información del cliente, los servlets se pueden llamar entre ellos, lo que permite una mejor estructuración y eficiencia, permite el uso de sesiones y pueden servir como enlace entre el cliente y una base de datos [17]. Viendo todas estas ventajas, está claro que encaja a la perfección con las características requeridas. Para el inicio del desarrollo gráfico de la página web, se ha usado Microsoft Office SharePoint Designer 2007, que ha servido como guía a la hora de realizar las hojas de estilos y aplicarlas a distintas páginas Web, así como para la creación de formularios, inserción de botones, etc., permitiendo al autor comprender cómo se crea una página web desde cero, ya que permite modificar gráficamente y ver el código generado con cada modificación posteriormente.

Para la creación del certificado autofirmado del servidor web, se ha usado OpenSSL versión 1.0.1. Esta aplicación, open source, implementa los protocolos SSL y TLS, así como una serie de herramientas criptográficas que, entre otras cosas, permiten la creación y firma de claves y certificados digitales [18].

Para la administración de los certificados digitales se ha usado la aplicación TinyCA versión 0.7.5, una herramienta gráfica que permite controlar la funcionalidad completa de una Autoridad de Certificación, desde la creación y revocación de certificados, hasta la exportación de estos en distintos formatos [19], lo que librará al administrador de tener que realizar todas estas operaciones mediante línea de comandos, cambiándolo por una interfaz gráfica sencilla de usar. TinyCA es realmente una interfaz de OpenSSL, lo que significa que los certificados creados tendrán el mismo formato que si se usara directamente la aplicación de línea de comandos.

4.2 Cliente

Como ya se expuso en el capítulo anterior, el lado cliente se puede dividir en dos partes principales:

La interfaz web de usuario, que permite el acceso al servicio web para el tratamiento de la información de usuario, las casas, ver los registros de acceso y la creación de códigos QR de acceso.

La parte de aplicación QR, que se encarga de detectar y leer el código QR y enviarlo al servidor para que éste dé el consentimiento de abrir la puerta o no. El puerto donde el servidor de aplicación está escuchando estará puesto de manera fija mientras que la IP será insertada por el usuario a la hora de iniciar la aplicación.

4.2.1 Website (GUI)

La interfaz web de usuario se ha diseñado usando el lenguaje JSP, ya que está basado en lenguaje Java, lo que nos permite integrar el servidor web de manera que se comunique con el resto de partes de nuestro proyecto sin problemas.

El sitio web cuenta con los siguientes servicios:

Login: que permite al usuario acceder a su cuenta mediante el uso de un par nombre de usuario/contraseña.

47 Apertura de puertas usando Códigos QR QR Door Opener

Alta de usuario: que permite la creación de un nuevo usuario en el sistema, solicitándole la inserción de los siguientes datos personales: nombre, NIF, número de teléfono, correo electrónico, nombre de usuario y contraseña.

Una vez logueado, el usuario podrá acceder al resto de servicios del sistema, que son:

Modificar datos de usuario: se permite al usuario la modificación del nombre de usuario, su número de teléfono, su correo, su nombre de usuario y su contraseña. El NIF, al ser único, no se modificará en ningún caso.

Ver casas de usuario: en este caso, el usuario puede ver las casas a las que tiene acceso, borrarlas (se borrarán completamente si el usuario es administrador de la casa, mientras que si es usuario autorizado sólo se borrará su relación con la casa), añadir nuevas casas, pedir certificados digitales y crear códigos QR para acceder a cualquiera de ellas. En cuanto a la petición de certificado, se enviará un correo al sistema de administración para solicitar su generación, tras lo cual se procederá a la creación de claves y certificado, que serán enviados al usuario a su dirección de correo electrónico junto con las instrucciones de instalación. A la hora de generar el código QR, le será solicitado el tiempo de validez del código, así como el usuario que va a acceder usando ese código, lo que permite al usuario, además de generar códigos para su propio acceso, solicitar el acceso de otra persona, y controlar cuándo ésta entra en el domicilio.

Ver Registros: se mostrará una tabla con los registros de acceso a los domicilios de usuario, que contendrá en cada fila la dirección del domicilio accedido, el nombre del usuario generador del código, el nombre del usuario que lo ha usado para acceder, la fecha de acceso completa y si ha habido algún fallo. Por ejemplo, se guardará el acceso si se usa un código caducado, o si el certificado digital ha sido incorrecto, mostrando un error que explique que ese código había alcanzado su fecha de expiración o que el certificado es incorrecto respectivamente.

En la sección, 4.3.1 Servidor Web, se puede encontrar información detallada sobre cómo están implementadas estas funcionalidades.

4.2.2 Detección y lectura del código QR

Para el control de la cámara Web, se ha usado el repositorio de Sarxos [20] disponible en Github, una de las comunidades Open-Source más importantes, que permite el almacenamiento y uso de millones de repositorios guardados en ella [21]. Otra de las opciones disponibles para el control de la cámara Web desde Java era usar JMyron, una librería que contiene clases y métodos para el control completo de la cámara, permitiendo detección de movimiento y patrones entre otras [22]. Sin embargo, este proyecto usa el repositorio de Sarxos ya que, aparte de ser más sencillo de entender, contiene varios ejemplos que han sido de utilidad para el desarrollo de la aplicación deseada. Este repositorio permite al usuario acceder a cámaras conectadas a dispositivos directamente desde código Java, que es lo que buscamos en este proyecto, permitiendo que podamos tomar imágenes de nuestra cámara para analizarlas posteriomente.

Para el análisis de estas imágenes en busca de códigos QR, se ha usado la librería ZXing (Zebra Crossing), una librería Open-Source implementada en Java para su uso en Android, cuyo principal desarrollador es Sean Owen, y que permite el procesado de códigos de barras en varios formatos, entre ellos el código QR [23]. Es una de las librerías más usadas en el desarrollo de aplicaciones que usen códigos QR, como se puede comprobar si se busca información por la red.

Al llamar a la aplicación, se abre una ventana que mostrará la imagen vista desde la cámara, de manera que el usuario pueda comprobar si el código que muestra a la cámara se ve correctamente, así como una ventana de texto que dará información al usuario. Se llamará entonces al método para detectar el código QR, que toma una imagen de la cámara mediante el uso del repositorio de Sarxos, comprueba si contiene un QR e intenta descifrarlo, usando la librería ZXING. Tiene un número máximo establecido de 100 imágenes tomadas de manera que, si alguien llama a la aplicación cliente

Diseño de la aplicación

48

48

y no muestra un código QR por pantalla en un tiempo, la aplicación termine y el dispositivo donde esté funcionando no consuma energía.

Si en una de estas 100 iteraciones se detecta y descifra la información contenida en el código QR, que será una cadena que contenga los datos guardados en la Base de Datos sobre ese código concreto, se iniciará la comunicación con el servidor para enviarle esta cadena y que el servidor permita o no el acceso al domicilio. Se podría pensar que es más seguro el envío de la imagen en sí al servidor, ya que al enviar la cadena cualquiera podría leerla y ver el contenido. Sin embargo, la comunicación entre el cliente y el servidor irá encriptada mediante una comunicación SSL/TLS, de manera que un atacante no podría leer la información contenida en el código QR, ni si la respuesta es afirmativa o negativa. Además, el envío de la cadena es mucho más eficiente, ya que mientras que una imagen ocupa del orden de MB, una cadena difícilmente llegará a los KB.

Tras la recepción de la respuesta enviada por el servidor, se mostrará por pantalla un mensaje que diga abriendo puerta o QR inválido para decir si el código QR leído es correcto o no, a lo que, en una implementación futura, se añadiría la emisión de una señal de apertura de puerta a través de alguno de los puertos del ordenador.

4.3 Servidor

En la parte servidor distinguimos el servidor web, el servidor de base de datos, el servidor de aplicación y la autoridad de certificación. Los servidores interactúan entre sí mediante el uso de sockets, y a nivel de programación, se emplean una serie de conectores que transforman un lenguaje de programación en otro, permitiendo que el desarrollador use el lenguaje Java para todo, conseguiendo la funcionalidad completa del sistema.

4.3.1 Servidor Web

El servidor web almacena la página web para permitir el acceso de usuarios a ella. La conexión se realiza mediante el protocolo HTTPS, usando un certificado digital autofirmado por el servidor Web, debido a que desde el punto de vista didáctico resulta de más provecho, además de porque para usar un certificado firmado por una Autoridad de Certificación reconocida hay que pagar. En caso de que este proyecto se usara para un futuro producto, se procedería a solicitar la firma del certificado a una Autoridad de Certificación reconocida, para generar más confianza en los usuarios los cuales, al acceder a una web cuyo certificado no está firmado por una autoridad reconocida, recibirán un aviso de que el certificado digital no es confiable, además de que esto puede provocar un intento de suplantación de identidad de la página web para obtener las credenciales de los usuarios.

A continuación se procede a explicar cómo se han realizado las distintas funcionalidades de la interfaz web.

4.3.1.1 Login

A la hora de hacer login, el usuario insertará en un formulario su nombre de usuario y su contraseña, que serán enviadas al servidor mediante el método POST, usando HTTPS para proteger la conexión. En el servidor, se realiza el hash de la contraseña y su respectiva sal, ya que, para evitar que ataques hacia nuestra base de datos permitan a los atacantes obtener las claves de usuario, no se guardarán las contraseñas en claro. Aunque se podría pensar que guardar el hash de la contraseña directamente evitaría la extracción de la contraseña, un atacante la puede extraer del hash por medio de ataques con Rainbow Tables, como ya ha sido explicado anteriormente. Añadiendo sal, que son una serie de bits aleatorios, a la contraseña antes de realizar el hash y guardar el valor en la base de

49 Apertura de puertas usando Códigos QR QR Door Opener

datos, podemos conseguir evitar este tipo de ataques (o al menos hacerlos más costosos, ya que habría que computar un hash no sólo por cada contraseña del diccionario, sino que por cada contraseña habría que calcular un hash para cada posible combinación de bits de sal) [24].

Si el usuario existe en la base de datos y la contraseña es correcta, se extraen los datos del usuario, se guardan en la variable de Sesión y se pasa al menú principal, donde estarán el resto de opciones.

4.3.1.2 Alta de usuario

Esta opción mostrará un formulario que permitirá a un nuevo usuario registrarse en el sistema. El formulario solicitará el nombre real del usuario, el NIF, el teléfono, el correo, el nombre de usuario (username) y la contraseña dos veces para evitar errores tipográficos y enviará estos datos al servidor usando el método POST. Tras esto, en el servidor se comprueba si el usuario existe ya en el sistema (el usuario se identifica por su NIF, pero además su teléfono y su username deben ser únicos en la base de datos) y, si no existía anteriormente, se guardan los datos en la base de datos, con la contraseña en forma de sal y hash, y se da acceso al usuario a la página principal con las distintas opciones, guardando los datos de usuario en la variable de Sesión.

4.3.1.3 Modificar datos de usuario

Esta opción mostrará al usuario un formulario con sus datos actuales y la opción de modificarlos. Aunque no podrá cambiar su NIF, ya que es su identificador único, el resto de datos podrán ser cambiados directamente, excepto la contraseña, para la que se solicitará la inserción de la contraseña anterior como forma de comprobación de que el usuario es quien dice ser, así como la nueva dos veces para evitar errores tipográficos, tras lo que se procederá a comprobar si todo es correcto y se reemplazará, obviamente en forma de hash con sal. En el caso de los datos normales, se comprobará que el nombre de usuario y el número de teléfono no existan en la base de datos antes de la modificación ya que estos datos deben ser únicos por cada usuario.

4.3.1.4 Ver casas de usuario

En esta opción, se muestran al usuario una tabla que contiene las casas a las que tiene permiso para acceder, además de permitírsele generar códigos QR de acceso, generar certificados digitales para las casas, y añadir y borrar casas.

En el caso de que se decida borrar una casa, se borrará el permiso que tiene ese usuario para acceder a ella, tras lo cual, se comprobará si el usuario era el administrador de la casa y, de ser así, se borrará la casa del sistema, incluyendo todas las relaciones del resto de usuarios con ella. Si no era el administrador, la casa permanecerá en la base de datos, borrando simplemente el permiso de acceso.

En el caso de querer añadir una nueva casa, se solicitará la dirección completa de ésta, es decir, calle, población y código postal, tras lo cual se comprobará si la casa existe en el sistema y se insertará si no es así. Si existe, se añadirá dicho usuario como secundario de la casa, aunque esto no es lo que se desearía en una futura implementación real, en la que se le mandaría un aviso al administrador de dicha casa a su correo para que sea él quien decida si el solicitante debe tener acceso a la casa o no, significando esto la generación de códigos QR de acceso cuyo creador sea él, y la posible petición de un certificado digital para dicha casa. En caso de que el usuario que inserta la casa sea el administrador, se le solicitará la confirmación de su correo electrónico, para el envío del certificado digital necesario para la comunicación entre el dispositivo de lectura de QR en la puerta del domicilio y el servidor de la aplicación. Cuando el usuario confirme su dirección de correo electrónico, se enviará un nuevo correo a la cuenta [email protected] (la cuenta de administración de certificados del sistema, creada por el desarrollador del proyecto), procediendo el administrador a la creación del certificado y a la adición de la información sobre su número de serie a la base de datos en la casa correspondiente, tras lo cual se enviará un correo al usuario adjuntando

Diseño de la aplicación

50

50

su clave pública y certificado almacenadas en un fichero PKCS#12 (usuario.p12), que permite que ambos estén guardados bajo una clave, así como el certificado de la autoridad certificadora en formato PEM (qrcodedoorepenerv2.pem), que permitirá al sistema de lectura y apertura de la puerta del domicilio verificar que el certificado enviado por el servidor de aplicación está firmado correctamente, probando su autenticidad. También se incluirán las instrucciones de instalación del sistema, para que cualquier usuario pueda realizar la conexión aun sin tener los conocimientos avanzados sobre el uso de la línea de comandos y de las aplicaciones necesarias.

En el caso de querer generar un código QR, se solicitará al usuario el tiempo de validez de dicho código, que podrá estar medido en minutos, horas y días, así como el usuario que lo va a usar, para poder guardar los datos en los registros de acceso. Una vez que el usuario envía los datos, el servidor se encarga de generar el código QR usando la librería ZXING, cuyo contenido será una cadena cuyo formato es el siguiente

"IdQR:" +idQR+ "IdUser:" +userId+ “IdUserAcceso:” +userAccessId+

"IdCasa:" +idCasa+ "FechaExpedicion:” +fechaexpedicion+

"FechaExpiracion:" + (fechaexpedicion+minutosvalidez)

Estos datos se codificarán en forma de código QR que será mostrado por pantalla al usuario, para que pueda usarlo, imprimirlo, fotografiarlo, descargarlo o lo que desee realizar con él.

En el caso de querer generar un certificado para una casa, se pasará a una pantalla de confirmación, en la que se mostrará al usuario su correo electrónico para que verifique si es ahí donde desea que se le envíe el nuevo certificado y la nueva clave privada para el domicilio, junto con las instrucciones de instalación del sistema, exactamente igual que cuando se inserta la casa por primera vez.

Para mostrar la tabla, se accede a la base de datos y se buscan todas las casas en las que el usuario tenga permiso de acceso, y se copiarán en un ArrayList de objetos CasaBean, que se guardará en la variable de sesión. Tras esto, en la página web, se presentarán dichas casas en forma de tabla, cada casa en una fila, gracias al uso de un bucle for. Para poder añadir un formulario a cada casa, de manera que se pueda recoger para cuál es para la que el usuario está solicitando el código, el certificado digital o si es la casa a borrar, cada fila será un formulario distinto. Esto impide usar las tablas definidas dentro de HTML, ya que este lenguaje no permite el uso de formularios dentro de tablas.

4.3.1.5 Ver registros

Al pulsar en esta opción, se muestran al usuario todos los registros de acceso con códigos QR generados por él. Esto permitirá al usuario tener información sobre el uso que se ha hecho de los códigos, cuándo han sido usados por los poseedores, así como si ha habido algún tipo de error, como por ejemplo, que se haya usado un QR inválido o que se usara tras estar caducado, de manera que el usuario sepa si está siendo atacado de algún modo.

Para mostrar la tabla, se accede a la base de datos y se buscan todos los registros de acceso correspondientes a un usuario, extrayendo la información necesaria, que será la dirección del domicilio, el nombre del poseedor del QR, la hora de acceso, y si ha habido algún tipo de error. Toda esta información será guardada en un ArrayList, con un objeto de tipo CasaBean por cada registro, y se pasará a la variable de Sesión, que se usará en la página web, donde se presentan los registros en forma de tabla, una fila por cada elemento del ArrayList. En este caso, al no usarse ningún tipo de formulario en la tabla, ya que sólo servirá para mostrar datos, se usará una tabla normal, que es mucho más simple de construir.

En los registros mostrados, tendremos cuatro tipos de error:

- Error desconocido, que se muestra cuando se ha producido algún tipo de error que no ha sido contemplado entre los errores de la aplicación.

51 Apertura de puertas usando Códigos QR QR Door Opener

- Código QR caducado, que se muestra cuando el código QR usado se corresponde con la casa donde se ha usado y es correcto, pero su fecha de uso ha sido superada.

- Código QR inválido, que se muestra cuando el contenido del código QR no es correcto, es decir, que es un código QR con una estructura correcta, pero que su contenido no coincide con los datos que tenemos en nuestra base de datos.

- Certificado digital inválido, que se muestra cuando el código QR se ha usado en una casa para la que no fue creado, de manera que el certificado digital enviado por la casa no coincide con el de la casa correspondiente al QR leído.

4.3.2 Servidor de Base de Datos

El servidor de base de datos será MySQL versión 5.5.31-0ubuntu0.12.04.2 y contendrá una base de datos con las tablas de usuarios, casas, permisos, códigos QR y registros, las cuales han sido explicadas anteriormente en la sección 3.2.2.

Se ha seleccionado MySQL Workbench como gestor gráfico de la base de datos, ya que permite la creación de tablas de una manera gráfica muy sencilla, evitando tener que usar directamente código MySQL, permitiendo además exportar el código necesario para obtener la misma estructura de base de datos posteriormente.

Para el acceso a los distintos registros de la base de datos, los servidores web y de aplicación usará el conector JDBC que proporciona MySQL para el tratamiento de peticiones a la base de datos a través de Java. Otra de las posibles opciones contempladas fue el uso de Hibernate, una herramienta que permite el uso de clases Java para la gestión de una base de datos MySQL. Aunque en principio esto decrementaba la dificultad para la creación y gestión de la base de datos, al realizarse todo de manera más gráfica y mediante el uso de clases Java ya implementadas, se decidió usar el conector JDBC, que simplemente conecta con la base de datos y le envía las distintas peticiones. Esto es más didáctico, ya que ha permitido al autor aprender los distintos tipos de peticiones que se pueden realizar en MySQL desde la base.

Cada vez que se quiera realizar una petición a la base de datos, hay que realizar los siguientes pasos:

- Establecimiento de la conexión a la Base de Datos, lo cual implica la inserción del usuario creador de la base de datos y su contraseña que, para mayor comodidad, se han guardado en el código de la aplicación.

- Creación de un objeto Statement, que es el que realiza la conversión de Java a MySQL. - Se ejecuta la petición a la base de datos a través del Statement. Si es de tipo SELECT, el

resultado se devolverá a un objeto ResultSet, del que se podrá extraer toda la información. - Por supuesto, una vez terminada la lectura/inserción de información, hay que cerrar los

flujos y la conexión a la base de datos.

4.3.3 Servidor de aplicación

El servidor de aplicación se encarga de la comprobación de los códigos QR procedentes de los usuarios. Cuando llega una petición al socket receptor, que estará escuchando en el puerto 2798 del servidor, se crea un nuevo hilo que se encarga de recibir la cadena leída por la cámara módulo que se encuentra en la casa del cliente, extrae los campos de dicha cadena y comprueba si dicho código QR existe en la base de datos y si cumple el tiempo de validez. Si todo es correcto, envía un asentimiento al cliente para que abra la puerta y si no, envía una denegación de acceso. Tras el envío de cualquiera de estos mensajes, guarda en la base de datos el registro de acceso, para que el usuario generador pueda comprobar el uso hecho con los QR generados por él. Al insertar un regitro en la base de datos, uno de los campos indica si ha habido algún tipo de error con el código QR leído. Los posibles errores son:

- No hay error (error 0). - Error desconocido (error 1), que se muestra cuando se ha producido algún tipo de error que

Diseño de la aplicación

52

52

no ha sido contemplado entre los errores de la aplicación. - Código QR caducado (error 2), que se muestra cuando el código QR usado se corresponde

con la casa donde se ha usado y es correcto, pero su fecha de uso ha sido superada. - Código QR inválido (error 3), que se muestra cuando el contenido del código QR no es

correcto, es decir, que es un código QR con una estructura correcta, pero que su contenido no coincide con los datos que tenemos en nuestra base de datos.

- Certificado digital inválido (error 4), que se muestra cuando el código QR se ha usado en una casa para la que no fue creado, de manera que el certificado digital enviado por la casa no coincide con el de la casa correspondiente al QR leído.

El establecimiento de la conexión se realiza mediante sockets SSL, ya que queremos garantizar la autenticidad, confidencialidad e integridad de los datos enviados. Cada parte de la conexión tendrá un certificado digital firmado por la Autoridad de Certificación del sistema, de manera que cuando la parte usuario establezca la conexión, solicitará al servidor su certificado digital, comprobará que es correcto y, de ser así, enviará el suyo al servidor para que este lo compruebe. Si ambos certificados son correctos, se establece la conexión segura entre el cliente y el servidor, tras lo cual, el módulo de lectura enviará la cadena obtenida del código QR al servidor, que extrae el identificador de la casa y la busca en la base de datos, para obtener el número de serie del certificado digital y comprobar que coinciden, de manera que un código QR sólo pueda ser usado en la casa para la que ha sido creado.

4.3.4 Autoridad de Certificación

Una autoridad de certificación es una entidad confiable que se encarga de emitir y revocar certificados digitales. En este proyecto, aunque se usará un certificado autofirmado en el servidor Web, se tendrá una autoridad de certificación propia que será la encargada de emitir certificados de usuario y de servidor, que serán los que permitan la comunicación segura entre el módulo de lectura y apertura de la puerta y el servidor de aplicación, garantizando además de confidencialidad, la autenticación mediante certificados.

Se usará una interfaz gráfica llamada TinyCA en lugar del uso de OpenSSL y Keytool, ya que es mucho más cómodo a la hora de crear los certificados y el resultado es el mismo (de hecho, TinyCA trabaja con OpenSSL y Keytool en segundo plano).

Los pasos seguidos para la creación de la PKI son:

- Se crean el par de claves y el certificado de la Autoridad de Certificación. - Se crean el par de claves y el certificado del Servidor de Aplicación, este último firmado por

la Autoridad de Certificación. - Cuando un usuario solicita un certificado digital para su casa, se enviará un correo a la

dirección [email protected] con el siguiente contenido

El usuario USUARIO con NIF NIF_USUARIO desea solicitar un

certificado digital para su domicilio DIRECCION POBLACION

CODIGOPOSTAL. Por favor, genérela y envíe un mail con el

certificado a la dirección CORREO_USUARIO.

- El administrador del sistema creará el par de claves del usuario, generará un certificado firmado por la Autoridad de Certificación, y los almacenará en un fichero en formato PKCS#12 que está protegido con una contraseña que el usuario no conocerá de manera que sólo el programa pueda desencriptar el fichero. Tras esto, deberá añadir a la base de datos el número de serie del certificado en la casa asociada, de manera que quede identificado el certificado digital que se ha creado para dicha casa. Posteriormente, enviará al usuario el fichero con extensión p12, que contiene la clave privada y el certificado de usuario, el certificado de la Autoridad de Certificación, que le permitirá comprobar que la firma es

53 Apertura de puertas usando Códigos QR QR Door Opener

correcta, y las instrucciones de instalación que permitirán al programa hacer uso de dichos certificados. Las instrucciones de instalación son: Descargar el certificado de la autoridad de certificación y la clave privada y certificado

del usuario, que vendrá en el fichero p12. Abrir una terminal (línea de comandos). Ir al directorio /home: cd /home Crear directorio userkeystore: sudo mkdir userkeystore Copiar al directorio userkeystore los dos archivos descargados:

sudo cp PATH_FICHERO_DESCARGADO /home/userkeystore/

Crear usertruststore: sudo keytool -importcert -trustcacerts -file QRdoorOpener2-

cacert.pem -alias CAcert -keystore usertruststore.jks

Si no está instalada la aplicación keytool, hacer lo siguiente: o Ejecutar el Ubuntu Software Center o Instalar Synaptic Package Manager o Ejecutar el Update Manager, hacer click en Check e instalar todas las

actualizaciones necesarias (puede llevar hasta horas en función de las actualizaciones necesarias).

o Ejecutar Synaptic Package Manager o Instalar OpenJDK Java Runtime

Crear el usertruststore Usar como contraseña del truststore 1234567890 Decir que sí queremos confiar en el certificado Ejecutar programa, pasando como parámetros el fichero p12 que contiene el certificado

y la clave de usuario y la IP del servidor: java -jar clienteQR.jar Cert+Key.p12 IPservidor

- Si todo ha sido instalado correctamente, se debería producir la comunicación entre el cliente y el servidor tras leer y desencriptar el código QR en el módulo de lectura de la vivienda del cliente.

4.4 Comunicación Cliente-Servidor

Como se ha explicado en los puntos anteriores, la comunicación entre el cliente y el servidor puede establecerse de dos formas: a través de la página web o a través del servidor de aplicación QR. En el primer caso, la comunicación es segura usando HTTPS, en la que sólo el servidor posee certificado digital, mientras que en el segundo, la aplicación se comunica mediante sockets SSL para establecer el túnel seguro, teniendo cada extremo de la comunicación su propio certificado digital.

4.4.1 Comunicación Web Segura (HTTPS)

HTTPS no es realmente un protocolo en sí, sino que es el uso del protocolo HTTP sobre SSL/TLS, es decir, aprovecha la seguridad establecida en el nivel de transporte mediante el uso de sockets seguros para obtener la seguridad a nivel de aplicación.

En este proyecto se va a usar un certificado digital autofirmado creado por el administrador del sistema, de manera que se crean certificados de las dos maneras posibles, certificados propios autofirmados en el servidor web y certificados firmados por una Autoridad de Certificación en la parte de comunicación segura de la aplicación, lo cual puede ser más didáctico que usar simplemente certificados firmados por una Autoridad de Certificación y, además, evita el tener que enviar al usuario el certificado digital de la página web para poder acceder al servicio, ya que la Autoridad de Certificación usada para firmar los certificados será propia del sistema y éstos no

Diseño de la aplicación

54

54

serán confiables para el navegador del usuario.

Para la generación del certificado autofirmado, se ha usado OpenSSL, seleccionando el algoritmo RSA para la generación de claves, un algoritmo que basa su fuerza contra ruptura en que la descomposición de números grandes en factores primos es muy compleja y llevaría demasiado tiempo [25].

También es necesario modificar la configuración del servidor Apache, para que redirija las conexiones que llegan al puerto 443 (puerto de HTTPS) a Tomcat, e indicar el path donde se encuentra el certificado digital a usar por el servidor.

Con todo esto, cuando el usuario intente acceder a la página web, recibirá un aviso diciendo que el certificado enviado por el servidor no es confiable, el usuario deberá aceptar el certificado y accederá así a la Web del servicio de manera segura.

4.4.2 Comunicación con la aplicación segura (Aplicación QR sobre SSL)

En el caso de la comunicación entre cliente y servidor de aplicación, ambos lados usarán un certificado digital para autenticarse, que será creado y firmado por la Autoridad de Certificación del sistema, de manera que ambas partes puedan comprobar que el certificado es correcto. Para la gestión de la Autoridad de Certificación se usa la aplicación TinyCA, que permite realizar todos los pasos de forma gráfica, lo cual es mucho más sencillo.

Entre las demás opciones contempladas para la creación y gestión de la Autoridad de Certificación, tenemos las siguientes:

- Uso de OpenSSL directamente para la creación de certificados, su firma usando una clave privada que sería la de la autoridad de certificación, la revocación de certificados, etc. Fue descartada porque es mucho más cómodo el uso de un entorno gráfico que haga automáticamente todos los pasos que tener que hacerlos a mano mediante un terminal.

- Otra forma bastante atractiva es el uso del API Bouncy Castle, que permite usar código Java para la gestión de una Autoridad de Certificación, la creación de certificados y claves y todo lo necesario en relación con criptografía de clave pública. Sin embargo, su uso era más complejo que el uso de TinyCA y el Keytool de Java.

Las claves y el certificado digital del servidor de aplicación son creados desde el principio, de manera que el servidor tiene su certificado firmado antes de la creación de ningún certificado de usuario, y por tanto, de la comunicación con ninguno de ellos.

Cuando un usuario quiere que su equipo funcione como cliente para el servicio, debe enviar una solicitud de creación de certificado digital a través de la web, tras lo cual, el administrador del servicio recibirá un correo con la petición del certificado, generará las claves y el certificado digital de cliente usando TinyCA, insertará el número de serie del certificado en la base de datos y enviará clave privada, certificado digital de usuario y certificado digital de la Autoridad de Certificación al usuario, junto con las instrucciones de instalación, que pueden encontrarse en la sección 4.3.4 Autoridad de Certificación.

Si el usuario ha seguido las instrucciones correctamente, al ejecutar el programa y leer un código QR se establecerá la conexión con el servidor mediante un socket SSL, de manera que servidor y cliente intercambian sus certificados, comprueban que está firmado por la Autoridad de Certificación del sistema, y la conexión es segura de aquí en adelante.

55 Apertura de puertas usando Códigos QR QR Door Opener

5 POSIBLES ATAQUES

n este capítulo se tratarán de explicar los tipos de ataque que puede sufrir el sistema, así como las distintas formas en las que se podrían tratar de evitar o cómo se han evitado en la implementación escogida.

5.1 DDoS

Un ataque de Denegación de Servicio Distribuido (Distributed Denial of Service) consiste en hacer que múltiples ordenadores, normalmente infectados para que actúen como Bots (ordenadores esclavo que hacen lo que un maestro solicita) ataquen al mismo tiempo un servidor, de manera que éste quede colapsado y no pueda responder a las peticiones de los usuarios [26].

Un ejemplo sería el caso en que un atacante que controla una serie de ordenadores ordene un ataque distribuido de tipo SYN Flooding, consistente en enviar multitud paquetes TCP de sincronización (SYN packets) al servidor, lo cual provoca que el servidor reserve capacidad para cada nueva conexión abierta, dejando el TCP handshake a medias. Como se puede observar de manera gráfica

E

“Los que dicen que es imposible no deberían molestar a los que lo están haciendo”

- Albert Einstein -

Figura 13. Ataque por Inundación de SYN (SYN Flooding Attack, Copyright Kungliga Tekniska Högskolan EP2500, Module 3 2013)

Posibles Ataques

56

56

en la Figura 13, si este procedimiento se hace con muchos equipos de manera simultánea, el servidor se quedará sin recursos y no podrá aceptar peticiones de usuarios legítimos [26].

En este proyecto, este tipo de ataque se podría sufrir y no hay implementado ningún tipo de defensa contra él. Para proteger el sistema contra un DDoS, se podrían seguir las recomendaciones de “Webmaster Required: defensive DDOS attack Ultimate Guide”, que conllevan en su mayoría una mejora de los equipos usados en cuanto a rendimiento, ancho de banda, uso de páginas web estáticas (lo cual es imposible en el caso de este proyecto) o instalar firewalls anti-DDoS [27]. Como se puede observar ninguna de ellas se va a implementar en este proyecto, ya que supera lo pretendido con él, y no se podrá combatir este ataque con los recursos disponibles.

5.2 SQL Injection

Este tipo de ataques se basa en lograr la inserción de una petición SQL a través de un formulario que se muestra al cliente, consiguiendo que, al procesar la petición, se puedan modificar o extraer datos de la base de datos si no se verifican las entradas insertadas.

Un ejemplo ilustrativo es el siguiente. Supongamos que en el código hay una petición a la base de datos en la que se solicita extraer los datos de un usuario que ha sido enviado a través de un formulario.

statement = "SELECT * FROM users WHERE name = '" + userName + "';"

El atacante, al insertar el nombre de usuario en el formulario, añade una nueva petición a la base de datos que le permita extraer más datos, tales como la contraseña del usuario o lo que él decida.

En el ejemplo anterior, si el atacante inserta como nombre de usuario la siguiente cadena

a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't

La petición enviada a la base de datos sería la siguiente

SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM

userinfo WHERE 't' = 't';

Lo que conllevaría a eliminar la tabla users y extraer toda la información de la tabla userinfo.

Para evitar que este tipo de comportamientos se lleven a cabo, se pueden tomar algunas medidas, recomendadas por el proyecto OWASP [28]:

- Usar un API seguro, que evite usar el intérprete completamente y que dé una entrada parametrizada, de manera que es el API usado para hacer las modificaciones en la base de datos el que se encarga de controlar que no haya peticiones peligrosas. En el caso del API usado en este proyecto, el JDBC, permite el uso de peticiones seguras, conocidas como Prepared Statements, que ayudan a evitar SQL Injection [29]. El modo en el que las Prepared Statements evitan SQL Injection se debe a que cuando se va a ejecutar una nueva consulta en la Base de Datos, no se hace directamente como con los Statements normales, lo que permitiría hacer el ataque de la manera explicada anteriormente, sino que primero ejecuta la parte del Statement que se encarga de abrir las tablas requeridas y luego busca o añade los valores. Esto se puede ver con el siguiente ejemplo, basado en el usado anteriormente.

57 Apertura de puertas usando Códigos QR QR Door Opener

PreparedStatement statement =

connection.prepareStatement("SELECT * FROM users WHERE name=?”);

statement.setString(1, username)

statement.executeQuery();

Este ejemplo lo que hace es crear en primer lugar un Prepared Statement que carga en la Base de Datos la petición dejando la variable name sin valor. Posteriormente, se le da valor a la variable y se ejecuta, pero el valor de la variable será el contenido completo de la cadena que haya contenida en username, de manera que aunque se intenten poner varias peticiones consecutivas como en el ejemplo explicado anteriormente, no será posible ya que todas serán consideradas una sola cadena.

- Otra opción es “escapar” caracteres especiales. Esto significa que si un atacante inserta por ejemplo una comilla en el nombre de usuario para intentar insertar una petición, este carácter no se tomará como el fin de una petición, sino como parte del nombre, que es lo que realmente espera el servidor. Es posible de implementar en este proyecto, pero llevaría demasiado tiempo tener en cuenta todos los posibles caracteres, y usando Prepared Statements es suficiente seguridad para lo que buscamos en un Proyecto Final de Carrera.

- Usar validación de los parámetros insertados mediante filtrado y White-List (Listas Blancas). Filtrado significa eliminar cualquier carácter que pueda causarte problemas, por ejemplo, si esperas un carácter alfanumérico, eliminar el resto de caracteres de la entrada. Whitelisting consiste en comprobar si los datos enviados por el usuario corresponden a los que deberían ser, comprobando el tipo de caracteres, la longitud de las cadenas suministradas, etc. Al igual que el caso anterior, se podría implementar pero conllevaría demasiado tiempo y los Prepared Statements proporcionados por JDBC son suficiente.

5.3 Pérdida de Autenticación y Gestión de Sesiones

Este tipo de ataque consiste en intentar extraer las contraseñas de los usuarios o en usar la sesión de otro usuario para suplantar su identidad. Se procede ahora a exponer los posibles defectos del sistema según OWASP y a comprobar si esos defectos podrían ser usados para atacar nuestro sistema.

- Extracción de contraseñas de la base de datos, que incluso siendo guardadas usando un hash, pueden ser obtenidas mediante el uso de Rainbow Tables. En este proyecto, este problema no sería tal debido a que las contraseñas están guardadas, además de usando una función hash, usando sal, de manera que aunque un atacante extrajera la tabla de datos de usuario con la contraseña, debería computar un hash por cada posible valor de la sal, que en nuestro caso tiene 24 Bytes, lo cual implica que un atacante debería computar

por cada posible cadena insertada como contraseña, lo cual implica un gran consumo de recursos por parte del atacante para descubrir una única contraseña.

- Uso del identificador de sesión de un usuario real de manera fraudulenta, lo cual puede conseguirse si el identificador de sesión está en la URL u obteniendo la cookie del usuario para usarla como identificador. En este proyecto, ninguno de dichos métodos podrá ser usado, ya que la sesión se guarda en el servidor y se cierra cuando el usuario hace el logout, evitando así que ningún atacante pueda extraer información atacando al usuario.

- Aprovechamiento de la falta de cierre de sesión al pasar un tiempo de inactividad, también conocido como timeout. Por ejemplo se podría dar el caso de que un usuario acceda al servicio en un ordenador público y olvide cerrar la sesión mediante el logout. Pasado un

Posibles Ataques

58

58

tiempo, otro usuario accede a la web del servicio en ese ordenador y se encuentra que hay una sesión abierta, pudiendo modificar datos de usuario, eliminando casas, creando códigos de acceso para ellas, toda la información estaría disponible. En este proyecto, este ataque se evita añadiendo un timeout al servidor, de manera que si el usuario pasa un tiempo sin realizar actividad, la sesión se cierra automáticamente.

5.4 Fallos en la configuración de seguridad

Este tipo de ataques consiste en usar fallos en la configuración de la seguridad del sistema para intentar obtener acceso a información restringida, ficheros cuyo acceso debería ser permitido sólo a ciertos usuarios, u otras funcionalidades que podrían poner en peligro el sistema completo. Según OWASP, esto se puede conseguir de las siguientes maneras:

- Si el software no está correctamente actualizado, refiriéndonos a Sistema Operativo, Servidores, Base de Datos, librerías usadas, etc. En el caso de este proyecto, al haber sido instalados todos los componentes mediante el Gestor de Paquetes Synaptic con todas las actualizaciones realizadas antes, supondremos que todo el software está actualizado a la última versión disponible.

- Si hay usuarios y contraseñas por defecto que no han sido borradas de nuestro servidor, un atacante podría acceder a él usando estas credenciales y obtener toda la información que necesito, o simplemente eliminar archivos del sistema o inutilizarlo. En el caso de este proyecto no hemos creado ningún usuario por defecto para nuestra aplicación, sin embargo sí que hay posibilidad de que el servidor Apache o Tomcat tengan usuarios por defecto que no hayan sido eliminados, por lo que se ha procedido a modificar los usuarios que tienen acceso a las funcionalidades de administrador para evitar que sean los que vienen instalados por defecto

- Si el tratamiento de errores muestra una traza de error al usuario, lo que podría llevar al atacante a ver la estructura de ficheros de la aplicación e intentar acceder a ellos, copiando por ejemplo las clases usadas, extrayendo el código Java de ellas usando alguna herramienta y viendo las posibles vulnerabilidades que pueda tener el código para explotarlas. En este proyecto, este tipo de vulnerabilidad hay que tenerla en cuenta, ya que ahora mismo, al producirse un error, se muestra una traza de error que dice la línea de código que ha fallado, el motivo y la estructura de ficheros, lo cual hay que evitar.

5.5 Replay Attack

Un ataque de repetición consiste en el hecho de que un atacante guarda la información enviada por el usuario al servidor y, tras un tiempo, vuelve a enviarla esperando tener acceso con las credenciales del usuario. La mejor forma de entender este ataque en mediante un ejemplo gráfico, que puede observarse en la Figura 14, en la que tenemos a Alice, el usuario real, que accede al servidor que está en Bob usando su contraseña. Mientras esto sucede, el atacante, Trudy, está grabando todos los mensajes intercambiados, almacenándolos para su posterior uso. Pasado un tiempo, Trudy coge los paquetes almacenados de la comunicación entre Alice y Bob, y los reenvía a Bob, de manera que éste cree que quien está intentando acceder es Alice y permite el acceso a Trudy.

Este ataque se podría realizar incluso si la conexión estuviera encriptada, ya que aunque Trudy fuera incapaz de entender los mensajes, sí sería capaz de suponer que Alice está accediendo a una web que requiere inicio de sesión, copiar los mensajes, y reenviarlos aunque no comprenda su contenido.

59 Apertura de puertas usando Códigos QR QR Door Opener

En el caso de este proyecto, la protección contra este tipo de ataque viene dada mediante el uso de SSL/TLS. Gracias a este protocolo de seguridad, basado en certificados digitales, la conexión queda encriptada, y además SSL/TLS implementa una forma de evitar Replay Attacks, consistente en el uso de un Message Authentication Code (MAC), que cambia en cada paquete enviado, y que se computa a partir de la clave de integridad de sesión (que se obtiene a partir de una dupla de valores aleatorios generados por cliente y servidor), el número de secuencia del paquete, y el contenido del paquete [30] . Esto hace que, por mucho que Trudy intente reenviar los paquetes enviados por Alice anteriormente, el MAC sea erróneo y Bob detecte el intento de suplantación de identidad.

Mientras esto sucede,

5.6 Man-In-The-Middle (MITM) Attack

Un ataque de Hombre en el Medio, conocido popularmente por su nombre en inglés Man-In-The-Middle, consiste en un atacante que se sitúa entre los dos lados de la comunicación, haciendo que cada extremo crea que él es el otro extremo. Para entenderlo mejor, conviene explicarlo usando la

Figura 15, en la que los dos extremos de la comunicación son Alice y Bob, mientras que el atacante será Trudy, que para hacer creer a Bob que es Alice, encripta el número aleatorio R con su clave privada y envía la clave pública asociada para que Bob pueda comprobar que el encriptado era correcto. Las mismas peticiones enviadas por Bob a Trudy, serán enviadas por Trudy a Alice, para cumplir con el protocolo de comunicación establecido y hacer creer a Alice que está hablando con Bob. Tras esta autenticación, Trudy puede desencriptar cada paquete que ambos lados envíen, ya que tanto Alice como Bob encriptarán los paquetes con la clave pública de Trudy, de manera que sólo tiene que desencriptar, ver lo que hay y modificarlo si así lo desea, encriptarlo otra vez usando la clave pública del otro extremo y enviarlo.

En el caso de este proyecto, en lugar de enviar directamente las claves públicas, Alice, Bob y Trudy

Figura 14. Ataque de Repetición (Replay Attack,Copyright Kungliga Tekniska Högskolan, IK2206 Lecture 7, 2012)

Posibles Ataques

60

60

tienen cada uno su propio certificado digital firmado por la autoridad de certificación del sistema, de manera que, para que el certificado de Trudy fuera aceptado como modo de autenticación, Trudy debería estar dada de alta en el sistema y pedir su certificado a la Autoridad de Certificación, lo cual es la primera complicación que tendría Trudy para llevar a cabo el ataque. Además, cuando un usuario solicita un certificado digital, el tipo de certificado creado es un certificado de tipo Cliente, por lo que no podría hacer creer a Alice que ese certificado es del servidor Bob. Más dificultades aún, cuando estamos hablando de la comunicación entre el módulo de detección y lectura del código QR en la casa y el servidor de aplicación, además de comprobar que la firma del certificado digital es la de la Autoridad de Certificación de nuestro sistema, el servidor comprueba que el código QR recibido fue creado para la casa cuyo certificado digital se ha recibido. De esta manera, aun consiguiendo el atacante ponerse como MITM, al reenviar los datos del QR al servidor junto con su certificado se produciría un fallo de autenticación, ya que no coincidiría el número de serie del certificado con el guardado en la base de datos para dicho código QR, y en caso de que Trudy modificase la cadena, la información contenida en dicho código QR no corresponderá con la guardada en la base de datos.

5.7 Seguridad interna del sistema

En este caso, lo que se trataría de impedir sería que los propios desarrolladores del sistema/empleado de la empresa de gestión puedan acceder a los datos de la base de datos. Como se ha explicado anteriormente, las contraseñas de los usuarios almacenadas en la base de datos no es accesible para las personas que tengan acceso a ellas, ya sean atacantes internos o externos, al estar almacenado el hash con sal de la contraseña, siendo la función que genera el hash irreversible. Así, será imposible autenticarse en el sistema como un usuario y generar nuevos códigos QR en su

Figura 15. Ataque Man in the Middle (Copyright Kungliga Tekniska Högskolan, IK2206 Lecture 7, 2012)

61 Apertura de puertas usando Códigos QR QR Door Opener

nombre.

Sin embargo, los datos almacenados en la base de datos sobre los códigos QR ya generados por los usuarios están en claro, es decir, alguien con acceso a la base de datos podría extraer de ella los datos necesarios para crear su propio código QR y acceder con él al domicilio del usuario. Esto supone un grave problema de seguridad, aunque el atacante que vaya a generar el código QR a partir de los datos obtenidos deberá saber el formato exacto de la cadena con la que se genera el código QR para poder crearlo. Aun así, no se va a implementar ningún sistema contra este tipo de ataques en este proyecto, confiando en que los empleados de la empresa (en este caso el creador del sistema) no van a usar las información disponible en la base de datos para su propio beneficio.

Posibles Ataques

62

62

63 Apertura de puertas usando Códigos QR QR Door Opener

6 EJEMPLO PASO A PASO

n este capítulo se procede a mostrar un ejemplo completo de cómo funciona la aplicación, lo que permitirá al lector obtener una visión más detallada de las distintas interfaces del sistema y de cómo se llegaría desde la creación de un nuevo usuario para la aplicación hasta la apertura de una puerta de un domicilio mediante el uso de un código QR.

6.1 Acceso a página web y creación de un nuevo usuario

En primer lugar se comienza mostrando el proceso de acceso a la página web y la creación de un nuevo usuario para el servicio.

Al acceder a la página web, mediante HTTPS, se muestra un aviso de certificado desconocido, ya que no ha sido firmado por ninguna entidad reconocida, por lo que habrá que aceptar dicho certificado, como se puede observar en la Figura 16, en la que se han querido además mostrar los

E

“No hay distancia que no se pueda recorrer ni meta que no se pueda alcanzar”.

- Napoleón Bonaparte -

Figura 16. Petición de Confirmación de Certificado

Ejemplo Paso a Paso

64

64

datos incluidos en el certificado digital, donde se puede comprobar que es autofirmado (Issued To e Issued By coinciden), la fecha de validez (hasta final de Octubre de 2013) y las huellas digitales por si el usuario desea comprobar que es correcta poniéndose en contacto con el creador.

Tras aceptar el certificado, se pasará a la página principal o index del sitio Web, que tendrá una estructura como la mostrada en la Figura 17, en la que se permitirá al usuario elegir si desea hacer login mediante un usuario y una contraseña creada anteriormente o si se desea proceder a crear un nuevo usuario. Como el usuario que va a acceder es nuevo en este ejemplo, se pulsa sobre la opción Nuevo Usuario, que le llevará a una página como la de la Figura 18, en la que se le solicitan una serie

de datos personales, entre los que destacan Nombre, NIF, correo electrónico y nombre de usuario y contraseña para el acceso al servicio, esta por partida doble.

Figura 17. Página principal del sitio Web

Figura 18. Creación de nuevo usuario

65 Apertura de puertas usando Códigos QR QR Door Opener

El usuario de prueba creado se llamará Universidad de Sevilla, cuyo NIF será 12345678S, su correo, que debe ser real para poder recibir los certificados digitales será el del autor proporcionado por la Universidad de Sevilla, y el nombre de usuario y contraseña serán: prueba, prueba. Tras pulsar sobre el botón Dar de Alta, se pasará al menú principal del sitio web, en que se mostrarán todas las opciones.

6.2 Menú principal, inserción de nueva casa y solicitud de Certificado

Tras el acceso del nuevo usuario, ya sea a través de la página de login o directamente tras la creación de un nuevo usuario, se mostrará el menú principal del sitio web, que puede observarse en la Figura 19, y que permite al usuario elegir si desea modificar los datos de usuario, si desea ver las casas que tiene disponibles, o si prefiere ver los registros de acceso. En este ejemplo se procederá a insertar una nueva casa y a solicitar un certificado digital para ella, por lo que se pulsará en la opción Ver Casas y posteriormente en la opción Generar Certificado.

Figura 20. Opciones de Usuario

Figura 19. Inserción de Nueva Casa

Ejemplo Paso a Paso

66

66

Tras pulsar en Ver Casas, se pasará al menú de la Figura 20, que contendrá un formulario vacío, al

ser un usuario nuevo, para solicitar al usuario la inserción de nuevas casas en el sistema. Se procede a insertar una nueva casa, que en este ejemplo será la Escuela Técnica Superior de Ingeniería de la Universidad de Sevilla. Tras la inserción de la nueva casa, como es el primer usuario que la inserta y por tanto su administrador, se pasa a una pantalla en la que se solicita la confirmación de la dirección de correo electrónico, como puede verse en la Figura 21, para el envío del certificado digital que permitirá la comunicación segura entre el dispositivo de lectura del código QR y apertura de la puerta y el servidor de la aplicación.

El proceso completo de creación del certificado, su envío al usuario y la instalación, puede seguirse en el apartado siguiente, Creación de certificado digital y envío al cliente. Tras esto, se mostrarán distintas opciones que permitirán al usuario la creación de nuevos códigos QR para el acceso, la solicitud de un nuevo certificado digital para el domicilio, y la eliminación de dicha casa, como se muestra en la Figura 22. Una de las opciones es la Solicitud de un nuevo Certificado Digital, que

Figura 22. Confirmación del correo electrónico para el envío del certificado digital

Figura 21. Casa insertada

67 Apertura de puertas usando Códigos QR QR Door Opener

permita al servidor la autenticación del módulo de lectura del domicilio, y que es lo que se ha pedido a la hora de insertar la nueva casa, y que además supone los mismos pasos que si se pulsara la opción Crear Certificado, opción que nos llevará a la página mostrada en la Figura 21, que simplemente servirá para que el usuario verifique la dirección de correo electrónico en la que va a recibir dicho certificado, tras lo cual el usuario debe pulsar en confirmar, lo que enviará un correo al administrador del servicio de Certificados del sistema para que proceda a la creación del Certificado solicitado.

6.3 Creación de Certificado Digital y envío al cliente

Tras la confirmación de su dirección de correo por parte del cliente, el sistema envía un correo al administrador de la Autoridad de Certificación del sistema que, como puede comprobarse en la Figura 23, contiene los datos necesarios para reconocer al usuario y la casa en la base de datos, así como la dirección de correo electrónico donde se debe enviar el certificado junto con las instrucciones una ver generado.

Tras la recepción de este correo, el administrador procederá a la creación del par de claves para el domicilio del usuario, la firma de la clave pública usando la clave privada de la Autoridad de certificación, lo que llevará a la generación del certificado digital, la adición del número de serie del certificado digital al registro del domicilio en la Base de Datos del sistema para poder reconocerlo cuando el módulo de lectura de códigos QR lo reciba, y finalmente el envío al usuario del certificado digital del cliente y su clave privada, así como el certificado digital de la Autoridad de Certificación para que pueda comprobar que la firma es correcta. Todos estos pasos se verán a continuación siguiendo el ejemplo paso a paso con imágenes.

Figura 23. Correo Recibido por el Administrador de la Autoridad de Certificación

Ejemplo Paso a Paso

68

68

Para empezar, el administrador usará la aplicación TinyCA para la generación de las claves y certificados de los usuarios. En las Figuras 24 y 25 se pueden observar los certificados de la

Autoridad de Certificación, llamada QRdoorOpener2, y el del usuario con NIF 12345678S, respectivamente, pudiéndose comprobar en ellas los números de serie, la validez, los algoritmos usados para la generación de claves y para la firma del certificado, etc.

Figura 25. TinyCA, Certificado Generado para el usuario 12345678S

Figura 24. Certificado Digital de la Autoridad de Certificación

69 Apertura de puertas usando Códigos QR QR Door Opener

Tras la creación de las claves y la firma de la clave pública para la creación del certificado usando TinyCA, el administrador debe abrir el gestor de la Base de Datos, en este caso MySQL Workbench, para insertar el número de serie del certificado en el registro de la casa para la que ha sido creado dicho certificado, que en este caso es 0B. Como puede observarse en la Figura 26, el administrador debe acceder al registro de la casa, cuya dirección completa viene en el correo que recibe tras la petición por parte del usuario, y añadir el número de serie en el campo idCertificado.

Posteriormente, el administrador procederá a exportar el certificado del usuario y el certificado de la Autoridad de Certificación, para poder enviarlos al usuario y que este lo instale en su módulo de lectura de códigos QR. Como puede observarse en la Figura 27, se generan un fichero con extensión p12 y otro con extensión pem. Esto se debe a que el fichero p12 contiene la clave privada y el certificado digital del usuario, encriptados con una clave que sólo el administrador del sistema conoce, de manera que este certificado sólo pueda ser usado por el módulo de lectura del código QR que tendrá esta contraseña preinstalada, mientras que el certificado de la Autoridad de Certificación no necesita ser guardado bajo clave y será usado por el módulo de lectura para comprobar que la firma del certificado enviado por el servidor de la aplicación sea correcta. Los ficheros generados en este ejemplo son los seleccionados en la Figura 27.

Finalmente, el administrador del sistema enviará al usuario estos ficheros generados al correo que éste confirmó a la hora de solicitar el nuevo certificado digital, como puede observarse en la Figura 28. Además de estos ficheros, se adjuntarán las instrucciones de instalación de los certificados, para que el usuario los coloque en el lugar correcto y pueda usar el programa para abrir la puerta del domicilio para el que el certificado fue generado.

Figura 25. Adición del número de serie del Certificado generado al registro de la casa

Ejemplo Paso a Paso

70

70

Figura 26. Ficheros de claves y certificados generados

Figura 27. Correo recibido por el usuario con los certificados e instrucciones

71 Apertura de puertas usando Códigos QR QR Door Opener

6.4 Creación de código QR de acceso, uso de éste para el acceso y comprobación de registros

Una vez que el usuario ha recibido el correo con los certificados digitales, la clave privada y las instrucciones, y tras seguir los pasos de éstas para colocar los ficheros en el directorio correcto, ya puede usar el servicio para abrir la puerta del domicilio. En primer lugar, debe acceder a la web para solicitar un código QR de acceso que, como se puede comprobar en la Figuras 29 y 30, requerirá al usuario que indique el tiempo de validez del código QR (que estará medido en días, horas, minutos) y el NIF de la persona que accede al domicilio, la cual deberá también estar dada de alta en el sistema. En el caso de la Figura 29, el usuario está generando un código de acceso para sí mismo, mientras que en la Figura 30, el código será generado para que lo use otro usuario del sistema, cuyo NIF deberá ser conocido por el usuario generador.

Cuando el usuario pulsa sobre el botón Confirmar, se pasa a la pantalla mostrada en la Figura 31, que contiene el código QR generado, en el que está encriptada la información del usuario generador del código así como del que va a usarlo y la casa en la que se va a usar. En el caso de la Figura 31, este código QR corresponde al que se generó para el propio usuario y, como se puede observar, aparece el usuario que ha generado el QR y la dirección del domicilio para el que ha sido generado de manera encriptada, es decir, como las tres primeras letras del nombre y las dos primeras de la dirección. Esto permite imprimir la imagen mostrada y usarla en el dispositivo de acceso, evitando que cualquiera que encuentre este papel impreso sepa dónde usarlo. Una llave normal, aunque se encuentre, no se sabe qué puerta abre, así que una llave en forma de código QR no debe perder esta ventaja. Al realizarse el proceso desde una página web, se podrá generar este código QR desde un ordenador, una tablet, un móvil o cualquier otro dispositivo con acceso a Internet, lo que permitirá su uso directamente desde el dispositivo, la impresión de dicho código, el guardado de la imagen en el equipo para su posterior uso o envío a otro usuario, y muchas otras posibilidades de uso.

Figura 28. Solicitud de QR propio

Ejemplo Paso a Paso

72

72

Figura 30. Solicitud de código QR para otro usuario del sistema

Figura 29. QR generado para el propio usuario

73 Apertura de puertas usando Códigos QR QR Door Opener

En este ejemplo, se ha generado el código QR en un ordenador, y posteriormente se ha procedido a copiar la imagen a un Smartphone para usarlo a la hora de acceder al domicilio para que las imágenes que se han insertado en la memoria sean de mayor calidad, pero como se ha explicado anteriormente se podría acceder directamente desde el móvil a la página web y generarlo todo desde él, simplificando el proceso. Tras la generación, se pasará al proceso de apertura de la puerta. Tal y como está programado ahora mismo el proyecto, hay que realizar la llamada a la aplicación, la cual abrirá una nueva ventana en la que se mostrará la imagen leída a través de la cámara del módulo de lectura, y una serie de mensajes. En este ejemplo paso a paso, se han decidido realizar dos capturas distintas, una correcta, que se produce el uso del código QR generado para el propio usuario antes de que pase el tiempo de validez del código QR, que era de diez minutos, y otra errónea, que se obtiene tras usar un código QR generado para otro usuario que también existe en el sistema, esperando a que pase el tiempo de validez, es decir, a que el código de acceso caduque. El hecho de realizar dos capturas distintas se justifica porque se desean observar las diferencias, que pueden observarse en las Figuras 32 y 33 respectivamente.

Como se puede comprobar en las Figuras 32 y 33, la única diferencia que se puede observar está en el hecho de que cuando el código es erróneo, se muestra un mensaje que dice “Imposible abrir puerta, inténtelo de nuevo con un código válido” y en el caso de que el código sea correcto el mensaje indica “Se va a proceder a la apertura de la puerta, Bienvenido”. Tal y como está implementado el proyecto actualmente, sólo se muestra este mensaje por pantalla, ya que el módulo de lectura está

Figura 30. Acceso correcto usando código QR generado para el propio usuario

Figura 31. Acceso erróneo usando código QR generado para otro usuario

Ejemplo Paso a Paso

74

74

actualmente instalado en un ordenador que no se encuentra conectado a ninguna puerta. Sin embargo, sí sirve para comprobar que todo funciona correctamente, ya que desde la lectura de los códigos QR en el módulo de lectura hasta la apertura de la puerta o el aviso de código incorrecto, hay una serie de pasos que implican que la comunicación se ha realizado de manera segura, la comprobación de que el código se está usando en la casa correcta, que el código existía en la base de datos, que el tiempo de validez no ha sido superado, etc.

Cada vez que se usa de uno de los códigos QR generados por un usuario para intentar el acceso a un domicilio, se procede a almacenar dicho intento de acceso en la base de datos del sistema, de manera que si dicho usuario accede a la sección de Registros de acceso, aparecerá una ventana como la mostrada en la Figura 34, que en este caso muestra los intentos de acceso con el código QR propio y con el código QR generado para el otro usuario, junto con los datos de acceso (fecha, domicilio) y si el acceso ha sido correcto o erróneo junto al motivo del error si es que lo ha habido.

Figura 32. Registro de Acceso

75 Apertura de puertas usando Códigos QR QR Door Opener

7. CONCLUSIONES Y TRABAJO

FUTURO

n este capítulo final, se tratarán de exponer las conclusiones finales que se pueden extraer

del proyecto realizado, así como intentar ver qué modificaciones se podrían realizar para

tratar de mejorar el resultado final obtenido.

7.1 Conclusiones

Tras la finalización del proyecto se puede comprobar que hemos cumplido los requisitos expuestos al comienzo de su realización: se ha conseguido la realización de un sistema que permite la apertura de una puerta mediante el uso de un dispositivo móvil (ya sea Smartphone, tablet o cualquier otro), de manera segura y registrando los accesos realizados para su posterior comprobación.

Todo ello se ha conseguido mediante la creación de un sistema robusto, que combina distintas tecnologías para la consecución del sistema final. La base de todo el sistema es Java, pero el autor ha debido estudiar lenguajes de programación web como HTML y el uso de JSP en él, la creación y gestión de una base de datos con lenguaje MySQL, el uso de librerías especiales de Java, como pueden ser las del proyecto ZXING, el gestor de cámaras de SARXOS o las librerías de comunicaciones seguras de Java, y otras muchas herramientas, que no han hecho sino aumentar los conocimientos para la creación de aplicaciones en los sistemas de comunicaciones.

Aunque no se abra una puerta en sí (cosa que sólo necesitaría la adición de varias líneas de programación al programa para enviar una señal de apertura al circuito que controle la puerta), la aplicación realiza el resto de las funcionalidades en su totalidad. Como se puede ver en el Capítulo 6, Ejemplo paso a paso, el proceso se realiza de manera completa, añadiendo nuevos usuarios al sistema, añadiendo casas enlazadas a dicho usuario, solicitando la creación de certificados digitales para esas casas, generando nuevos códigos QR de acceso, que tendrán un tiempo de validez y un identificador de las persona creadora y de la persona que lo va a usar, y finalmente la comprobación de los registros de acceso, de forma que el usuario pueda comprobar si ha habido algún intento fraudulento o erróneo de uso de códigos generados por él. A estas funcionalidades comentadas en el capítulo anterior mediante un ejemplo completo, hay que añadir la modificación de los datos del usuario, la eliminación de casas y cómo afecta esto a usuarios secundarios, ya que si la casa es eliminada por su administrador quedará eliminada del registro de todos los usuarios que tenían acceso a ella, la comunicación segura que se establece mediante SSL/TLS entre el módulo de lectura que se encuentra en la casa y el servidor de aplicación, de manera que la comunicación sea encriptada y ningún atacante pueda obtener la información intercambiada o engañar al sistema con mensajes erróneos (abrir una puerta cuando no sea correcto el código QR o mantenerla cerrada si es correcto).

E

“Nunca te olvides de sonreír porque el día que no sonrías será un día perdido”

- Charles Chaplin -

Conclusiones y Trabajo Futuro

76

76

Muchos escépticos pensarán que este sistema es inseguro, ya que si alguien consigue robar el código QR de un usuario, podrá acceder a su domicilio. Pero si este problema se mira desde otro punto de vista vemos que es más fiable que los métodos actuales. Por un lado, si alguien consigue robar la llave que el usuario usa para entrar en su casa, el usuario deberá cambiar la cerradura para evitar que el ladrón entre, mientras que con este sistema sólo habría que ponerse en contacto con el administrador para que revoque el código QR. Además, si el atacante usase el código QR, quedaría registrado en el sistema, permitiendo obtener más información sobre cuándo se habría producido la intrusión.

Otro problema de inseguridad podría ser el hecho de que un atacante obtuviese la contraseña del usuario, pudiendo realizar las modificaciones deseadas en la cuenta o generando códigos de acceso, o que el usuario dejase una sesión abierta en cualquiera de los dispositivos, permitiendo que un atacante que llegue tras él tenga acceso total con los privilegios de este usuario. El primer problema es el mismo que el que se produce si un atacante obtiene la contraseña de banca online del usuario, todo pasa por el cuidado que tenga a la hora de guardar las contraseñas de manera segura o de evitar que otros usuarios vean las contraseñas que escribe cuando accede al sistema. El segundo está solucionado, ya que el tiempo de expiración de sesión inactiva es bastante pequeño como para evitar que un atacante acceda.

7.2 Trabajo Futuro

En cuanto al trabajo futuro que se puede realizar en este proyecto, es decir, a las mejoras que se podrían añadir al sistema para conseguir una funcionalidad más completa, destacan las siguientes, que iremos enumerando.

- Implementación del sistema de lectura del código QR en una placa, de manera que se consiga reducir el tamaño del dispositivo que se encuentra en la puerta del domicilio y se comunica con el servidor central. El programa de lectura irá cargado en él, y se iniciará al pulsar un botón, pero deberá tener cable Ethernet o antena WI-FI para la conexión a Internet (preferiblemente Ethernet para evitar posibles hackeos del sistema) y algún puerto de entrada para la inserción de los certificados generados.

- Mejora del sistema de usuarios administradores y secundarios de casas, de manera que si un usuario secundario quiere tener acceso a una casa que ya tiene otro usuario administrador, se envíe a éste una petición de acceso, para que confirme que permite a dicho usuario acceder a la casa (ahora mismo se inserta el usuario nuevo, aunque sea con rol secundario, lo que significa actualmente que si el administrador borra la casa, también se eliminará del usuario secundario, mientras que el secundario sólo podrá eliminar su propio enlace con la casa).

- Adición de más información al registro de accesos, permitiendo un mayor conocimiento de accesos fraudulentos. Tal y como está implementado actualmente, si el código QR no es reconocido no se guarda la información en la tabla de registros, pero sería recomendable modificar esto para que si un atacante intenta acceder a un domicilio con un código QR desconocido, este hecho quede almacenado en la tabla de registros.

- Modificación del menú de registros de acceso, para que el usuario pueda filtrar y ordenar los datos que quiere presentar, de forma que todo se vea de acuerdo a las necesidades actuales del usuario.

- Mejora de la seguridad del sistema a todos los niveles, ya que aunque en principio la seguridad es bastante adecuada, siempre se están produciendo nuevos ataques que deben ser tenidos en cuenta. Uno de los principales a tener en cuenta sería la posibilidad de extracción de información de sistema a través de los formularios que insertan información en la base de datos que, aunque en principio llevan algo de protección gracias al formato usado, sigue siendo un punto de posible ataque.

- Encriptar de alguna forma la información del código QR que hay guardada en la base de datos. Si bien la contraseña de los usuarios está encriptada, no es así la información del código QR, y si un atacante conociera el formato de la cadena que luego forma el código QR podría usar los valores de la base de datos para crear sus propios códigos QR equivalentes a los válidos en el momento del

77 Apertura de puertas usando Códigos QR QR Door Opener

ataque.

- Inserción de un registro de los códigos QR activos para un usuario, de manera que éste pueda comprobar cuáles están en uso actualmente, y que permita la revocación de dichos códigos de acceso.

- Implementación de una aplicación para Smartphone, en Android, iOS, Windows Phone, y todos los posibles sistemas operativos disponibles para móviles, para que el acceso se pueda realizar de manera más simple y cómoda para el usuario.

- Uso de una entidad certificadora reconocida para la creación del certificado digital de la página web, de manera que un atacante no pueda falsear la web y crear también un certificado autofirmado.

- A la hora de identificar las casas, en lugar de usar la dirección como valor que se compara para decidir si está ya en el sistema o no, usar la referencia catastral, que sería una manera de identificar unívocamente un domicilio, evitando además posibles fallos de comparación debido a minúsculas y mayúsculas, el usar calle, plaza, ronda, etc.

Conclusiones y Trabajo Futuro

78

78

79 Apertura de puertas usando Códigos QR QR Door Opener

8. BIBLIOGRAFÍA

[1] Samsung, «Samsung Galaxy S4 I9505, Especificaciones,» 2013. [En línea]. Available: http://www.samsung.com/es/consumer/mobile-phone/smartphones/galaxy/GT-I9505ZWAPHE-spec.

[2] comScore, Inc., «comScore Mobilens, EU5 Smartphone Penetration Reaches 55 Percent in October 2012,» 17 Diciembre 2012. [En línea]. Available: http://www.comscore.com/Insights/Press_Releases/2012/12/EU5_Smartphone_Penetration_Reaches_55_Percent_in_October_2012.

[3] HackerspaceWiki, «Hackerspaces,» [En línea]. Available: http://hackerspaces.org/wiki/.

[4] W. Roush, «Xconomy, People Doing Strange Things With Soldering Irons: A Visit to Hackerspace,» 22 05 2009. [En línea]. Available: http://www.xconomy.com/national/2009/05/22/people-doing-strange-things-with-soldering-irons-a-visit-to-hackerspace/.

[5] L. B. K. U. H. N. Jeremy Blum, «LIBEtech,» Enero-Mayo 2012. [En línea]. Available: http://www.libetech.com/.

[6] C. Kaufman, R. Perlman y M. Speciner, «PKI (Public Key Infrastructure), Introduction,» de Private Communication in a Public World, Prentice Hall, 2002, pp. 365-367.

[7] C. Kaufman, R. Perlman y M. Speciner, «PKI, PKI Trust Models,» de Private Communication in a Public World, Prentice Hall, 2002, pp. 368-374.

[8] C. Kaufman, R. Perlman y M. Speciner, «PKI, PKIX and X.509,» de Network Security: Private Communication in a Public World, Prentice Hall, 2002, pp. 382-387.

[9] C. Kaufman, R. Perlman y M. Speciner, «SSL/TLS, Encoding,» de Network Security: Private Communication in a Public World, Prentice Hall, 2002, pp. 488-493.

[10] Distributed Rainbow Tables Project, «Free Rainbow Tables,» [En línea]. Available: https://www.freerainbowtables.com/.

[11] Canonical Ltd. Ubuntu, «Ubuntu,» [En línea]. Available: http://www.ubuntu.com/.

[12] Canonical Ltd. Ubuntu, «Ubuntu Documentation Help SynapticHowto,» [En línea]. Available: https://help.ubuntu.com/community/SynapticHowto.

[13] Oracle, «MySQL Workbench,» [En línea]. Available: http://www.mysql.com/products/workbench/.

[14] Oracle, «MySQL Connectors,» [En línea]. Available: http://www.mysql.com/products/connector/.

<Bibliografía

80

80

[15] The Apache Software Fundation, «Apache Tomcat,» [En línea]. Available: http://tomcat.apache.org/.

[16] Apache, «Tomcat Wiki FAQ/Connectors,» [En línea]. Available: http://wiki.apache.org/tomcat/FAQ/Connectors.

[17] F. J. Muñoz Calle, «Laboratorio de Software de Comunicaciones, Práctica 8,» Sevilla, 2012, pp. 5-9.

[18] The OpenSSL Project, «OpenSSL, Cryptography and SSL/TLS toolkit,» [En línea]. Available: http://www.openssl.org/.

[19] C. U. Scholler, «Package TinyCA, Debian,» [En línea]. Available: http://packages.debian.org/sid/tinyca.

[20] Sarxos, «Webcam-Capture Repository,» 2012. [En línea]. Available: https://github.com/sarxos/webcam-capture.

[21] GitHub Inc., «GitHub,» [En línea]. Available: https://github.com/.

[22] SourceForge, «Myron WebcamXtra, SourceForge,» [En línea]. Available: http://webcamxtra.sourceforge.net/.

[23] S. Owen, «Zxing ("Zebra Crossing"),» [En línea]. Available: http://code.google.com/p/zxing/.

[24] C. Kaufman, R. Perlman y M. Speciner, «Authentication of people,» de Network Security Private Communication in a Public World, Prentice Hall, 2002, pp. 235-254.

[25] C. Kaufman, R. Perlman y M. Speciner, «Public Key Algorithms, RSA,» de Network Security- Private Communication in a Public World, Prentice Hall, 2002, pp. 152-163.

[26] W. Stalling, «Malicius Software, Distributed Denial of Service Attack,» de Network Security Essentials, 4th Edition, Prentice Hall, 2011, pp. 365-370.

[27] l. swordsman, «Database Develop Tips, Webmaster Required: defense DDOS attacks Ultimate Guide,» 18 Diciembre 2004. [En línea]. Available: http://www.databasedevelop.com/76413/.

[28] The Open Web Application Security Project, «Top 10 2013-A1-Injection, OWASP,» 12 Junio 2013. [En línea]. Available: https://www.owasp.org/index.php/Top_10_2013-A1-Injection.

[29] The Open Web Application Security Project, «Preventing SQL Injection in Java, OWASP,» 14 Enero 2008. [En línea]. Available: https://www.owasp.org/index.php/Preventing_SQL_Injection_in_Java.

[30] C. Kaufman, R. Perlman y M. Speciner, «SSL/TLS, Encoding,» de Private Communications in a Public World, Prentice Hall, 2002, pp. 490-497.

81 Apertura de puertas usando Códigos QR QR Door Opener

9. ANEXO I: CÓDIGO DEL PROGRAMA

9.1 Módulo de Lectura y Apertura

9.1.1 Clases

9.1.1.1 Constantes.java

package PFCCarlosCorralesClienteSeguro;

/*Interfaz donde estarán las constantes necesarias para el

proyecto */

public interface Constantes {

//puerto por defecto donde escucha el servidor

final int PUERTO=2798;

}

9.1.1.2 LectorSarxos.java

/* Clase que implementa el lector de códigos QR y el envÃo de

los datos

* al servidor a través de una conexión SSL.

* La lectura del código QR a través de la webcam viene del

ejemplo

* de Sarxos en Github

* https://github.com/sarxos/webcam-capture/tree/master/webcam-

capture-examples/webcam-capture-qrcode/

*

*/

package PFCCarlosCorralesClienteSeguro;

import java.awt.Dimension;

import java.awt.FlowLayout;

import java.awt.Toolkit;

import java.awt.event.WindowEvent;

import java.awt.image.BufferedImage;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.UnknownHostException;

import java.util.concurrent.Executor;

import java.util.concurrent.Executors;

import java.util.concurrent.ThreadFactory;

Anexo I: Código del Programa

82

82

import javax.net.ssl.SSLSocket;

import javax.net.ssl.SSLSocketFactory;

import javax.swing.JFrame;

import javax.swing.JTextArea;

import com.github.sarxos.webcam.Webcam;

import com.github.sarxos.webcam.WebcamPanel;

import com.github.sarxos.webcam.WebcamResolution;

import com.google.zxing.BinaryBitmap;

import com.google.zxing.ChecksumException;

import com.google.zxing.FormatException;

import com.google.zxing.NotFoundException;

import

com.google.zxing.client.j2se.BufferedImageLuminanceSource;

import com.google.zxing.common.BitMatrix;

import com.google.zxing.common.DecoderResult;

import com.google.zxing.common.DetectorResult;

import com.google.zxing.common.HybridBinarizer;

import com.google.zxing.qrcode.decoder.Decoder;

import com.google.zxing.qrcode.detector.Detector;

public class LectorSarxos extends JFrame implements Runnable,

ThreadFactory {

private static final long serialVersionUID =

6441489157408381878L;

private Executor executor =

Executors.newSingleThreadExecutor(this);

private Webcam webcam = null;

private WebcamPanel panel = null;

private JTextArea textarea = null;

private String IPServ=null;

private String ficherop12=null;

/* Aquí es donde realmente se ejecuta el lector. Se le pasa

como

* parámetro la IP del servidor para conectarse a él.

*/

public LectorSarxos(String ficherop12arg,String

IPServClient) {

this.IPServ=IPServClient;

this.ficherop12=ficherop12arg;

setLayout(new FlowLayout());

setTitle("Leer QR con Webcam"); //definimos la

ventana de visión

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Dimension size = WebcamResolution.QVGA.getSize();

//pantalla en tamaño decente

83 Apertura de puertas usando Códigos QR QR Door Opener

webcam = Webcam.getDefault();

webcam.setViewSize(size);

panel = new WebcamPanel(webcam); //para ver la webcam

en el layout definido antes

panel.setPreferredSize(size);

textarea = new JTextArea(); //Área de texto donde

veremos si se ha leido el QR o no

textarea.setEditable(false);

textarea.setLineWrap(true); //para que en caso de que

sea una cadena larga, ocupe más de una línea.

textarea.setPreferredSize(size);

add(panel); //añadimos la webcam y el area de texto

al layout

add(textarea);

pack(); //inicia la muestra por pantalla

setVisible(true);

executor.execute(this);

}

public void run() {

DetectorResult result = null; //QR leido

DecoderResult resultdecoded=null; //información del

QR

BufferedImage image = null;

BitMatrix matrix=null;

DataOutputStream flujosalida=null;

DataInputStream flujoentrada=null;

SSLSocket ss=null;

boolean abrir=false;

int i=0;

/*Mientras que no se decodifique el QR, o se pasen

100 iteraciones del bucle

* se sigue intentando leer.

* Las 100 iteraciones son una forma de evitar que la

cámara está funcionando

* indefinidamente.

*/

while

(((result==null)||(resultdecoded==null))&&(i<100)){

image=null;

if (webcam.isOpen()) {

while((image = webcam.getImage()) == null)

{

Anexo I: Código del Programa

84

84

}

}

BufferedImageLuminanceSource source = new

BufferedImageLuminanceSource(image);

BinaryBitmap bitmap = new BinaryBitmap(new

HybridBinarizer(source));

try {

matrix = bitmap.getBlackMatrix();

result = new

Detector(matrix).detect(); //detección de código QR

System.out.println("Detectado");

resultdecoded = new

Decoder().decode(result.getBits()); //decodificación de código

QR

System.out.println("Decodificado");

} catch (NotFoundException e) {

// fall thru, it means there is no QR

code in image

} catch (FormatException e) {

e.printStackTrace();

} catch (ChecksumException e) {

e.printStackTrace();

}

i++;

}

//si se ha leido y decodificado el QR

if ((result != null)&&(resultdecoded!=null)) {

System.out.println("obtenido resultado");

textarea.setText("Codigo QR leido\n");

textarea.append(resultdecoded.getText()+"\n");

try {

//PREPARAMOS KEYSTORE Y TRUSTORE

//set necessary keystore properties -

using a JKS

System.setProperty("javax.net.ssl.keyStore","/home/userkeys

tore/"+ficherop12);

System.setProperty("javax.net.ssl.keyStorePassword","1234567890"

);

System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");

85 Apertura de puertas usando Códigos QR QR Door Opener

//set necessary truststore properties - using JKS

System.setProperty("javax.net.ssl.trustStore","/home/userkeystor

e/usertruststore.jks");

System.setProperty("javax.net.ssl.trustStorePassword","123456789

0");

System.setProperty("javax.net.ssl.trustStoreType", "JKS");

// register a https protocol handler -

this may be required for previous JDK versions

System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl

.internal.www.protocol");

SSLSocketFactory factory =

(SSLSocketFactory) SSLSocketFactory.getDefault();

ss=

(SSLSocket)factory.createSocket(this.IPServ, Constantes.PUERTO);

//abrimos el socket

flujosalida= new

DataOutputStream(ss.getOutputStream());

flujoentrada= new DataInputStream

(ss.getInputStream());

System.out.println("CONEXION:

"+ss.getInetAddress().getHostAddress()+":"+ss.getPort());

String stringdecoded =

resultdecoded.getText();

flujosalida.writeUTF(stringdecoded);

//enviamos al servidor los datos del QR en forma de cadena

abrir=flujoentrada.readBoolean();

//recibimos confirmación del servidor para abrir la puerta

if(abrir==true){

System.out.println("abriendo

puerta\n");

textarea.append("Se va a

proceder a la apertura de la puerta\n");

textarea.append("Bienvenido\n");

}else{

System.out.println("CódigoQR

inválido\n");

textarea.append("Imposible abrir

la puerta\n");

textarea.append("Inténtelo de

nuevo con un código válido\n");

}

flujosalida.close(); //cerramos

sockets y flujos.

flujoentrada.close();

Anexo I: Código del Programa

86

86

ss.close();

} catch (UnknownHostException e1) {

e1.printStackTrace();

} catch (IOException e1) {

e1.printStackTrace();

}

}else if(i<100){

textarea.append("CódigoQR no

reconocido\n");

}else{

textarea.append("Tiempo de espera

agotado\n");

}

try {

Thread.sleep(5000); //para que el usuario vea

que se ha leido el QR

} catch (InterruptedException e) {

e.printStackTrace();

}

//y cerramos la ventana

WindowEvent wev = new WindowEvent(this,

WindowEvent.WINDOW_CLOSING);

Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent

(wev);

}

public Thread newThread(Runnable r) {

Thread t = new Thread(r, "example-runner");

t.setDaemon(true);

return t;

}

}

9.1.1.3 MainCliente.java

/*Clase principal del cliente de lectura del código QR

* Simplemente se encarga de llamar al lector con una

* conexión al servidor con IP que el usuario pasa como

parámetro.

* Si no pasa IP, se supone que el servidor está en en

localhost

* IP 127.0.0.1

*/

package PFCCarlosCorralesClienteSeguro;

87 Apertura de puertas usando Códigos QR QR Door Opener

import PFCCarlosCorralesClienteSeguro.LectorSarxos;

public class mainCliente {

public static void main(String[] args) {

if(args.length>2||args.length<1){

System.out.println("Uso Cliente Archivo.p12

<IPServidor>");

}else if(args.length==1){

new LectorSarxos(args[0], "127.0.0.1");

}else{

System.out.println(args[0]+" " +args[1]);

new LectorSarxos(args[0], args[1]);

}

}

}

9.1.2 Librerías externas usadas

9.1.2.1 SLF4J

bridj-0.6.3-20130316.190111-13.jar

dx-1.7.jar

slf4j-api-1.7.2.jar

slf4j-nop-1.7.5.jar

9.1.2.2 ZXING

core.jar

javase.jar

9.1.2.3 Sarxos

webcam-capture-0.3.10-RC2.jar

Anexo I: Código del Programa

88

88

9.2 Servidor de Aplicación

9.2.1 Clases

9.2.1.1 Constantes.java

package PFCCarlosCorralesServidorQR;

/*Interfaz donde estarán las constantes necesarias para el

proyecto */

public interface Constantes {

//Parámetros del servidor por defecto

//final String SERVIDOR="127.0.0.1";

//puerto por defecto donde escucha el servidor QR

final int PUERTO_QR=2798;

//Usuario y clave para el acceso a la base de datos

final String USERBBDD="root";

final String PASSWORDBBDD="PFC";

//Nombre de la base de datos para acceder a ella

final String BBDDNAME="BBDDPFCv5";

}

9.2.1.2 MetodosServidorQR.java

/*Esta clase contiene los métodos del servidor QR

*

*/

package PFCCarlosCorralesServidorQR;

public class MetodosServidorQR {

/*Este método deberá comunicarse con la base de datos para

comprobar que

* el usuario exista y el QR sea válido

*/

int CompruebaQR(String cadenaQRrec, String cadenaQRBBDD,

long fechaactualmili){

int valido=1; //Sirve para devolver el error al

usuario, de

//forma que se guarde en la BBDD.

//Error 1 indica error desconocido.

int index2=cadenaQRBBDD.indexOf("FechaExpiracion:");

String

fechafinString=cadenaQRBBDD.substring(index2+16);

long fechavalidezBBDD=Long.parseLong(fechafinString);

89 Apertura de puertas usando Códigos QR QR Door Opener

System.out.println("FechaActual: "+fechaactualmili);

System.out.println("fechavalidezBBDD:

"+fechavalidezBBDD);

if((fechavalidezBBDD<fechaactualmili)){

valido=2; //error 2 indica fecha de validez

superada

System.out.println("CódigoQR Inválido: Fecha

Validez superada");

}else if(cadenaQRrec.compareTo(cadenaQRBBDD)!=0){

valido=3; //error 3 indica que el código

contenido en el QR no coincide

//con el guardado en al BBDD.

System.out.println("CódigoQR Inválido: No

coincide el contenido");

}else{

valido=0; //error 0 indica que el código es

correcto

System.out.println("CódigoQR Válido");

}

return valido;

}

}

9.2.1.3 MedotosSQL.java

/*Clase que contiene los métodos para la gestión de la

información

* en la base de datos SQL

*/

package PFCCarlosCorralesServidorQR;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

public class MetodosSQL {

/*Método que realiza la conexión a la base de datos.

* Devuelve la conexión en sí

*/

public Connection ConectarServidorSQL(){

Connection conexion=null;

// Establecemos la conexión con la base de datos.

Anexo I: Código del Programa

90

90

try {

Class.forName("com.mysql.jdbc.Driver");

//aseguramos que el

//Driver se inicializa y

//registra

conexion = DriverManager.getConnection

("jdbc:mysql://localhost/"+Constantes.BBDDNAME,Constantes.USERBB

DD, Constantes.PASSWORDBBDD);

} catch (SQLException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

System.out.println("Conectado a la BBDD");

return conexion;

}

/*Este método devuelve un String con los datos que deberá

haber en el código QR

* para que el servidor pueda compararlos y ver si es

correcto o incorrecto

* Se le pasa como parámetro el identificador del código QR

* para que pueda ser reconocido dentro de la BBDD

*/

public String getUserData(Connection conexion, String

IdQR){

String datos=null;

if (conexion != null){

PreparedStatement stmt;

try {

int IdInteger=Integer.parseInt(IdQR);

String sqlString="SELECT * FROM QRCodes

WHERE idQR=?";

stmt =

conexion.prepareStatement(sqlString);

//buscamos en la base de datos la

información del código QR a través de su identificador

stmt.setInt(1, IdInteger);

ResultSet res = stmt.executeQuery();

//obtenemos la informacion

while(res.next()){

int QRCodeid = res.getInt("idQR");

String IdUser =

res.getString("idUsuario");

String

IdUserAccess=res.getString("idUsuarioAccede");

int idCasa= res.getInt("idCasa");

long

FechaExpedicion=res.getLong("FechaExpedicion");

91 Apertura de puertas usando Códigos QR QR Door Opener

long FechaValidez=res.getLong("FechaValidez");

datos= "IdQR:" +

Integer.toString(QRCodeid) +" IdUser:"+IdUser+"

IdUseraccede:"+IdUserAccess+"

IdCasa:"+Integer.toString(idCasa)+" FechaExpedicion:"+

Long.toString(FechaExpedicion)+"

FechaExpiracion:"+Long.toString(FechaValidez);

System.out.println(datos);

}

res.close(); //cerramos las conexiones con

la base de datos

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return datos;

}

/*Este método inserta estadísticas de acceso en la BBDD.

* Cuando un usuario quiere acceder con un código QR, tras

la comprobación

* se guarda en la BBDD quién ha entrado, la hora y si ha

habido error.

*/

public void InsertaEstadistica(Connection conexion, int

IdQR, long fechaacceso, int error, int idCasa, String

idUsuario){

if (conexion != null){

PreparedStatement stmt;

String sqlString="INSERT INTO EstadisticaAcceso

(idQR, FechaAcceso, Tipo_de_Error, idCasa, idUsuario) VALUES

(?,?,?,?,?) ";

try {

stmt =

conexion.prepareStatement(sqlString);

//preparamos los valores del

statement

stmt.setInt(1,IdQR);

stmt.setLong(2, fechaacceso);

stmt.setInt(3,error);

stmt.setInt(4,idCasa);

stmt.setString(5,idUsuario);

//Insertamos los datos de acceso en

la base de datos

stmt.executeUpdate();

System.out.println("Estadística

insertada en DDBB");

stmt.close();

Anexo I: Código del Programa

92

92

}catch (SQLException e) {

e.printStackTrace();

}

}

}

/*Método que extrae el Número de Serie del certificado de

la

* base de datos. Se le pasa como parémetro el

identificador de la

* casa, ya que cada casa tendrá asociado un único

certificado.

* Devuelve el Número de Serie de dicho certificado

*/

public int extraeIDCert(Connection conexionBBDD, int

idCasa) {

int idCert=0;

if (conexionBBDD != null){

PreparedStatement stmt;

String sqlString="SELECT * FROM Casas WHERE

idCasa=?";

try {

stmt =

conexionBBDD.prepareStatement(sqlString);

stmt.setInt(1, idCasa);

//obtenemos el número de serie del

certificado asociado a la casa

ResultSet res = stmt.executeQuery();

while(res.next()){

String idCertString =

res.getString("idCertificado");

//En la base de datos está en

forma de cadena

//lo convertimos a entero

idCert=Integer.parseInt(idCertString,16);

}

res.close();

stmt.close();

}catch (SQLException e) {

e.printStackTrace();

}

}

return idCert;

}

}

93 Apertura de puertas usando Códigos QR QR Door Opener

9.2.1.4 ServidorMain.java

/*Clase principal de servidor. Se encarga únicamente de imprimir

por

* pantalla el inicio y de crear un nuevo socket de servidor

*/

package PFCCarlosCorralesServidorQR;

public class ServidorMain {

public static void main(String[] args) {

SocketServidorQR s;

System.out.println(" SERVIDOR QRCode ");

System.out.println("----Realizado por Carlos Corrales

Yerpes----");

System.out.println("------------------------------------

--------");

System.out.println("(pulse ctrl-c para terminar");

s=new SocketServidorQR(Constantes.PUERTO_QR);

}

}

9.2.1.5 SocketClienteQR.java

/**

* Socket para recibir datos de los clientes QR.

* Recibe los datos del QR del cliente y comprueba si son

correctos

* Envía al cliente un OK si los datos son válidos.

*

*/

package PFCCarlosCorralesServidorQR;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.math.BigInteger;

import java.sql.Connection;

import java.sql.SQLException;

Anexo I: Código del Programa

94

94

import java.util.Date;

import javax.net.ssl.SSLSession;

import javax.net.ssl.SSLSocket;

//Se inicia un hilo por cada conexión, de manera que sea

independiente

public class SocketClienteQR extends Thread{

private SSLSocket sc;

private DataInputStream entrada=null;

private DataOutputStream salida=null;

private MetodosServidorQR metodosQR;

private MetodosSQL metodosBBDD;

private String cadenaBBDD=null;

private Connection conexionBBDD=null;

private String idQRrec=null;

private boolean usuario=false;

private int errorQR=1;

public SocketClienteQR (SSLSocket s){

this.sc=s;

System.out.println("CONEXION:

"+this.sc.getInetAddress().getHostAddress()+":"+this.sc.getPort(

));

this.start();

}

public synchronized void run(){

//Cargamos los métodos del servidor QR y de conexión y

tratamiento de la BBDD

this.metodosQR= new MetodosServidorQR();

this.metodosBBDD= new MetodosSQL();

try {

this.entrada= new

DataInputStream(sc.getInputStream());

this.salida= new

DataOutputStream(sc.getOutputStream());

Date fechaactual= new Date(); //capturamos

la fecha de acceso

long

fechaactualmili=fechaactual.getTime(); //la pasamos a

milisegundos para la comparación

//Capturamos los datos del certificado del

usuario

//Para la comprobación de la información

95 Apertura de puertas usando Códigos QR QR Door Opener

SSLSession sesionSSL=sc.getSession();

javax.security.cert.X509Certificate[]

certcliente=sesionSSL.getPeerCertificateChain();

BigInteger

idCertUsuario=certcliente[0].getSerialNumber();

System.out.println("Recibiendo imagen...");

this.wait(4000); //Esperamos un tiempo

prudencial para recibir los datos del QR

String cadenarecibida= entrada.readUTF();

//leemos la entrada

System.out.println("Recibido: "

+cadenarecibida);

this.conexionBBDD=metodosBBDD.ConectarServidorSQL();

//conectamos con la base de datos

int

indexIdUser=cadenarecibida.indexOf("IdUser");

this.idQRrec=cadenarecibida.substring(5,

indexIdUser-1);

this.idQRrec.trim(); //obtenemos el

identificador del código QR

this.cadenaBBDD=metodosBBDD.getUserData(this.conexionBBDD,

idQRrec); //obtenemos la cadena correspondiente a ese código QR

de la base de datos

int

indexidUserAccede=cadenaBBDD.indexOf("IdUseraccede");

String

idUser=cadenaBBDD.substring(indexIdUser+7,indexidUserAccede-1);

idUser.trim(); //obtenemos el

identificador de usuario

int

indexFechaexpedicion=cadenaBBDD.indexOf("FechaExpedicion");

int

indexidCasa=cadenaBBDD.indexOf("IdCasa");

int

idCasa=Integer.parseInt((cadenaBBDD.substring(indexidCasa+7,

indexFechaexpedicion-1)).trim()); //obtenemos el identificador

de la casa

int

idCertBBDD=metodosBBDD.extraeIDCert(this.conexionBBDD, idCasa);

//y extraemos de la BBDD el Identificador del certificado que se

corresponde con esa casa

if(idCertBBDD!=idCertUsuario.intValue()){

errorQR=4; //error 4 indica que el

número de certificado y el identificador de casa del QR

//no se corresponden.

Anexo I: Código del Programa

96

96

System.out.println("Cert Inválido");

}else{

//Si el certificado es correcto, se

comprueba que le código

//QR sea el correcto, el que hay en

la base de datos

this.errorQR=metodosQR.CompruebaQR(cadenarecibida,

this.cadenaBBDD, fechaactualmili);

if(errorQR==0){

//si el QR y el certificado son

correctos, se deja acceder

//al usuario

usuario=true;

}

}

//Enviamos al usuario el código de

apertura de puerta

this.salida.writeBoolean(usuario);

//y guardamos lo ocurrido en la tabla de

estadísticas

System.out.println("IDusuario: "+idUser);

this.metodosBBDD.InsertaEstadistica(conexionBBDD,

Integer.parseInt(idQRrec), fechaactualmili, errorQR, idCasa,

idUser);

entrada.close(); //cerramos los flujos

salida.close();

sc.close(); //cerramos el socket

this.conexionBBDD.close(); //desconectamos

de la BBDD

} catch (IOException e) {

e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

97 Apertura de puertas usando Códigos QR QR Door Opener

9.2.1.6 SocketServidorQR.java

/**

* Clase para la creación del socket para la escucha de

peticiones al servidor QR

* Se puede quedar escuchando múltiples sockets al mismo

tiempo.

*/

package PFCCarlosCorralesServidorQR;

import java.io.*;

import javax.net.ssl.SSLServerSocket;

import javax.net.ssl.SSLServerSocketFactory;

import javax.net.ssl.SSLSocket;

public class SocketServidorQR extends Thread{

private int puerto;

public SocketServidorQR(int puerto){

this.puerto=puerto;

this.start();

}

public void run(){

try{

//EStablecemos el keystore y el trustore

//set necessary keystore properties - using a JKS

System.setProperty("javax.net.ssl.keyStore","/home/usuario/serve

rkeystore/QRappServer.p12");

System.setProperty("javax.net.ssl.keyStorePassword","CCCPFC2013"

);

System.setProperty("javax.net.ssl.keyStoreType",

"PKCS12");

//set necessary truststore properties - using JKS

System.setProperty("javax.net.ssl.trustStore","/home/usuario/ser

verkeystore/servertruststore.jks");

System.setProperty("javax.net.ssl.trustStorePassword","CCCPFC201

3");

System.setProperty("javax.net.ssl.trustStoreType",

"JKS");

// register a https protocol handler - this may be

required for previous JDK versions

Anexo I: Código del Programa

98

98

System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl

.internal.www.protocol");

SSLServerSocketFactory factory =

(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

SSLServerSocket

s=(SSLServerSocket)factory.createServerSocket(this.puerto);

s.setReuseAddress(true); //se pueden tener varios

sockets a la vez

s.setNeedClientAuth(true); //se obliga al usuario a

enviar su certificado a través de la conexión SSL

while(true){

SSLSocket sc=(SSLSocket) s.accept(); //se acepta

una conexión

new SocketClienteQR(sc); //se crea un nuevo hilo

socket cliente por cada conexión recibida

}

}catch(IOException e){

System.out.println(e.toString());

}

}

}

9.2.2 Librerías externas usadas

9.2.2.1 ZXING

core.jar

javase.jar

9.2.2.2 MySQL

mysql-connector-java-5.1.25-bin.jar

99 Apertura de puertas usando Códigos QR QR Door Opener

9.3 Servidor Web

9.3.1 Clases

9.3.1.1 CasaBean.java

/*Java Bean para una casa

* Incluye el identificador de casa, su dirección, código postal

y población

*/

package PFCWebPackage;

public class CasaBean {

private int idCasa;

private String Direccion;

private String CP;

private String Poblacion;

public int getIdCasa(){

return idCasa;

}

public void setIdCasa(int newIdCasa){

idCasa=newIdCasa;

}

public String getDireccion(){

return Direccion;

}

public void setDireccion(String newDireccion){

Direccion=newDireccion;

}

public String getCP(){

return CP;

}

public void setCP(String newCP){

CP=newCP;

}

public String getPoblacion(){

return Poblacion;

}

public void setPoblacion(String newPoblacion){

Poblacion=newPoblacion;

}

}

Anexo I: Código del Programa

100

100

9.3.1.2 CasaServlet.java

/*Servlet para la gestión de la información de las casas

* Usa el método Post, lo que permite mayor seguridad

*/

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/CasaServlet")

public class CasaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public CasaServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

HttpSession session=request.getSession(); //Obtenemos

los datos de la sesión

String opcion=request.getParameter("Tratamiento");

//obtenemos el parámetro tratamiento

CasaBean casa=new CasaBean(); //Capturamos los datos

enviados en el formulario

casa.setIdCasa(Integer.parseInt(request.getParameter("IDcas

a")));

casa.setDireccion(request.getParameter("Direccion"));

casa.setCP(request.getParameter("CP"));

casa.setPoblacion(request.getParameter("Poblacion"));

UserBean

usuario=(UserBean)session.getAttribute("currentSessionUser");

String DNI=usuario.getDNI();

101 Apertura de puertas usando Códigos QR QR Door Opener

//Comprobamos la opción solicitada por el usuario

//Si es generar un QR

if(opcion.compareTo("Generar QR")==0){

session.setAttribute("Casa",casa);

response.sendRedirect("GenerarQR.jsp");

//generarqr page

//Si es crear un certificado

}else if(opcion.compareTo("Crear Certificado")==0){

session.setAttribute("Casa", casa);

response.sendRedirect("CrearCertificado.jsp");

//Crear certificado page

//Si es eliminar la casa

}else if(opcion.compareTo("Eliminar")==0){

//Creamos la conexion a la BBDD

Connection

conexion=FuncionesWeb.ConectarServidorSQL();

//Eliminamos la casa con el id enviado de la

bbdd

boolean

eliminada=FuncionesWeb.EliminarCasa(casa.getIdCasa(), DNI,

conexion);

if(eliminada==true){

response.sendRedirect("VerCasaServlet");

//generarqr page

}else{

response.sendRedirect("Error.jsp"); //si

no se ha podido eliminar, damos error

}

//Cerramos la conexión a la BBDD

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

//Si no es ninguna de las anteriores, se ha

accedido por error

}else{

System.out.println("ERROR");

response.sendRedirect("Error.jsp");

}

}

}

Anexo I: Código del Programa

102

102

9.3.1.3 Constantes.java

package PFCWebPackage;

/*Interfaz donde estarán las constantes necesarias para el

proyecto */

public interface Constantes {

//Parámetros del servidor por defecto

//puerto por defecto donde escucha el servidor QR

final int PUERTO_QR=2798;

//Datos por defecto de la base de datos. Nombre y user

password de acceso

final String BBDDNAME="BBDDPFCv5";

final String USERBBDD="root";

final String PASSWORDBBDD="PFC";

}

9.3.1.4 CorreoCertServlet.java

/*Servlet para el tratamiento de la petición de envío de correo

* al servidor para la generación del certificado

*/

package PFCWebPackage;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/CorreoCertServlet")

public class CorreoCertServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public CorreoCertServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

103 Apertura de puertas usando Códigos QR QR Door Opener

HttpSession sesion=request.getSession(); //Obtenemos la

sesión para extraer los datos

//extraemos usuario y casa de la sesión

UserBean

usuario=(UserBean)sesion.getAttribute("currentSessionUser");

CasaBean casa=(CasaBean)sesion.getAttribute("Casa");

//Enviamos el mail al correo de administración

boolean enviado=FuncionesWeb.enviarmail(usuario,

casa);

//si se ha enviado correctamente, se vuelve al menú

principal

if(enviado=true){

response.sendRedirect("userLogged.jsp");

}else{

response.sendRedirect("error.jsp");

}

}

}

9.3.1.5 EstadisticaBean.java

/*JavaBean que contiene la información de la estadística

guardada

* Contiene el identificador del QR, las distintas fechas de

expedición

* acceso y expiración, la dirección completa, el usuario y el

tipo de error

*/

package PFCWebPackage;

public class EstadisticaBean {

private int idQR;

private long fechaexpedicion;

private long fechaexpiracion;

private long fechaacceso;

private String direccion;

private String poblacion;

private String usuario;

private String usuarioacceso;

private int error;

public int getIdQR(){

return idQR;

}

public void setIdQR(int newIdQR){

idQR=newIdQR;

}

Anexo I: Código del Programa

104

104

public long getFechaexpedicion(){

return fechaexpedicion;

}

public void setFechaexpedicion(long newfechaexpedicion){

fechaexpedicion=newfechaexpedicion;

}

public long getFechaexpiracion(){

return fechaexpiracion;

}

public void setFechaexpiracion(long newfechaexpiracion){

fechaexpiracion=newfechaexpiracion;

}

public long getFechaacceso(){

return fechaacceso;

}

public void setFechaacceso(long newfechaacceso){

fechaacceso=newfechaacceso;

}

public String getPoblacion(){

return poblacion;

}

public void setPoblacion(String newPoblacion){

poblacion=newPoblacion;

}

public String getDireccion(){

return direccion;

}

public void setDireccion(String newDireccion){

direccion=newDireccion;

}

public String getUsuario(){

return usuario;

}

public void setUsuario(String newUsuario){

usuario=newUsuario;

}

public String getUsuarioacceso(){

return usuarioacceso;

}

public void setUsuarioacceso(String newUsuarioacceso){

105 Apertura de puertas usando Códigos QR QR Door Opener

usuarioacceso=newUsuarioacceso;

}

public int getError(){

return error;

}

public void setError(int newError){

error=newError;

}

}

9.3.1.6 FuncionesWeb.java

/*Esta clase contiene todos los mÉtodos usados por el servidor

Web

* Se incluye una descripción de cada uno

*/

package PFCWebPackage;

import java.awt.image.BufferedImage;

import java.io.FileOutputStream;

import java.io.IOException;

import java.security.NoSuchAlgorithmException;

import java.security.spec.InvalidKeySpecException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.Date;

import java.util.Properties;

import javax.imageio.ImageIO;

import javax.mail.Message;

import javax.mail.MessagingException;

import javax.mail.Session;

import javax.mail.internet.InternetAddress;

import javax.mail.internet.MimeMessage;

import com.google.zxing.BarcodeFormat;

import com.google.zxing.Writer;

import com.google.zxing.WriterException;

import com.google.zxing.common.BitMatrix;

import com.google.zxing.qrcode.QRCodeWriter;

import com.sun.mail.smtp.SMTPTransport;

Anexo I: Código del Programa

106

106

public class FuncionesWeb {

static ResultSet rs = null;

/*mÉtodo que crea una nueva conexión al servidor SQL

* Devuelve dicha conexión

*/

public static Connection ConectarServidorSQL(){

Connection conexion=null;

// Establecemos la conexión con la base de datos.

try {

Class.forName("com.mysql.jdbc.Driver");

//aseguramos que el

//Driver se inicializa y

//registra

conexion = DriverManager.getConnection

("jdbc:mysql://localhost/"+Constantes.BBDDNAME,Constantes.USERBB

DD, Constantes.PASSWORDBBDD);

} catch (SQLException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return conexion;

}

/*MÉtodo que hace el login del usuario.

* Comprueba en la BBDD si el usuario existe y decide

* si le da acceso o no. Si hay 3 fallos consecutivos en el

* mismo usuario, se bloquea la cuenta

*/

public static UserBean login(UserBean bean){

PreparedStatement stmt = null;

String username = bean.getUsername();

String password = bean.getPassword();

Connection conexion=null;

conexion=ConectarServidorSQL();

String sqlString="SELECT * FROM Usuario WHERE

User=?"; //seleccionamos los datos del usuario

try {

stmt = conexion.prepareStatement(sqlString);

stmt.setString(1, username);

107 Apertura de puertas usando Códigos QR QR Door Opener

//Extraemos el usuario de la base de datos,

basado en el username

rs=stmt.executeQuery();

if(rs.next()==false){

//si el usuario no existe, el usuario es

inválido

bean.setValid(false);

}else{

//si existe, extraemos el password (con

salt y hash) de la bbdd y lo validamos

String

verdaderoHash=rs.getString("Password");

int intentos=rs.getInt("Intentos");

//y comprobamos si coincide con el

computado

if((PasswordHash.validatePassword(password,

verdaderoHash))&&(intentos<3)){

//si el password es correcto,

extramos los datos de usuario

//de la BBDD y los copiamos al

JavaBean

String nombre=rs.getString("Nombre");

String DNI=rs.getString("idUsuario");

String

telefono=rs.getString("Telefono");

String email=rs.getString("Email");

long

ultimoacceso=rs.getLong("UltimoAcceso");

bean.setNombre(nombre);

bean.setDNI(DNI);

bean.setTelefono(telefono);

bean.setEmail(email);

bean.setPassword(verdaderoHash);

bean.setValid(true);

bean.setUltimoAcceso(ultimoacceso);

String sqlString2="UPDATE Usuario SET

Intentos=? WHERE User=?";

PreparedStatement

stmt2=conexion.prepareStatement(sqlString2);

stmt2.setInt(1, 0);

stmt2.setString(2, username);

//Si el usuario se autentica

correctamente, se pone a 0 el contador de fallos

stmt2.executeUpdate();

stmt2.close();

Anexo I: Código del Programa

108

108

}else{

/*Si el password es

incorrecto,aumentamos el número de

intentos y comprobamos si es 3. de

ser 3

se bloquea el usuario*/

if(intentos<3){

intentos++;

String sqlString2="UPDATE

Usuario SET Intentos=? WHERE User=?";

PreparedStatement

stmt2=conexion.prepareStatement(sqlString2);

stmt2.setInt(1, intentos);

stmt2.setString(2, username);

//Si el usuario se autentica

correctamente, se pone a 0 el contador de fallos

stmt2.executeUpdate();

stmt2.close();

}

}

}

} catch (SQLException e) {

e.printStackTrace();

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

} catch (InvalidKeySpecException e) {

e.printStackTrace();

}

//Cerramos los flujos creados y la conexión a

la bbdd

if (rs!=null){

try {

rs.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

if (stmt!=null){

try {

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

109 Apertura de puertas usando Códigos QR QR Door Opener

if (conexion!=null){

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

//Y devolvemos el bean creado

return bean;

}

/*inserta nuevo usuario en la BBDD si no existÃa ya

* comprueba el DNI (clave principal), el nombre de usuario

y el telefono,

* los tres valores que deben ser únicos

*/

public static boolean InsertarNuevoUsuario (UserBean user){

boolean devuelto=false;

String DNI=user.getDNI();

String username=user.getUsername();

String telefono=user.getTelefono();

String password=user.getPassword();

String email=user.getEmail();

long ultimoacceso=user.getUltimoAcceso();

Connection conexion;

try {

//creamos un hash a partir del password

String hash=PasswordHash.createHash(password);

//creamos una nueva conexión a la BBDD

conexion=ConectarServidorSQL();

if (conexion != null){

String sqlStringComprueba="SELECT * FROM Usuario

WHERE Usuario.idUsuario=? OR Usuario.User=? OR

Usuario.Telefono=?";

PreparedStatement stmt;

stmt =

conexion.prepareStatement(sqlStringComprueba);

stmt.setString(1, DNI);

stmt.setString(2, username);

stmt.setString(3, telefono);

//Comprobamos si en la BBDD existÃa ya algún

usuario con el mismo DNI o mismo Username

ResultSet rs=stmt.executeQuery();

Anexo I: Código del Programa

110

110

//Si existÃa, devolvemos false, indicando que no

se ha podido insertar el usuario en la BBDD

if(rs.next()){

devuelto=false;

}else{

//Si no, lo insertaos

String sqlStringInserta="INSERT INTO

Usuario (idUsuario, Nombre, Telefono, User, Password, Email,

Intentos, UltimoAcceso) VALUES (?,?,?,?,?,?,?,?)";

PreparedStatement

stmt2=conexion.prepareStatement(sqlStringInserta);

stmt2.setString(1, DNI);

stmt2.setString(2, user.getNombre());

stmt2.setString(3, telefono);

stmt2.setString(4, username);

stmt2.setString(5, hash);

stmt2.setString(6, email);

stmt2.setInt(7,0);

stmt2.setLong(8, ultimoacceso);

stmt2.executeUpdate();

stmt2.close();

devuelto=true;

}

//Cerramos los flujos de la base de datos

rs.close();

stmt.close();

}

//cerramos la conexión a la base de datos

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

} catch (NoSuchAlgorithmException e1) {

e1.printStackTrace();

} catch (InvalidKeySpecException e1) {

e1.printStackTrace();

}

return devuelto;

}

//Este método inserta los datos de nuevo código QR en la

BBDD.

//Devuelve el String a insertar en el código QR en sí.

//por supuesto, comprueba que el usuario pueda pedir un QR

para esa casa.

public static String InsertaQR(Connection conexion,

String userId, int idCasa, long minutosvalidez, String

dniacceso){

String cadenadevuelta=null;

111 Apertura de puertas usando Códigos QR QR Door Opener

int idQR=0;

if (conexion != null){

String sqlStringPermisos="SELECT * FROM

Permisos WHERE Permisos.idUsuario=? and Permisos.idCasa=?";

try {

PreparedStatement

stmt=conexion.prepareStatement(sqlStringPermisos);

stmt.setString(1, userId);

stmt.setInt(2, idCasa); //añadimos

los valores al statement

//Extraemos los permisos del usuario en la

casa

ResultSet rs=stmt.executeQuery();

if(rs.next()){ //si hay

coincidencia

//Obtenemos la fecha de

expedición (actual) y la fecha de expiración

Date expedicion= new

Date();

long

fechaexpedicion=expedicion.getTime();

long

fechaexpiracion=fechaexpedicion+minutosvalidez;

String

sqlStringInsertaQR="INSERT INTO QRCodes (idUsuario, idCasa,

FechaExpedicion, FechaValidez, idUsuarioAccede) VALUES

(?,?,?,?,?)";

PreparedStatement

stmt2=conexion.prepareStatement(sqlStringInsertaQR);

//damos los valores a los

campos en stmt2

stmt2.setString(1,

userId);

stmt2.setInt(2, idCasa);

stmt2.setLong(3,

fechaexpedicion);

stmt2.setLong(4,

fechaexpiracion);

stmt2.setString(5,

dniacceso);

//Insertamos el nuevo QR

en la BBDD

stmt2.executeUpdate();

stmt2.close();

Anexo I: Código del Programa

112

112

String

sqlStringcompruebaQR="SELECT idQR FROM QRCodes WHERE

QRCodes.idUsuario=? and QRCodes.idCasa=? and

QRCodes.FechaExpedicion=? and QRCodes.FechaValidez=? and

QRCodes.idUsuarioAccede=?";

PreparedStatement

stmt3=conexion.prepareStatement(sqlStringcompruebaQR);

//damos valores a los

campos de stmt3

stmt3.setString(1,

userId);

stmt3.setInt(2, idCasa);

stmt3.setLong(3,

fechaexpedicion);

stmt3.setLong(4,

fechaexpiracion);

stmt3.setString(5,

dniacceso);

ResultSet

rs2=stmt3.executeQuery();

//Comprobamos si se ha

grabado bien e imprimimos el QR

if (rs2.next()){

idQR=rs2.getInt("idQR");

cadenadevuelta="IdQR:" + Integer.toString(idQR) +"

IdUser:"+userId+ " IdUseraccede:"+dniacceso+"

IdCasa:"+Integer.toString(idCasa)+" FechaExpedicion:"+

Long.toString(fechaexpedicion)+"

FechaExpiracion:"+Long.toString(fechaexpedicion+minutosvalidez);

}

rs2.close();

stmt3.close();

}

rs.close();

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return cadenadevuelta;

}

//Método que genera el códigoQR gráfico a partir de los

datos que queremos insertarle

public static boolean GeneraQR(String cadenaQR, String DNI,

String PATH_WEB) {

113 Apertura de puertas usando Códigos QR QR Door Opener

final String FORMATO_IMAGEN = "jpeg";

String RUTA_IMAGEN = null;

final int ancho = 500;

final int alto = 500;

BitMatrix bm;

Writer writer = new QRCodeWriter();

boolean generado=false;

//PATH donde se guarda el código QR

RUTA_IMAGEN = PATH_WEB+ "/qr"+DNI+ ".jpeg";

try {

//codificamos el la cadena QR en formato

QR

bm = writer.encode(cadenaQR,

BarcodeFormat.QR_CODE, ancho, alto);

BufferedImage image = new

BufferedImage(ancho, alto, BufferedImage.TYPE_INT_RGB);

//Creamos el código QR gráfico

for (int y = 0; y < ancho; y++) {

for (int x = 0; x < alto; x++) {

int grayValue = (bm.get(x, y) ?

1 : 0) & 0xff;

image.setRGB(x, y, (grayValue ==

0 ? 0 : 0xFFFFFF));

}

}

//el mÉtodo de generación del gráfico QR

de ZXING tiene un fallo

//hay que invertir los colores

image = invertirColores(image);

FileOutputStream qrCode;

//creamos el output stream

qrCode = new

FileOutputStream(RUTA_IMAGEN);

//y copiamos la imagen

ImageIO.write(image, FORMATO_IMAGEN,

qrCode);

qrCode.close();

generado=true;

} catch (IOException e) {

e.printStackTrace();

} catch (WriterException e) {

e.printStackTrace();

}

return generado;

Anexo I: Código del Programa

114

114

}

//Función para invertir colores, ya que la librería ZXING

da el código QR invertido

public static BufferedImage

invertirColores(BufferedImage image) {

final int ancho = 500;

final int alto = 500;

for (int x = 0; x < ancho; x++) {

for (int y = 0; y < alto; y++) {

int rgb = image.getRGB(x, y);

if (rgb == -16777216) {

image.setRGB(x, y, -1);

} else {

image.setRGB(x, y, -16777216);

}

}

}

return image;

}

/*Extrae de la BBDD el DNI a partir del username y

* lo devuelve para poder usarlo

*/

public static String ExtraerDNI(Connection conexion,

String username) {

String DNI=null;

if (conexion != null){

String sqlStringExtrae="SELECT idUsuario

FROM Usuario WHERE User=?";

PreparedStatement stmt;

try {

stmt =

conexion.prepareStatement(sqlStringExtrae);

stmt.setString(1,username);

//seleccionamos el campo de usuario

correspondiente al username

ResultSet res = stmt.executeQuery();

//obtenemos la informacion

while(res.next()){

DNI =

res.getString("idUsuario");

}

res.close();

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

115 Apertura de puertas usando Códigos QR QR Door Opener

}

//Devolvemos el DNI

return DNI;

}

/*Este mÉtodo extrae todas las casas de un usuario de la BBDD

*Las devuelve en forma de ArrayList

*/

public static ArrayList<CasaBean>

ExtraeCasas(Connection conexion, String DNI) {

int i=0;

ArrayList<CasaBean> casas=null;

ArrayList<Integer> codigosCasas=new

ArrayList<Integer>();

if (conexion != null){

String sqlStringExtraeCasas="SELECT idCasa

FROM Permisos WHERE idUsuario=? AND Valido=1";

PreparedStatement stmt;

try {

stmt =

conexion.prepareStatement(sqlStringExtraeCasas);

stmt.setString(1,DNI);

//Extraemos todos los identificadores

de casa de los permisos, cuando coincidan con el id de usuario

ResultSet res = stmt.executeQuery();

//obtenemos la informacion

while(res.next()){

i++; //mientras haya casas, se

añade al array de códigos de casas

codigosCasas.add(res.getInt("idCasa"));

}

res.close();

stmt.close();

if(i!=0){ //si el usuario tiene

alguna casa

casas=new ArrayList<CasaBean>();

int casaactual;

for(int j=0;j<i;j++){ //vamos

recorriendo el bucle y rellenando el array de casas

CasaBean casarellena=new

CasaBean();

casaactual=codigosCasas.get(j);

String

sqlStringExtraeCasaActual="SELECT * FROM Casas WHERE idCasa=?";

Anexo I: Código del Programa

116

116

PreparedStatement stmt2 =

conexion.prepareStatement(sqlStringExtraeCasaActual);

stmt2.setInt(1,

casaactual);

//extraemos los datos de

la casa

ResultSet res2 =

stmt2.executeQuery();

while(res2.next()){

casarellena.setIdCasa(res2.getInt("idCasa"));

casarellena.setCP(res2.getString("Codigo_Postal"));

casarellena.setPoblacion(res2.getString("Poblacion"));

casarellena.setDireccion(res2.getString("Direccion"));

casas.add(j,casarellena); //y añadimos la casa al array en

la posición que toque

}

res2.close();

stmt2.close();

}

}

} catch (SQLException e) {

e.printStackTrace();

}

}

return casas;

}

/*MÉtodo que elimina el enlace entre la casa y el usuario de la

base de datos

* Si el usuario era administrador, se borran todos los enlaces

de los usuarios que tuvieran

* acceso a dicha casa.

*/

public static boolean EliminarCasa(int idCasa, String

DNI, Connection conexion) {

boolean eliminada=false;

int permisoselim=0;

boolean admin=false;

if (conexion != null){

PreparedStatement stmt;

String sqlStringadmin="SELECT

Administrador FROM Permisos WHERE idUsuario=?";

try {

stmt =

conexion.prepareStatement(sqlStringadmin);

117 Apertura de puertas usando Códigos QR QR Door Opener

stmt.setString(1, DNI);

ResultSet rs=stmt.executeQuery();

if(rs.next()){

admin=rs.getBoolean("Administrador");

System.out.println("Admin

extract: "+admin);

if(admin==false){ //si no es el

admin, solo se borra el permiso de ese usuario

PreparedStatement stmt2;

//preparamos es Statement

String

sqlStringNoaccesoSec="UPDATE Permisos SET Valido=0 WHERE

idCasa=? and idUsuario=?";

stmt2=conexion.prepareStatement(sqlStringNoaccesoSec);

stmt2.setInt(1, idCasa);

stmt2.setString(2, DNI);

permisoselim=stmt.executeUpdate();//cambiamos el permiso

del usuario

stmt2.close(); //cerramos

el statement

}else{ //si es el admin, se

actualizan todos los permisos para que todos sean inválidos

PreparedStatement stmt2;

//preparamos el statement

String

sqlStringBorraPerms="UPDATE Permisos SET Valido=0 WHERE

idCasa=?";

stmt2=conexion.prepareStatement(sqlStringBorraPerms);

stmt2.setInt(1, idCasa);

permisoselim=stmt2.executeUpdate();//borramos de tabla

permisos

stmt2.close(); //y

cerramos los statements

}

}

rs.close();

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

if(permisoselim!=0){

Anexo I: Código del Programa

118

118

eliminada=true; //si se ha borrado el

estado de validez, se toma como casa eliminada

}

}

return eliminada;

}

/*MÉtodo que inserta una nueva casa para el usuario

en la BBDD.

* Devuelve un entero para saber su ha habido la casa

existÃa o no.

* Si una casa ya existe, se inserta como usuario

secundario

* si no existe, se inserta como administrador

*/

public static int InsertarCasa(Connection conexion,

String userid, String direccion, String poblacion, String

codigopostal){

int idcasa=0;

int devuelto=0;

int usuarioexiste=0;

if (conexion != null){

PreparedStatement stmt;

try {

String sqlStringCompCasa="SELECT

idCasa FROM Casas WHERE Casas.Direccion=? and +Casas.Poblacion=?

and + Casas.Codigo_Postal=?";

stmt =

conexion.prepareStatement(sqlStringCompCasa);

stmt.setString(1, direccion);

stmt.setString(2, poblacion);

stmt.setString(3, codigopostal);

//Comprobamos que la casa no

estuviera insertada anteriormente

ResultSet rs=stmt.executeQuery();

if(rs.next()){ //si la casa existÃa,

comprobamos si el usuario tenia ya esa casa anteriormente

idcasa=rs.getInt("idCasa"); //se

tiene el id de la casa

String

sqlStringCompruebaUsuario="SELECT * FROM Permisos WHERE idCasa=?

AND idUsuario=?";

PreparedStatement stmt2;

stmt2 =

conexion.prepareStatement(sqlStringCompruebaUsuario);

stmt2.setInt(1, idcasa);

stmt2.setString(2, userid);

ResultSet

rs2=stmt2.executeQuery();

119 Apertura de puertas usando Códigos QR QR Door Opener

if(rs2.next()){ //si el usuario

existÃa ya, se pone la bandera a 1

usuarioexiste=1;

}

stmt2.close();

rs2.close();

//procedemos a comprobar ahora

si en esa casa habÃa administrador valido

String

sqlStringCompruebaAdmin="SELECT * FROM Permisos WHERE idCasa=?

AND Administrador=1 AND Valido=1";

PreparedStatement stmt3;

stmt3 =

conexion.prepareStatement(sqlStringCompruebaAdmin);

stmt3.setInt(1, idcasa);

ResultSet

rs3=stmt3.executeQuery();

if(rs3.next()){ //si hay

administrador valido, significa que hay que insertar usuario

secundario

devuelto=1; //casa existÃ-

a, se añade para el usuario autorizado

if(usuarioexiste==0){//si

el usuario no tenÃa esa casa antes, se añade el permiso

String

sqlStringSecundario="INSERT INTO Permisos(idUsuario, idCasa,

Valido, Administrador) VALUES (?,?,?,?)";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringSecundario);

stmt4.setString(1,

userid);

stmt4.setInt(2,

idcasa);

stmt4.setInt(3, 1);

stmt4.setInt(4, 0);

stmt4.executeUpdate();

stmt4.close();

}else{ //si ese usuario

tenÃa la casa antes, se cambia el valor del campo válido

System.out.println("en insertar secundario");

String

sqlStringSecundario="UPDATE Permisos SET Valido=1,

Administrador=0 WHERE idCasa=? AND idUsuario=?";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringSecundario);

Anexo I: Código del Programa

120

120

stmt4.setInt(1,

idcasa);

stmt4.setString(2,

userid);

stmt4.executeUpdate();

stmt4.close();

}

}else{ //si la casa existe pero

no hay administrador, es decir, la casa ha cambiado de

propietario

devuelto=2;

if(usuarioexiste==0){

String

sqlStringAdminNuevo="INSERT INTO Permisos(idUsuario, idCasa,

Valido, Administrador) VALUES (?,?,?,?)";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringAdminNuevo);

stmt4.setString(1,

userid);

stmt4.setInt(2,

idcasa);

stmt4.setInt(3, 1);

stmt4.setInt(4, 1);

stmt4.executeUpdate();

stmt4.close();

}else{

String

sqlStringSecundario="UPDATE Permisos SET Valido=1,

Administrador=1 WHERE idCasa=? AND idUsuario=?";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringSecundario);

stmt4.setInt(1,

idcasa);

stmt4.setString(2,

userid);

stmt4.executeUpdate();

stmt4.close();

}

}

stmt3.close();

121 Apertura de puertas usando Códigos QR QR Door Opener

}else{

//si no, la insertamos en la

BBDD

//tanto en la tabla de casas

como en la de permisos

devuelto=2;

String

sqlStringInsertCasa="INSERT INTO Casas (Direccion, Poblacion,

Codigo_Postal) VALUES (?,?,?)";

PreparedStatement

stmt2=conexion.prepareStatement(sqlStringInsertCasa);

stmt2.setString(1, direccion);

stmt2.setString(2, poblacion);

stmt2.setString(3,

codigopostal);

stmt2.executeUpdate();

//comprobamos si ha sido

insertado correctamente

String

sqlStringCompCasaIns="SELECT idCasa FROM Casas WHERE

Casas.Direccion=? and +Casas.Poblacion=? and +

Casas.Codigo_Postal=?";

PreparedStatement

stmt3=conexion.prepareStatement(sqlStringCompCasaIns);

stmt3.setString(1, direccion);

stmt3.setString(2, poblacion);

stmt3.setString(3,

codigopostal);

ResultSet

rs2=stmt3.executeQuery();

if (rs2.next()){ //si la casa se

ha insertado, lo hacemos tambiÉn en la tabla de permisos

idcasa=rs2.getInt("idCasa");

String

sqlStringInsertPermiso="INSERT INTO Permisos(idUsuario, idCasa,

Valido, Administrador) VALUES (?,?,?,?)";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringInsertPermiso);

stmt4.setString(1,

userid);

stmt4.setInt(2, idcasa);

stmt4.setInt(3, 1); //1

implica que el usuario es valido

stmt4.setInt(4, 1); //1

implica que el usuario es administrador

stmt4.executeUpdate();

stmt4.close();

}

stmt2.close();

Anexo I: Código del Programa

122

122

stmt3.close();

rs2.close();

}

rs.close();

stmt.close();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

return devuelto;

}

/*Este mÉtodo modifica el usuario en la BBDD.

* Devuelve un boolean que indica si la modificación ha sido

correcta o no.

*

*/

public static boolean ModificaUsuario(UserBean

usuariomodificado,

Connection conexion) {

boolean modificado=false;

if (conexion != null){

PreparedStatement stmt;

try {

String sqlStringModUsuario="UPDATE

Usuario SET Nombre=?,Telefono=?,User=?, Email=? WHERE

idUsuario=?";

stmt =

conexion.prepareStatement(sqlStringModUsuario);

stmt.setString(1,

usuariomodificado.getNombre());

stmt.setString(2,

usuariomodificado.getTelefono());

stmt.setString(3,usuariomodificado.getUsername());

stmt.setString(4,

usuariomodificado.getEmail());

stmt.setString(5,

usuariomodificado.getDNI());

//Intentamos realizar los cambios

solicitados

int cambios=stmt.executeUpdate();

if (cambios!=0){ //si ha habido

cambios

modificado=true;

123 Apertura de puertas usando Códigos QR QR Door Opener

}

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return modificado;

}

/*Este mÉtodo modifica la contraseña del usuario

pasado con DNI

* devuelve un boolean indicando si la modificación

ha sido realizada correctamente.

*/

public static boolean ModificaContrasenya(String DNI,

String contrasenyanueva, Connection

conexion) {

boolean modificado=false;

if (conexion != null){

PreparedStatement stmt;

try {

//Creamos el hash de la contraseña

nueva

String

hash=PasswordHash.createHash(contrasenyanueva);

String sqlModContrasenya="UPDATE

Usuario SET Password=? WHERE idUsuario=?";

stmt =

conexion.prepareStatement(sqlModContrasenya);

stmt.setString(1, hash);

stmt.setString(2, DNI);

//Realizamos el update

int cambios=stmt.executeUpdate();

if (cambios!=0){ //si se ha

modificado la contraseña

modificado=true;

}

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

} catch (InvalidKeySpecException e) {

e.printStackTrace();

}

}

Anexo I: Código del Programa

124

124

return modificado;

}

/*Este mÉtodo extrae todas las estadÃsticas de la

BBDD para presentarla

* Devuelve un ArrayList con todas las estadÃsticas

*/

public static ArrayList<EstadisticaBean>

ExtraeEstadisticas(

Connection conexion, String dni) {

ArrayList<EstadisticaBean> estadisticas=new

ArrayList<EstadisticaBean>();

if (conexion != null){

PreparedStatement stmt;

try {

EstadisticaBean estactual=null;

String

sqlExtraeEstadistica="SELECT * FROM EstadisticaAcceso WHERE

idUsuario=? ";

stmt =

conexion.prepareStatement(sqlExtraeEstadistica);

stmt.setString(1, dni);

//Extraemos de la BBDD todas las

estadÃsticas de un usuario

ResultSet res =

stmt.executeQuery(); //obtenemos la informacion

while(res.next()){

//Vamos insertando las

estadÃÅ›ticas en el array de estadÃsticas

estactual=new

EstadisticaBean();

estactual.setError(res.getInt("Tipo_de_Error"));

int

idQR=(res.getInt("idQR"));

estactual.setIdQR(idQR);

estactual.setFechaacceso(res.getLong("FechaAcceso"));

int

idCasa=res.getInt("idCasa");

String

sqlStringNombre="SELECT Nombre FROM Usuario WHERE idUsuario=?";

125 Apertura de puertas usando Códigos QR QR Door Opener

PreparedStatement

stmt2=conexion.prepareStatement(sqlStringNombre);

stmt2.setString(1, dni);

//Extraemos datos de

usuario y los insertamos tambiÉn

ResultSet res2 =

stmt2.executeQuery();

if(res2.next()){

String

nombre=res2.getString("Nombre");

estactual.setUsuario(nombre);

String

sqlStringExtraeCasa="SELECT * FROM Casas WHERE idCasa=?";

PreparedStatement

stmt3=conexion.prepareStatement(sqlStringExtraeCasa);

stmt3.setInt(1,

idCasa);

//y lo mismo con los

datos de la casa

ResultSet

res3=stmt3.executeQuery();

if(res3.next()){

String

direccion=res3.getString("Direccion");

String

poblacion=res3.getString("Poblacion");

estactual.setDireccion(direccion);

estactual.setPoblacion(poblacion);

String

sqlStringExtraeQR="SELECT * FROM QRCodes WHERE idQR=?";

PreparedStatement

stmt4=conexion.prepareStatement(sqlStringExtraeQR);

stmt4.setInt(1,

idQR);

//y finalmente

con los datos del QR usado

ResultSet

res4=stmt4.executeQuery();

if(res4.next()){

long

fechaexped=res4.getLong("FechaExpedicion");

long

fechaexpir=res4.getLong("FechaValidez");

String

dniAccede=res4.getString("idUsuarioAccede");

Anexo I: Código del Programa

126

126

estactual.setFechaexpedicion(fechaexped);

estactual.setFechaexpiracion(fechaexpir);

String

sqlStringExtraeNombreAccede="SELECT Nombre FROM Usuario WHERE

idUsuario=?";

PreparedStatement

stmt5=conexion.prepareStatement(sqlStringExtraeNombreAccede);

stmt5.setString(1, dniAccede);

ResultSet

res5 = stmt5.executeQuery();

if(res5.next()){

String nombreaccede=res5.getString("Nombre");

estactual.setUsuarioacceso(nombreaccede);

}else{

estadisticas=null;

}

res5.close();

stmt5.close();

}else{

estadisticas=null;

}

res4.close();

stmt4.close();

}else{

estadisticas=null;

}

res3.close();

stmt3.close();

}else{

estadisticas=null;

}

stmt2.close();

res2.close();

if(estadisticas!=null){

//si hay fallo en cualquiera de las lecturas a la bbdd, no se

copia la estadÃstica actual

127 Apertura de puertas usando Códigos QR QR Door Opener

estadisticas.add(estactual);

}

}

res.close();

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return estadisticas;

}

/*mÉtodo que envÃa un correo al administrador

pidiendo

* un certificado para el usuario.

* Devuelve un boolean que indica si se ha enviado

correctamente

* Basado en

http://stackoverflow.com/questions/3649014/send-email-using-java

* Cheok Yan Cheng

*/

static boolean enviarmail(UserBean usuario, CasaBean casa){

boolean enviado=false;

// Recipient's email ID needs to be mentioned.

String to = "[email protected]";

// Sender's email ID needs to be mentioned

String from = "[email protected]";

// Assuming you are sending email from localhost

final String SSL_FACTORY =

"javax.net.ssl.SSLSocketFactory";

// Get a Properties object

Properties props = System.getProperties();

props.setProperty("mail.smtps.host",

"smtp.gmail.com");

props.setProperty("mail.smtp.socketFactory.class",

SSL_FACTORY);

props.setProperty("mail.smtp.socketFactory.fallback",

"false");

props.setProperty("mail.smtp.port", "465");

props.setProperty("mail.smtp.socketFactory.port",

"465");

props.setProperty("mail.smtps.auth", "true");

props.put("mail.smtps.quitwait", "false");

// Get the default Session object.

Session session = Session.getDefaultInstance(props);

try{

Anexo I: Código del Programa

128

128

// Create a default MimeMessage object.

MimeMessage message = new MimeMessage(session);

// Set From: header field of the header.

message.setFrom(new InternetAddress(from));

// Set To: header field of the header.

message.addRecipient(Message.RecipientType.TO,

new InternetAddress(to));

// Set Subject: header field

message.setSubject("Petición certificado

"+usuario.getDNI());

// Send the actual HTML message, as big as you

like

message.setContent("<h1>Crear Certificado</h1> " +

"El usuario "+usuario.getNombre()+" con

DNI "+usuario.getDNI()+

" desea solicitar un certificado digital

para su domicilio "+casa.getDireccion()+ " "+

casa.getPoblacion()+ " "+casa.getCP()+".

Por favor, genÉrela y envÃe un mail con " +

"el certificado a la dirección

"+usuario.getEmail(),

"text/html" );

// Send message

SMTPTransport t =

(SMTPTransport)session.getTransport("smtps");

t.connect("smtp.gmail.com", "webqrcccpfc",

"CCCPFC2013" );

t.sendMessage(message,

message.getAllRecipients());

t.close();

enviado=true;

}catch (MessagingException mex) {

mex.printStackTrace();

}

return enviado;

}

/*Este mÉtodo comprueba si un usuario se encuentra en la base de

datos a partir de su DNI

* Devuelve un boolean que indica si existe o no

*/

public static boolean compruebaUsuario(String dNIacceso,

Connection conexion) {

boolean devuelto=false;

129 Apertura de puertas usando Códigos QR QR Door Opener

if (conexion != null){

PreparedStatement stmt;

try {

String sqlStringBuscaUsuario="SELECT

* FROM Usuario WHERE idUsuario=?";

stmt =

conexion.prepareStatement(sqlStringBuscaUsuario);

stmt.setString(1, dNIacceso);

//Comprobamos si el usuario existe en

la BBDD

ResultSet res = stmt.executeQuery();

//obtenemos la informacion

if(res.next()){

devuelto=true; //hay usuario

}

res.close(); //cerramos flujos

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return devuelto;

}

//este mÉtodo extrae ID de la casa a partir de la direccion

completa

//devuelve un entero cuyo valor es dicho identificador

public static int ExtraerIdCasa(String direccion, String

CP, String Poblacion, Connection conexion){

int idCasa=0;

if (conexion != null){

PreparedStatement stmt;

try {

String sqlStringBuscaCasa="SELECT *

FROM Casas WHERE Direccion=?, Poblacion=?, Codigo_Postal=?";

stmt =

conexion.prepareStatement(sqlStringBuscaCasa);

stmt.setString(1, direccion);

stmt.setString(2, Poblacion);

stmt.setString(3, CP);

//Comprobamos si la casa existe en la

BBDD

ResultSet res = stmt.executeQuery();

//obtenemos la informacion

if(res.next()){

idCasa=res.getInt("idCasa");

}

Anexo I: Código del Programa

130

130

res.close(); //cerramos flujos

stmt.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

return idCasa;

}

/*Este mÉtodo modifica la fecha de ultimo acceso del usuario con

el DNI pasado como parámetro.

*/

public static int ModificaUltimoAcceso(String DNI) {

Connection conexion;

int modificado=0;

try {

//creamos una nueva conexión a la BBDD

conexion=ConectarServidorSQL();

if (conexion != null){

String sqlStringComprueba="SELECT * FROM Usuario

WHERE Usuario.idUsuario=?";

PreparedStatement stmt;

stmt =

conexion.prepareStatement(sqlStringComprueba);

stmt.setString(1, DNI);

//Comprobamos si en la BBDD existÃa ya algún

usuario con el mismo DNI o mismo Username

ResultSet rs=stmt.executeQuery();

//Si existÃa, devolvemos false, indicando que no

se ha podido insertar el usuario en la BBDD

if(rs.next()){

Date fechaactual=new Date();

String sqlStringActualizaAcceso="UPDATE

Usuario SET UltimoAcceso=? WHERE idUsuario=?";

PreparedStatement stmt2;

stmt2=conexion.prepareStatement(sqlStringActualizaAcceso);

stmt2.setLong(1, fechaactual.getTime());

stmt2.setString(2, DNI);

int cambios=stmt2.executeUpdate();

if (cambios!=0){

modificado=1;

}

stmt2.close();

}

131 Apertura de puertas usando Códigos QR QR Door Opener

rs.close();

stmt.close();

}

conexion.close();

}catch (SQLException e) {

e.printStackTrace();

}

return modificado;

}

}

9.3.1.7 GenerarQRServlet.java

/*Servlet para la generación del código QR

* Usa post para más seguridad de la información

*/

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/GenerarQRServlet")

public class GenerarQRServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public GenerarQRServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

Anexo I: Código del Programa

132

132

//Obtenemos los datos de la sesión, para extraer el

usuario y la casa

HttpSession session=request.getSession();

Connection conexion=

FuncionesWeb.ConectarServidorSQL();

UserBean user=

(UserBean)session.getAttribute("currentSessionUser");

//obtenemos el usuario de sesión

CasaBean casa=

(CasaBean)session.getAttribute("Casa");

String username=user.getUsername();

String DNI=FuncionesWeb.ExtraerDNI(conexion,

username);

String

tipo_de_tiempo=request.getParameter("selecciontiempo");

String tiempo=request.getParameter("Time");

String

DNIacceso=request.getParameter("UsuarioAcceso");

int IdCasa=casa.getIdCasa();

//path donde se va a guardar el código QR en el

servidor Web

String

PATH="/usr/share/tomcat7/webapps/WebPFC/images/qr";

//String PATH="/home/usuario";

long tiempovalidez=0;

if (tipo_de_tiempo.compareTo("Minuto")==0){

tiempovalidez=Long.parseLong(tiempo)*60*1000;

}else if(tipo_de_tiempo.compareTo("Hora")==0){

tiempovalidez=Long.parseLong(tiempo)*3600*1000;

}else if(tipo_de_tiempo.compareTo("Dia")==0){

tiempovalidez=Long.parseLong(tiempo)*24*3600*1000;

}else{

System.out.println("ERROR");

}

//Comprobamos si el usuario que accede está en el

sistema

boolean

usuarioacc=FuncionesWeb.compruebaUsuario(DNIacceso, conexion);

if (usuarioacc==true){

//Insertamos el QR en la BBDD

String

cadenaQR=FuncionesWeb.InsertaQR(conexion,DNI,IdCasa,tiempovalide

z, DNIacceso);

if(cadenaQR!=null){

boolean

generado=FuncionesWeb.GeneraQR(cadenaQR, DNI,PATH);

if(generado==true){

133 Apertura de puertas usando Códigos QR QR Door Opener

response.sendRedirect("QRgenerado.jsp"); //Mostrar QR page

}else{

response.sendRedirect("QRfallo.jsp");

//Fallo QR page

}

}else{

response.sendRedirect("QRfallo.jsp");

//Fallo QR page

}

}else{

response.sendRedirect("UsuarioQRInvalido.jsp");

}

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

9.3.1.8 InsertarCasaServlet.java

/*Servlet para insertar casa en la BBDD

*

*/

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/InsertarCasaServlet")

public class InsertarCasaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public InsertarCasaServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

Anexo I: Código del Programa

134

134

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

Connection conexion=

FuncionesWeb.ConectarServidorSQL();

//obtenemmos los datos del usuario de la sesión

HttpSession session= request.getSession();

UserBean

usuario=(UserBean)session.getAttribute("currentSessionUser");

//Intentamos insertar la casa en la BBDD

int insertada=FuncionesWeb.InsertarCasa(conexion,

usuario.getDNI(), request.getParameter("Direccion"),

request.getParameter("Poblacion"), request.getParameter("CP"));

if (insertada==0){ //si ha habido algún problema

response.sendRedirect("Error.jsp"); //casa

existente.jsp

}else if(insertada==1){ //si existÃa y se ha

insertado un usuario autorizado

response.sendRedirect("VerCasaServlet");

}else if(insertada==2){ //si no existía y se acaba de

crear, se genera el certificado digital

int

idCasa=FuncionesWeb.ExtraerIdCasa(request.getParameter("Direccio

n"), request.getParameter("Poblacion"),

request.getParameter("CP"), conexion);

CasaBean casa=new CasaBean(); //Creamos la nueva

casa

casa.setIdCasa(idCasa);

casa.setDireccion(request.getParameter("Direccion"));

casa.setCP(request.getParameter("CP"));

casa.setPoblacion(request.getParameter("Poblacion"));

session.setAttribute("Casa", casa);

response.sendRedirect("CrearCertificado.jsp");

//Crear certificado page

}

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

135 Apertura de puertas usando Códigos QR QR Door Opener

9.3.1.9 LoginServlet.java

/*Servlet para el login de usuarios

*

*/

package PFCWebPackage;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/LoginServlet")

public class LoginServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public LoginServlet() {

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

UserBean user= new UserBean();

//Obtenemos el usuario y password del request

user.setUserName(request.getParameter("User"));

user.setPassword(request.getParameter("Password"));

//Comprobamos los datos en la BBDD

user = FuncionesWeb.login(user);

if (user.isValid()==true) { //si usuario válido

HttpSession session = request.getSession(true);

//creamos la sesión y guardamos los datos de usuario en ella

session.setAttribute("currentSessionUser",user);

int

ultimoaccesomodificado=FuncionesWeb.ModificaUltimoAcceso(user.ge

tDNI());

Anexo I: Código del Programa

136

136

if (ultimoaccesomodificado!=0){

response.sendRedirect("userLogged.jsp");

//logged-in page

} else {

response.sendRedirect("Error.jsp");

}

}else{

response.sendRedirect("invalidLogin.jsp");

//error page

}

}

}

9.3.1.10 LogoutServlet.java

/*Servlet que se encarga de hacer el Logout de los usuarios

*

*/

package PFCWebPackage;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/LogoutServlet")

public class LogoutServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public LogoutServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

HttpSession session=request.getSession();

session.invalidate(); //invalidamos la sesión

response.sendRedirect("index.jsp"); //reenviamos a la

página inicial

}

137 Apertura de puertas usando Códigos QR QR Door Opener

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

}

9.3.1.11 modificarContrasenyaServlet.java

/*Servlet de modificación de la contraseña

*

*/

package PFCWebPackage;

import java.io.IOException;

import java.security.NoSuchAlgorithmException;

import java.security.spec.InvalidKeySpecException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/modificarContrasenyaServlet")

public class modificarContrasenyaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public modificarContrasenyaServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

HttpSession session=request.getSession();

//extraemos la sesión para obtener usuario y contraseña

UserBean usuarioactual=new UserBean();

Anexo I: Código del Programa

138

138

usuarioactual=(UserBean)session.getAttribute("currentSessio

nUser");

String contrasenyaactual=usuarioactual.getPassword();

//realmente es un hash

String

contrasenyaactual2=request.getParameter("Contrasenyaactual");

//obtenemos la contraseña actual

String

contrasenyanueva=request.getParameter("Contrasenyanueva"); //y

dos veces la nueva para confirmarla

String

contrasenyanueva2=request.getParameter("Contrasenyanueva2");

try {

if(PasswordHash.validatePassword(contrasenyaactual2,

contrasenyaactual)){ //validamos la contraseña actual

if(contrasenyanueva.compareTo(contrasenyanueva2)==0){

//comprobamos si las contraseñas nuevas coinciden

String DNI=usuarioactual.getDNI();

Connection

conexion=FuncionesWeb.ConectarServidorSQL(); //si coinciden,

modificamos la contraseñas

boolean

modificada=FuncionesWeb.ModificaContrasenya(DNI,contrasenyanueva

,conexion);

if(modificada==true){ //si se ha

modificado, volvemos a la pantalla de inicio

usuarioactual.setPassword(contrasenyanueva);

session.setAttribute("currentSessionUser", usuarioactual);

response.sendRedirect("userLogged.jsp");

}else{ //si no, vamos a la página de

error

System.out.println("Problema

desconocido");

response.sendRedirect("Error.jsp");

}

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

}else{

System.out.println("Error en

concordancia de contrasenyas");

response.sendRedirect("Error.jsp");

139 Apertura de puertas usando Códigos QR QR Door Opener

}

}else{

System.out.println("Error en concordancia

de contrasenyas");

response.sendRedirect("Error.jsp");

}

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

} catch (InvalidKeySpecException e) {

e.printStackTrace();

}

}

}

9.3.1.12 modificarUsuarioServlet.java

/*Servlet para la modificación de Usuario

*

*/

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

/**

* Servlet implementation class modificarUsuarioServlet

*/

@WebServlet("/modificarUsuarioServlet")

public class modificarUsuarioServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public modificarUsuarioServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

Anexo I: Código del Programa

140

140

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

HttpSession session=request.getSession(); //extraemos

la sesión para obtener el usuario

UserBean usuarioactual=new UserBean();

usuarioactual=(UserBean)session.getAttribute("currentSessio

nUser");

UserBean usuariomodificado=new UserBean();

usuariomodificado.setNombre(request.getParameter("Nombre"))

;

usuariomodificado.setTelefono(request.getParameter("Telefon

o"));

usuariomodificado.setUserName(request.getParameter("Usernam

e"));

usuariomodificado.setEmail(request.getParameter("Email"));

//si alguno de los tres campos no coincide,

actualizar

if((usuarioactual.getNombre().compareTo(usuariomodificado.g

etNombre())!=0)

||(usuarioactual.getTelefono().compareTo(usuariomodificado.

getTelefono())!=0)

||(usuarioactual.getUsername().compareTo(usuariomodificado.

getUsername())!=0)

||(usuarioactual.getEmail().compareTo(usuariomodificado.get

Email())!=0)){

usuariomodificado.setDNI(usuarioactual.getDNI());

usuariomodificado.setPassword(usuarioactual.getPassword());

usuariomodificado.setValid(true);

Connection

conexion=FuncionesWeb.ConectarServidorSQL();

//modificamos el usuario en la BBDD

boolean

modificado=FuncionesWeb.ModificaUsuario(usuariomodificado,

conexion);

if(modificado==true){

session.setAttribute("currentSessionUser",

usuariomodificado);

response.sendRedirect("ModificarDatos.jsp");

}else{

response.sendRedirect("Error.jsp");

}

141 Apertura de puertas usando Códigos QR QR Door Opener

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

}else{

response.sendRedirect("ModificarDatos.jsp");

}

}

}

9.3.1.13 NewUserServlet.java

/*Servlet para la inserción de nuevo usuario en la BBDD

*

*/

package PFCWebPackage;

import java.io.IOException;

import java.util.Date;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/NewUserServlet")

public class NewUserServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public NewUserServlet() {

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

UserBean user=new UserBean();

boolean insertado=false;

Anexo I: Código del Programa

142

142

//extraemos primeros las dos contraseñas para

compararlas

String password=request.getParameter("Password");

String password2=request.getParameter("Password2");

if (password.compareTo(password2)==0){ //si ambas

contraseñas son iguales, procedemos a insertar el usuario

//extraemos los datos del formulario

user.setNombre(request.getParameter("Nombre"));

user.setTelefono(request.getParameter("Telefono"));

user.setDNI(request.getParameter("Dni"));

user.setEmail(request.getParameter("Email"));

user.setPassword(password);

user.setUserName(request.getParameter("Usuario"));

Date fechaactual= new Date();

user.setUltimoAcceso(fechaactual.getTime());

//y lo insertamos en la BBDD

insertado=FuncionesWeb.InsertarNuevoUsuario(user);

if(insertado==false){ //si no ha podido

insertarse

response.sendRedirect("invalidNewUser.jsp"); //error page

}else{ //si se ha insertado correctamente

HttpSession session =

request.getSession(true);

session.setAttribute("currentSessionUser",user);

response.sendRedirect("userLogged.jsp");

//logged-in page

}

}else{

response.sendRedirect("invalidNewUser.jsp");

//error page

}

}

}

9.3.1.14 PasswordHash.java

/*Clase que trata los password con hash y salt

* Obtenido de

* PBKDF2 salted password hashing.

* Author: havoc AT defuse.ca

* www: http://crackstation.net/hashing-security.htm

*/

143 Apertura de puertas usando Códigos QR QR Door Opener

package PFCWebPackage;

import java.security.SecureRandom;

import javax.crypto.spec.PBEKeySpec;

import javax.crypto.SecretKeyFactory;

import java.math.BigInteger;

import java.security.NoSuchAlgorithmException;

import java.security.spec.InvalidKeySpecException;

public class PasswordHash

{

public static final String PBKDF2_ALGORITHM =

"PBKDF2WithHmacSHA1";

// The following constants may be changed without breaking

existing hashes.

public static final int SALT_BYTE_SIZE = 24;

public static final int HASH_BYTE_SIZE = 24;

public static final int PBKDF2_ITERATIONS = 1000;

public static final int ITERATION_INDEX = 0;

public static final int SALT_INDEX = 1;

public static final int PBKDF2_INDEX = 2;

/**

* Returns a salted PBKDF2 hash of the password.

*

* @param password the password to hash

* @return a salted PBKDF2 hash of the password

*/

public static String createHash(String password)

throws NoSuchAlgorithmException, InvalidKeySpecException

{

return createHash(password.toCharArray());

}

/**

* Returns a salted PBKDF2 hash of the password.

*

* @param password the password to hash

* @return a salted PBKDF2 hash of the password

*/

public static String createHash(char[] password)

throws NoSuchAlgorithmException, InvalidKeySpecException

{

// Generate a random salt

SecureRandom random = new SecureRandom();

byte[] salt = new byte[SALT_BYTE_SIZE];

random.nextBytes(salt);

// Hash the password

byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS,

HASH_BYTE_SIZE);

Anexo I: Código del Programa

144

144

// format iterations:salt:hash

return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" +

toHex(hash);

}

/**

* Validates a password using a hash.

*

* @param password the password to check

* @param correctHash the hash of the valid password

* @return true if the password is correct,

false if not

*/

public static boolean validatePassword(String password,

String correctHash)

throws NoSuchAlgorithmException, InvalidKeySpecException

{

return validatePassword(password.toCharArray(),

correctHash);

}

/**

* Validates a password using a hash.

*

* @param password the password to check

* @param correctHash the hash of the valid password

* @return true if the password is correct,

false if not

*/

public static boolean validatePassword(char[] password,

String correctHash)

throws NoSuchAlgorithmException, InvalidKeySpecException

{

// Decode the hash into its parameters

String[] params = correctHash.split(":");

int iterations =

Integer.parseInt(params[ITERATION_INDEX]);

byte[] salt = fromHex(params[SALT_INDEX]);

byte[] hash = fromHex(params[PBKDF2_INDEX]);

// Compute the hash of the provided password, using the

same salt,

// iteration count, and hash length

byte[] testHash = pbkdf2(password, salt, iterations,

hash.length);

// Compare the hashes in constant time. The password is

correct if

// both hashes match.

return slowEquals(hash, testHash);

}

/**

* Compares two byte arrays in length-constant time. This

comparison method

145 Apertura de puertas usando Códigos QR QR Door Opener

* is used so that password hashes cannot be extracted from

an on-line

* system using a timing attack and then attacked off-line.

*

* @param a the first byte array

* @param b the second byte array

* @return true if both byte arrays are the same,

false if not

*/

private static boolean slowEquals(byte[] a, byte[] b)

{

int diff = a.length ^ b.length;

for(int i = 0; i < a.length && i < b.length; i++)

diff |= a[i] ^ b[i];

return diff == 0;

}

/**

* Computes the PBKDF2 hash of a password.

*

* @param password the password to hash.

* @param salt the salt

* @param iterations the iteration count (slowness

factor)

* @param bytes the length of the hash to compute in

bytes

* @return the PBDKF2 hash of the password

*/

private static byte[] pbkdf2(char[] password, byte[] salt,

int iterations, int bytes)

throws NoSuchAlgorithmException, InvalidKeySpecException

{

PBEKeySpec spec = new PBEKeySpec(password, salt,

iterations, bytes * 8);

SecretKeyFactory skf =

SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);

return skf.generateSecret(spec).getEncoded();

}

/**

* Converts a string of hexadecimal characters into a byte

array.

*

* @param hex the hex string

* @return the hex string decoded into a byte

array

*/

private static byte[] fromHex(String hex)

{

byte[] binary = new byte[hex.length() / 2];

for(int i = 0; i < binary.length; i++)

{

binary[i] =

(byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);

}

Anexo I: Código del Programa

146

146

return binary;

}

/**

* Converts a byte array into a hexadecimal string.

*

* @param array the byte array to convert

* @return a length*2 character string encoding

the byte array

*/

private static String toHex(byte[] array)

{

BigInteger bi = new BigInteger(1, array);

String hex = bi.toString(16);

int paddingLength = (array.length * 2) - hex.length();

if(paddingLength > 0)

return String.format("%0" + paddingLength + "d", 0)

+ hex;

else

return hex;

}

}

9.3.1.15 ServletEstadisticas.java

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import java.util.ArrayList;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

/**

* Servlet implementation class ServletEstadisticas

*/

@WebServlet("/ServletEstadisticas")

public class ServletEstadisticas extends HttpServlet {

private static final long serialVersionUID = 1L;

147 Apertura de puertas usando Códigos QR QR Door Opener

/**

* @see HttpServlet#HttpServlet()

*/

public ServletEstadisticas() {

super();

// TODO Auto-generated constructor stub

}

/**

* @see HttpServlet#doGet(HttpServletRequest request,

HttpServletResponse response)

*/

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

// TODO Auto-generated method stub

HttpSession session = request.getSession(true);

Connection

conexion=FuncionesWeb.ConectarServidorSQL();

UserBean usuario=new UserBean();

usuario=(UserBean)session.getAttribute("currentSessionUser"

);

ArrayList <EstadisticaBean> estadisticas= new

ArrayList<EstadisticaBean>();

estadisticas=FuncionesWeb.ExtraeEstadisticas(conexion,

usuario.getDNI());

int numestadisticas=0;

if(estadisticas!=null){

numestadisticas=estadisticas.size();

session.setAttribute("Estadisticas",

estadisticas.clone());

}else{

session.setAttribute("Estadisticas", null);

}

session.setAttribute("numestadisticas",

numestadisticas);

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

response.sendRedirect("VerEstadisticas.jsp"); //ver

estadisticas page

}

/**

* @see HttpServlet#doPost(HttpServletRequest request,

HttpServletResponse response)

*/

Anexo I: Código del Programa

148

148

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

}

9.3.1.16 UserBean.java

/*Java Bean de usuario

* Contiene username, password, nombre, telefono, DNI, email y

validez de usuario

*/

package PFCWebPackage;

public class UserBean {

private String username;

private String password;

private String nombre;

private String telefono;

private String DNI;

private String email;

public boolean valid;

private long ultimoacceso;

public String getNombre() {

return nombre;

}

public void setNombre(String newNombre) {

nombre = newNombre;

}

public String getTelefono() {

return telefono;

}

public void setTelefono(String newTelefono) {

telefono = newTelefono;

}

public String getDNI(){

return DNI;

}

public void setDNI( String newDNI){

DNI= newDNI;

}

149 Apertura de puertas usando Códigos QR QR Door Opener

public String getPassword() {

return password;

}

public void setPassword(String newPassword) {

password = newPassword;

}

public String getUsername() {

return username;

}

public void setUserName(String newUsername) {

username = newUsername;

}

public boolean isValid() {

return valid;

}

public void setValid(boolean newValid) {

valid = newValid;

}

public String getEmail(){

return email;

}

public void setEmail(String newEmail) {

email=newEmail;

}

public long getUltimoAcceso(){

return ultimoacceso;

}

public void setUltimoAcceso(long newUltimoAcceso){

ultimoacceso=newUltimoAcceso;

}

}

9.3.1.17 VerCasaServlet.java

/*Servlet para el tratamiento de ver casas

*

*/

package PFCWebPackage;

import java.io.IOException;

import java.sql.Connection;

import java.sql.SQLException;

import java.util.ArrayList;

Anexo I: Código del Programa

150

150

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

@WebServlet("/VerCasaServlet")

public class VerCasaServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public VerCasaServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

HttpSession session = request.getSession(true);

//extraemos la sesión para obtener el usuario

Connection

conexion=FuncionesWeb.ConectarServidorSQL();

UserBean usuario=new UserBean();

usuario=(UserBean)session.getAttribute("currentSessionUser"

);

ArrayList <CasaBean> casas= new

ArrayList<CasaBean>();

//Extraemos las casas del usuario a partir de su DNI

casas=FuncionesWeb.ExtraeCasas(conexion,

usuario.getDNI());

int numcasas=0; //para controlar el número de casas

if(casas!=null){

numcasas=casas.size();

session.setAttribute("casasusuario",

casas.clone());

}else{

session.setAttribute("casasusuario", null);

}

session.setAttribute("numcasas", numcasas);

try {

conexion.close();

} catch (SQLException e) {

e.printStackTrace();

}

151 Apertura de puertas usando Códigos QR QR Door Opener

response.sendRedirect("VerCasasv2.jsp"); //ver casas

page

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException,

IOException {

}

}

9.3.2 Página Web (JSPs)

9.3.2.1 CasaExiste.jsp

<!-- JSP para indicar que la casa solicitada ya existe -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Casa existente</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

Anexo I: Código del Programa

152

152

</div>

<center>Lo sentimos, imposible insertar la casa.</center>

<center>Esta casa ya existe en el sistema</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.2 CrearCertificado.jsp

<!-- JSP para la creación de certificado -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

import="PFCWebPackage.CasaBean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Generar Certificado</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

153 Apertura de puertas usando Códigos QR QR Door Opener

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<!-- Se muestra al usuario su correo para confirmarlo

y se le permite enviar la solicitud -->

<div id="cuerpo">

Anexo I: Código del Programa

154

154

Se enviará un correo a su dirección

<%=currentUser.getEmail()%> con su certificado digital y las

instrucciones de instalación <br/>

<form action=CorreoCertServlet method="post">

<input type="submit" value="Confirmar">

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.3 Error.jsp

<!-- JSP general de error -->

<%@ page language="java" contentType="text/html;

charset=windows-1256" pageEncoding="windows-1256"

import="PFCWebPackage.UserBean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;

charset=windows-1256">

<link href="css/estiloslogin.css" rel="stylesheet"

type="text/css"/>

<title>Error Page</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

155 Apertura de puertas usando Códigos QR QR Door Opener

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<center>

Error inesperado, vuelva al inicio de sesion

</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.4 GenerarQR.jsp

<!-- JSP para la generación del código QR -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Generar QR</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

Anexo I: Código del Programa

156

156

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

157 Apertura de puertas usando Códigos QR QR Door Opener

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

<!-- Se pide al usuario que indique el tiempo de

validez del código QR. Puede estar en

Minutos, Horas o dÃas -->

<div id="cuerpo">

Ha seleccionado Generar QR. Indique el numero de

dias/horas/minutos<br/>

<form action=GenerarQRServlet method="post">

<INPUT type="radio" name="selecciontiempo"

checked value="Minuto">Minuto<br />

<INPUT type="radio" name="selecciontiempo"

value="Hora">Hora<br />

<INPUT type="radio" name="selecciontiempo"

value="Dia">Dia<br />

<input name="Time" type="text"/> <br/>

<input name="UsuarioAcceso"

type="text"/>DNI del usuario que accede <br/>

<input type="submit" name="envio"

value="Confirmar"/>

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.5 Index.jsp

<!-- JSP principal de la Web -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

Anexo I: Código del Programa

158

158

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosindex.css" rel="stylesheet"

type="text/css"/>

<title>Nueva página principal QR Door Opener</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<img alt="Portada US" src="images/portada.jpg"/>

</div>

<div id="menu">

<h1><a href="login.jsp">Login</a></h1>

<h2>Accede a tu cuenta</h2>

<h1><a href="nuevousuario.jsp">Nuevo Usuario</a></h1>

<h2>Crea una nueva cuenta a nuestro servicio</h2>

</div>

<div id="cuerpo">

Página principal del Proyecto Final de Carrera de Carlos

Corrales Yerpes,

alumno de la Universidad de Sevilla.<br />

El propósito de este proyecto es la creación de un sistema

que permita al

usuario la apertura de la puerta de un domicilio utilizando

un código QR en

su teléfono móvil u otro dispositivo portátil.<br />

Para más información, contacte con nosotros

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.6 invalidLogin.jsp

<!-- JSP de error por login invalido -->

<%@ page language="java" contentType="text/html;

charset=windows-1256" pageEncoding="windows-1256" %>

159 Apertura de puertas usando Códigos QR QR Door Opener

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;

charset=windows-1256">

<link href="css/estiloslogin.css" rel="stylesheet"

type="text/css"/>

<title>Invalid Login</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<center>

Lo sentimos, el usuario insertado no esta registrado

en el Servicio. <br/>

Por favor, registrese si desea acceder.

</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.7 invalidNewUser.jsp

<!-- JSP que indica que el usuario nuevo que se ha intentado

crear es

invalido -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

Anexo I: Código del Programa

160

160

<link href="css/estilosNuevoUsuario.css" rel="stylesheet"

type="text/css"/>

<title>Invalid new User</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<center>Lo sentimos, imposible insertar el nuevo

usuario.</center>

<center>Alguno de los campos DNI, user o telefono ya existe

en el sistema</center>

<center>O quizás haya cometido un error al insertar la

contraseña</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.8 login.jsp

<!-- JSP del servicio login de usuario -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-

8" />

<link href="css/estiloslogin.css" rel="stylesheet"

type="text/css"/>

161 Apertura de puertas usando Códigos QR QR Door Opener

<title>Login QR Door Opener</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<!-- Se solicita al usuario el nombre de usuario y el password -

->

<div id="menu">

<form action=LoginServlet method="post">

<div id="menutrozo">

<div id="menuizq">

<h1> Usuario </h1>

<h2> Inserta nombre usuario</h2>

</div>

<div id="menudcha">

<input name="User" type="text"/>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> Contraseña </h1>

<h2> Inserta contraseña</h2>

</div>

<div id="menudcha">

<input name="Password" type="password" />

</div>

</div>

<div id="menuboton">

<input name="Button1" type="submit" value="login" />

</div>

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

Anexo I: Código del Programa

162

162

</html>

9.3.2.9 modificarContrasenya.jsp

<!-- JSP de modificación de contraseña -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

import="PFCWebPackage.CasaBean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Modificar Contraseña</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

163 Apertura de puertas usando Códigos QR QR Door Opener

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<!-- Se solicita al usuario la contraseña actual,

asà como

la contraseña nueva que desea insertar duplicada,

para comprobar

que no hay errores -->

<div id="cuerpo">

<form action="modificarContrasenyaServlet"

method="post">

Contraseña actual

<input type="password" name="Contrasenyaactual">

<br/>

Contraseña nueva

<input type="password"

name="Contrasenyanueva"><br/>

Repita contraseña nueva

<input type="password"

name="Contrasenyanueva2"><br/>

<input type="submit" name="Enviar" value="Enviar">

</form>

</div>

Anexo I: Código del Programa

164

164

</div>

</body>

</html>

9.3.2.10 ModificarDatos.jsp

<!-- JSP de modificacion de datos -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

import="PFCWebPackage.CasaBean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Modificar Datos Usuario</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

165 Apertura de puertas usando Códigos QR QR Door Opener

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<div id="cuerpo">

<!-- en este formulario, se permita al usuario

modificar

nombre, telefono, mail, y nombre de usuario -->

<form action="modificarUsuarioServlet"

method="post">

Nombre

<input type="text" maxlength=20 name="Nombre"

value="<%=currentUser.getNombre()%>" > <br/>

Telefono

<input type="text" name="Telefono"

value=<%=currentUser.getTelefono()%>> <br/>

Email

<input type="text" name="Email"

value=<%=currentUser.getEmail()%>> <br/>

Username

<input type="text" name="Username"

value=<%=currentUser.getUsername()%>> <br/>

Anexo I: Código del Programa

166

166

<input type="submit" name="enviar"

value="Modificar Datos">

</form>

<!-- Tambien se le permite acceder a la

modificacion de contraseña -->

<form action="modificarContrasenya.jsp">

<input type="submit" name="contraseña"

value="Modificar Contraseña">

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.11 nuevousuario.jsp

<!-- JSP para la creacion de nuevo usuario -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-

8" />

<link href="css/estilosNuevoUsuario.css" rel="stylesheet"

type="text/css"/>

<title>Nuevo Usuario QR Door Opener</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

167 Apertura de puertas usando Códigos QR QR Door Opener

<div id="menu">

<!-- En este formulario se solicitaran al usuario sus datos

-->

<form action=NewUserServlet method="post">

<div id="menutrozo">

<div id="menuizq">

<h3> Nombre </h3>

</div>

<div id="menudcha">

<input name="Nombre" type="text" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> DNI </h3>

</div>

<div id="menudcha">

<input name="Dni" type="text" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> Teléfono </h3>

</div>

<div id="menudcha">

<input name="Telefono" type="text" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> E-mail </h3>

</div>

<div id="menudcha">

<input name="Email" type="text" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> Usuario </h3>

</div>

<div id="menudcha">

<input name="Usuario" type="text" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> Contraseña </h3>

</div>

Anexo I: Código del Programa

168

168

<div id="menudcha">

<input name="Password" type="password" />

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h3> Insertar Contraseña de nuevo</h3>

</div>

<div id="menudcha">

<input name="Password2" type="password" />

</div>

</div>

<input name="EnviarDatos" type="submit" value="Dar de Alta"

/>

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.12 QRfallo.jsp

<!-- JSP general de error -->

<%@ page language="java" contentType="text/html;

charset=windows-1256" pageEncoding="windows-1256"

import="PFCWebPackage.UserBean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;

charset=windows-1256">

169 Apertura de puertas usando Códigos QR QR Door Opener

<link href="css/estiloslogin.css" rel="stylesheet"

type="text/css"/>

<title>Invalid Login</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<center>

Imposible generar el codigo QR en estos momentos

<br/>

Por favor, intentelo de nuevo más tarde

</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.13 QRgenerado.jsp

<!-- JSP de codigo QR generado -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

Anexo I: Código del Programa

170

170

import="PFCWebPackage.CasaBean"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>QR Generado</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

<% CasaBean casa =

(CasaBean)session.getAttribute("Casa");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

171 Apertura de puertas usando Códigos QR QR Door Opener

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<div id="cuerpo">

<!-- Se muestra por pantalla el QR generado,

acompañado del nombre de usuario y la direccion -->

<%String

nombreusuario=currentUser.getNombre().substring(0,3);

String

direccion=casa.getDireccion().substring(0,2);

String idQR=nombreusuario.concat(direccion);%>

QR Generado para: <%=idQR %> <br/>

<center>

<img

src="images/qr/qr<%=currentUser.getDNI()%>.jpeg"/>

</center>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

Anexo I: Código del Programa

172

172

</body>

</html>

9.3.2.14 userLogged.jsp

<!-- Menu principal del servicio, disponible tras realizar el

login -->

<%@ page language="java"

contentType="text/html;

charset=windows-1256" pageEncoding="windows-1256"

import="PFCWebPackage.UserBean"

import="java.util.Date"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;

charset=windows-1256">

<link href="css/estilosseleccion.css"

rel="stylesheet" type="text/css"/>

<title> User Logged Successfully </title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");

Date d= new

Date(currentUser.getUltimoAcceso());%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

<center>

Fecha de ultimo acceso: <%= d %>

173 Apertura de puertas usando Códigos QR QR Door Opener

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

Anexo I: Código del Programa

174

174

</body>

</html>

9.3.2.15 UsuarioQRInvalido.jsp

<!-- JSP general de error -->

<%@ page language="java" contentType="text/html;

charset=windows-1256" pageEncoding="windows-1256"

import="PFCWebPackage.UserBean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html;

charset=windows-1256">

<link href="css/estiloslogin.css" rel="stylesheet"

type="text/css"/>

<title>Invalid Login</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<center>

El DNI del usuario de acceso insertado no esta en el

sistema. <br/>

Por favor, vuelva a intentarlo cuando este dado de

alta.

</center>

</div>

175 Apertura de puertas usando Códigos QR QR Door Opener

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

Teléfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.16 VerCasasv2.jsp

<!-- JSP que permite ver las casas del usuario -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

import="PFCWebPackage.CasaBean"

import="java.util.ArrayList"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Casas</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

Anexo I: Código del Programa

176

176

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

<div id=cuerpo>

<center>Casas del Usuario</center>

<input type="text" maxlength="26" size="26" style="text-

align: center" name="Direccion" value="Direccion" readonly/>

<input type="text" maxlength="15" size="6" style="text-

align: center" name="CP" value="Codigo Postal" readonly/>

177 Apertura de puertas usando Códigos QR QR Door Opener

<input type="text" maxlength="15" size="15" style="text-

align: center" name="Poblacion" value="Poblacion" readonly/>

<!-- Primero hay que extraer las casas del usuario de la

variable de sesiin

que es donde han sido copiadas por el servidor -->

<%

int

numcasas=(Integer)session.getAttribute("numcasas");

System.out.println("Numero de casas: " +numcasas);

ArrayList<CasaBean>

casas=(ArrayList<CasaBean>)(session.getAttribute("casasusuario")

);

for (int i = 0; i < numcasas; i++){

CasaBean casaactual=casas.get(i);

%>

<!-- en este bucle, se van imprimiendo todas las casas una

por una -->

<form action=CasaServlet method="post">

<input type="hidden" name="IDcasa"

value="<%=casaactual.getIdCasa() %>" />

<input type="text" maxlength="26" size="26"

style="text-align: left" name="Direccion"

value="<%=casaactual.getDireccion() %>" readonly/>

<input type="text" maxlength="5" size="6"

style="text-align: right" name="CP"

value="<%=casaactual.getCP()%>" readonly/>

<input type="text" maxlength="15" size="15"

style="text-align: left" name="Poblacion"

value="<%=casaactual.getPoblacion()%>" readonly/>

<input type="submit" name="Tratamiento"

value="Generar QR" >

<input type="submit" name="Tratamiento"

value="Crear Certificado">

<input type="submit" name="Tratamiento"

value="Eliminar" >

</form>

<% } %>

<!-- tambien se presenta un formulario para que el usuario

inserte nuevas casas -->

<form action=InsertarCasaServlet method="post">

<input type="text" maxlength="26" size="26"

style="text-align: right" name="Direccion" />

<input type="text" maxlength="5" size="6"

style="text-align: right" name="CP" />

Anexo I: Código del Programa

178

178

<input type="text" maxlength="15" size="15"

style="text-align: right" name="Poblacion" />

<center> <input type="submit" name="InsertarCasa"

value="Insertar Casa"> </center>

</form>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

</div>

</div>

</body>

</html>

9.3.2.17 VerEstadisticas.jsp

<!-- JSP para ver las estadisticas -->

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"

import="PFCWebPackage.UserBean"

import="PFCWebPackage.EstadisticaBean"

import="java.util.ArrayList"

import="java.util.Date"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-

8">

<link href="css/estilosseleccion.css" rel="stylesheet"

type="text/css"/>

<title>Estadisticas</title>

</head>

<body>

<div id="contenedor">

<div id="logo">

<a href="index.jsp"> <img alt="Portada US"

src="images/portadapequena.jpg"/> </a>

</div>

179 Apertura de puertas usando Códigos QR QR Door Opener

<div id="menu">

<div id="encabezado">

<div id="encabezadowelc">

<center>

<% UserBean currentUser =

(UserBean)session.getAttribute("currentSessionUser");%>

Bienvenido,

<%= currentUser.getNombre()%>

</center>

</div>

<div id="encabezadologout">

<a href=LogoutServlet>Cerrar Sesion</a>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1> <a href= ModificarDatos.jsp>

Modificar datos Usuario</a></h1>

</div>

<div id="menudcha">

<h2> Modifique los datos de su cuenta</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=VerCasaServlet> Ver Casas</a>

</h1>

</div>

<div id="menudcha">

<h2> Consulte sus casas y genere codigos

de acceso</h2>

</div>

</div>

<div id="menutrozo">

<div id="menuizq">

<h1><a href=ServletEstadisticas> Ver

Registros </a></h1>

</div>

<div id="menudcha">

<h2> Vea los registros de acceso con sus

codigos</h2>

</div>

</div>

</div>

Anexo I: Código del Programa

180

180

<div id=cuerpo>

<center>Registro de Accesos del Usuario</center>

<table border="2" align=center>

<tr>

<th align=center>Nombre</th>

<th align=center> Usuario Acceso </th>

<th align=center>Direccion </th>

<th align=center>Ciudad</th>

<th align=center>id QR</th>

<th align=center>Fecha Acceso</th>

<th align=center>Error</th>

</tr>

<!-- Lo primero es extraer las estadisticas de la variable

de sesion, que sera

donde el servidor las ha guardado -->

<%

int

numestadisticas=(Integer)session.getAttribute("numestadisticas")

;

System.out.println("Numero de Estadisticas: "

+numestadisticas);

ArrayList<EstadisticaBean>

Estadisticas=(ArrayList<EstadisticaBean>)(session.getAttribute("

Estadisticas"));

for (int i = 0; i < numestadisticas; i++){

EstadisticaBean

estadisticaactual=Estadisticas.get(i);

Date d=new

Date(estadisticaactual.getFechaacceso());

%>

<!-- Se va haciendo un bucle y copiando las estadisticas en

una tabla -->

<tr>

<td align=right>

<%=estadisticaactual.getUsuario()%>

</td>

<td align=right>

<%=estadisticaactual.getUsuarioacceso()%>

</td>

<td align=right>

<%=estadisticaactual.getDireccion()%>

181 Apertura de puertas usando Códigos QR QR Door Opener

</td>

<td align=right>

<%=estadisticaactual.getPoblacion()%>

</td>

<td align=right>

<%=estadisticaactual.getIdQR() %>

</td>

<td align=right>

<%=d%>

</td>

<td align=right>

<%int error=estadisticaactual.getError();

String cadenaimprimir;

switch(error){

case 0:

cadenaimprimir="Acceso Correcto";

break;

case 1:

cadenaimprimir="Error Desconocido";

break;

case 2:

cadenaimprimir="Código QR caducado";

break;

case 3:

cadenaimprimir="Código QR inválido";

break;

case 4:

cadenaimprimir="Certificado digital

inválido";

break;

default:

cadenaimprimir="Problema de servicio

desconocido";

break;

} %>

<%=cadenaimprimir %>

</td>

</tr>

<%} %>

</table>

</div>

<div id="contacta">

contacta <br />

E-mail: <a

href="mailto:[email protected]">[email protected]</a><br

/>

TelÉfono: +34625591780

Anexo I: Código del Programa

182

182

</div>

</div>

</body>

</html>

9.3.3 Página Web (CSSs)

9.3.3.1 EstilosIndex.css

body {

font:13px Arial, Helvetica, sans-serif;

background-color:#9B021A;

text-align:center;

margin: 0 auto;

}

#contenedor {

width:1024px; /*ancho total de la pagina*/

margin: 0 auto;

background-color:#FFFFFF;

text-align:left; /*aqui alineamos todo de nuevo a la

izquierda, pero dentro del contenedor*/

}

#logo{

float:left; /*lo colocamos a la izquierda totalmente*/

width:350px;

}

#menu{

float:right;

width:340px;

background-color:#FFFFFF;

}

#menu h1{

color:#9B021A;

font-size:26px;

margin-top:20px;

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu h2{

color:#FFCC00;

183 Apertura de puertas usando Códigos QR QR Door Opener

font-size:12px;

margin-top:auto;

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu a:visited{

color:#9B021A;

}

#menu a:link{

color:#9B021A;

}

#cuerpo{

width: 1000px; /*el ancho mas el padding me da 600px de

ancho que es loque mide la pagina */;

clear: both; /*con esto nos aseguramos de que no se ponga

nada a los lados*/;

padding: 10px;

font-size: 16px;

color: #000000;

background-color:#FFFFFF;

text-align:center;

}

#contacta {

width:1024px;

margin-left:auto;

margin-right:auto;

color:#FFCC00;

background-color:#9B021A;

text-align:center;

line-height:1.2em;

}

}

9.3.3.2 estiloslogin.css

body {

font: 13px Arial, Helvetica, sans-serif;

background-color: #9B021A;

text-align: center;

margin: 0 auto;

}

#contenedor {

width:1024px; /*ancho total de la pagina*/

margin: 0 auto;

background-color:#FFFFFF;

Anexo I: Código del Programa

184

184

text-align:left; /*aqui alineamos todo de nuevo a la

izquierda, pero dentro del contenedor*/

}

#logo{

float:left; /*lo colocamos a la izquierda totalmente*/

width:474px;

height:340px;

background-color:#FFFFFF;

}

#encabezado{

float:right;

width:550px;

background-color:#FFFFFF;

}

#encabezadowelc{

float:left;

width:300px;

}

#encabezadologout{

float: right;

width: 250px;

}

#menu{

float:right;

height:340px;

width:550px;

background-color:#FFFFFF;

}

#menu h1{

color:#9B021A;

font-size:26px;

margin-top:20px;

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu h2{

color:#FFCC00;

font-size:12px;

/*margin-top:5px;*/

text-align:right;

/*text-transform:uppercase;*/

width:200px;

}

185 Apertura de puertas usando Códigos QR QR Door Opener

#menu h3{

color:#9B021A;

font-size:5px;

text-align:right;

}

#menu a:visited{

color:#9B021A;

}

#menu a:link{

color:#9B021A;

}

#menutrozo{

margin-top:10px;

height: 80px;

}

#menuizq{

float:left;

width:300px;

}

#menudcha{

margin-top:20px;

float: right;

width:250px;

}

#menuboton{

text-align:center;

}

#cuerpo{

width: 1000px; /*el ancho mas el padding me da 600px de

ancho que es loque mide la pagina */;

clear: both; /*con esto nos aseguramos de que no se ponga

nada a los lados*/;

padding: 10px;

font-size: 16px;

color: #000000;

background-color:#FFFFFF;

text-align:center;

}

#contacta {

width:1024px;

margin-left:auto;

margin-right:auto;

color:#FFCC00;

background-color:#9B021A;

text-align:center;

line-height:1.2em;

Anexo I: Código del Programa

186

186

}

}

9.3.3.3 estilosMenu.css

body {

font: 13px Arial, Helvetica, sans-serif;

background-color: #9B021A;

text-align: center;

margin: 0 auto;

}

#contenedor {

width:1024px; /*ancho total de la pagina*/

margin: 0 auto;

background-color:#FFFFFF;

text-align:left; /*aqui alineamos todo de nuevo a la

izquierda, pero dentro del contenedor*/

}

#logo{

float:left; /*lo colocamos a la izquierda totalmente*/

width:250px;

height:200px;

background-color:#FFFFFF;

}

#menu{

float:right;

height:200px;

width:774px;

background-color:#FFFFFF;

}

#menu h1{

color:#9B021A;

font-size:20px;

margin-top:20px;

text-align:right;

text-transform:uppercase;

width:250px;

}

#menu h2{

color:#FFCC00;

font-size:12px;

/* margin-top:5px;*/

text-align:right;

187 Apertura de puertas usando Códigos QR QR Door Opener

text-transform:uppercase;

width:200px;

}

#menu a:visited{

color:#9B021A;

}

#menu a:link{

color:#9B021A;

}

#menutrozo{

margin-top:10px;

height: 60px;

}

#menuizq{

float:left;

width:300px;

}

#menudcha{

/*margin-top:25px;*/

float: right;

width:250px;

}

#menuboton{

text-align:center;

}

#cuerpo{

width: 1000px; /*el ancho mas el padding me da 600px de

ancho que es loque mide la pagina */;

clear: both; /*con esto nos aseguramos de que no se ponga

nada a los lados*/;

padding: 10px;

font-size: 16px;

color: #000000;

background-color:#FFFFFF;

text-align:center;

}

#contacta {

width:1024px;

margin-left:auto;

margin-right:auto;

color:#FFCC00;

background-color:#9B021A;

text-align:center;

line-height:1.2em;

}

Anexo I: Código del Programa

188

188

}

9.3.3.4 estilosNuevoUsuario.css

body {

font:13px Arial, Helvetica, sans-serif;

background-color:#9B021A;

text-align:center;

margin: 0 auto;

}

#contenedor {

width:1024px; /*ancho total de la pagina*/

margin: 0 auto;

background-color:#FFFFFF;

text-align:left; /*aqui alineamos todo de nuevo a la

izquierda, pero dentro del contenedor*/

}

#logo{

float:left; /*lo colocamos a la izquierda totalmente*/

width:474px;

height:340px;

background-color:#FFFFFF;

}

#encabezado{

float:right;

width:550px;

background-color:#FFFFFF;

}

#encabezadowelc{

float:left;

width:300px;

}

#encabezadologout{

float: right;

width: 250px;

}

#menu{

float:right;

height:340px;

width:550px;

background-color:#FFFFFF;

}

189 Apertura de puertas usando Códigos QR QR Door Opener

#menu h1{

color:#9B021A;

font-size:26px;

margin-top:20px;

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu h2{

color:#FFCC00;

font-size:12px;

margin-top:auto;

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu h3{

color: #9B021A;

font-size:12px;

margin-top:auto;

text-align:right;

width:200px;

}

#menu a:visited{

color:#9B021A;

}

#menu a:link{

color:#9B021A;

}

#menutrozo{

margin-top:10px;

height: 35px;

}

#menuizq{

margin-top:10px;

float:left;

width:300px;

}

#menudcha{

margin-top:10px;

float: right;

width:250px;

}

#cuerpo{

width: 1000px; /*el ancho mas el padding me da 600px de

ancho que es loque mide la pagina */;

Anexo I: Código del Programa

190

190

clear: both; /*con esto nos aseguramos de que no se ponga

nada a los lados*/;

padding: 10px;

font-size: 16px;

color: #000000;

background-color:#FFFFFF;

text-align:center;

}

#contacta {

width:1024px;

margin-left:auto;

margin-right:auto;

color:#FFCC00;

background-color:#9B021A;

text-align:center;

line-height:1.2em;

}

}

9.3.3.5 estilosseleccion.css

body {

font: 13px Arial, Helvetica, sans-serif;

background-color: #9B021A;

text-align: center;

margin: 0 auto;

}

#contenedor {

width:1024px; /*ancho total de la pagina*/

margin: 0 auto;

background-color:#FFFFFF;

text-align:left; /*aqui alineamos todo de nuevo a la

izquierda, pero dentro del contenedor*/

}

#logo{

float:left; /*lo colocamos a la izquierda totalmente*/

width:474px;

height:340px;

background-color:#FFFFFF;

}

#encabezado{

float:right;

width:550px;

background-color:#FFFFFF;

191 Apertura de puertas usando Códigos QR QR Door Opener

}

#encabezadowelc{

float:left;

width:300px;

}

#encabezadologout{

float: right;

width: 250px;

}

#menu{

float:right;

height:340px;

width:550px;

background-color:#FFFFFF;

}

#menu h1{

color:#9B021A;

font-size:20px;

margin-top:20px;

text-align:right;

text-transform:uppercase;

width:250px;

}

#menu h2{

color:#FFCC00;

font-size:12px;

/* margin-top:5px;*/

text-align:right;

text-transform:uppercase;

width:200px;

}

#menu a:visited{

color:#9B021A;

}

#menu a:link{

color:#9B021A;

}

#menutrozo{

margin-top:10px;

height: 60px;

}

#menuizq{

float:left;

width:300px;

}

Anexo I: Código del Programa

192

192

#menudcha{

margin-top:13px;

float: right;

width:250px;

}

#menuboton{

text-align:center;

}

#cuerpo{

width: 1000px; /*el ancho mas el padding me da 600px de

ancho que es loque mide la pagina */;

clear: both; /*con esto nos aseguramos de que no se ponga

nada a los lados*/;

padding: 10px;

font-size: 16px;

color: #000000;

background-color:#FFFFFF;

text-align:left;

}

#contacta {

width:1024px;

margin-left:auto;

margin-right:auto;

color:#FFCC00;

background-color:#9B021A;

text-align:center;

line-height:1.2em;

}

}

9.3.4 Librerías Externas usadas

9.3.4.1 ZXING

core.jar

javase.jar

9.3.4.2 Cheok Yan Cheng

javax.mail.jar

193 Apertura de puertas usando Códigos QR QR Door Opener

9.4 Base de datos

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;

SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS,

FOREIGN_KEY_CHECKS=0;

SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `BBDDPFCv5` ;

CREATE SCHEMA IF NOT EXISTS `BBDDPFCv5` DEFAULT CHARACTER SET

latin1 COLLATE latin1_spanish_ci ;

USE `BBDDPFCv5` ;

-- -----------------------------------------------------

-- Table `BBDDPFCv5`.`Usuario`

-- -----------------------------------------------------

DROP TABLE IF EXISTS `BBDDPFCv5`.`Usuario` ;

CREATE TABLE IF NOT EXISTS `BBDDPFCv5`.`Usuario` (

`idUsuario` VARCHAR(9) NOT NULL ,

`Nombre` VARCHAR(45) NOT NULL ,

`Telefono` VARCHAR(9) NOT NULL ,

`User` VARCHAR(15) NOT NULL ,

`Password` VARCHAR(102) NOT NULL ,

`Email` VARCHAR(45) NOT NULL ,

`Intentos` INT NOT NULL ,

`UltimoAcceso` MEDIUMTEXT NOT NULL ,

PRIMARY KEY (`idUsuario`) )

ENGINE = InnoDB;

CREATE UNIQUE INDEX `idUsuario_UNIQUE` ON `BBDDPFCv5`.`Usuario`

(`idUsuario` ASC) ;

CREATE UNIQUE INDEX `Telefono_UNIQUE` ON `BBDDPFCv5`.`Usuario`

(`Telefono` ASC) ;

CREATE UNIQUE INDEX `User_UNIQUE` ON `BBDDPFCv5`.`Usuario`

(`User` ASC) ;

CREATE UNIQUE INDEX `Email_UNIQUE` ON `BBDDPFCv5`.`Usuario`

(`Email` ASC) ;

-- -----------------------------------------------------

-- Table `BBDDPFCv5`.`Casas`

-- -----------------------------------------------------

DROP TABLE IF EXISTS `BBDDPFCv5`.`Casas` ;

CREATE TABLE IF NOT EXISTS `BBDDPFCv5`.`Casas` (

`idCasa` INT NOT NULL AUTO_INCREMENT ,

`Direccion` VARCHAR(45) NOT NULL ,

`Poblacion` VARCHAR(45) NOT NULL ,

`Codigo_Postal` VARCHAR(5) NOT NULL ,

`idCertificado` VARCHAR(45) NULL ,

PRIMARY KEY (`idCasa`) )

Anexo I: Código del Programa

194

194

ENGINE = InnoDB;

CREATE UNIQUE INDEX `idCasa_UNIQUE` ON `BBDDPFCv5`.`Casas`

(`idCasa` ASC) ;

CREATE UNIQUE INDEX `idCertificado_UNIQUE` ON

`BBDDPFCv5`.`Casas` (`idCertificado` ASC) ;

-- -----------------------------------------------------

-- Table `BBDDPFCv5`.`QRCodes`

-- -----------------------------------------------------

DROP TABLE IF EXISTS `BBDDPFCv5`.`QRCodes` ;

CREATE TABLE IF NOT EXISTS `BBDDPFCv5`.`QRCodes` (

`idQR` INT(11) NOT NULL AUTO_INCREMENT ,

`FechaExpedicion` MEDIUMTEXT NOT NULL ,

`FechaValidez` MEDIUMTEXT NOT NULL ,

`idCasa` INT(11) NOT NULL ,

`idUsuario` VARCHAR(9) NOT NULL ,

`idUsuarioAccede` VARCHAR(9) NOT NULL ,

PRIMARY KEY (`idQR`) )

ENGINE = InnoDB;

CREATE UNIQUE INDEX `idQR_UNIQUE` ON `BBDDPFCv5`.`QRCodes`

(`idQR` ASC) ;

-- -----------------------------------------------------

-- Table `BBDDPFCv5`.`Permisos`

-- -----------------------------------------------------

DROP TABLE IF EXISTS `BBDDPFCv5`.`Permisos` ;

CREATE TABLE IF NOT EXISTS `BBDDPFCv5`.`Permisos` (

`idPermisos` INT(11) NOT NULL AUTO_INCREMENT ,

`idCasa` INT(11) NOT NULL ,

`idUsuario` VARCHAR(9) NOT NULL ,

`Valido` TINYINT(1) NOT NULL ,

`Administrador` TINYINT(1) NOT NULL ,

PRIMARY KEY (`idPermisos`) )

ENGINE = InnoDB;

CREATE UNIQUE INDEX `idPermisos_UNIQUE` ON

`BBDDPFCv5`.`Permisos` (`idPermisos` ASC) ;

-- -----------------------------------------------------

-- Table `BBDDPFCv5`.`EstadisticaAcceso`

-- -----------------------------------------------------

DROP TABLE IF EXISTS `BBDDPFCv5`.`EstadisticaAcceso` ;

CREATE TABLE IF NOT EXISTS `BBDDPFCv5`.`EstadisticaAcceso` (

`IdEstadistica` INT(11) NOT NULL AUTO_INCREMENT ,

`FechaAcceso` MEDIUMTEXT NOT NULL ,

195 Apertura de puertas usando Códigos QR QR Door Opener

`Tipo_de_Error` INT(11) NOT NULL ,

`idUsuario` VARCHAR(9) NOT NULL ,

`idCasa` INT(11) NOT NULL ,

`idQR` INT(11) NOT NULL ,

PRIMARY KEY (`IdEstadistica`) )

ENGINE = InnoDB;

CREATE UNIQUE INDEX `IdEstadistica_UNIQUE` ON

`BBDDPFCv5`.`EstadisticaAcceso` (`IdEstadistica` ASC) ;

SET SQL_MODE=@OLD_SQL_MODE;

SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;

SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;