Seguridad en Java: Permisos de Código Autentificación, Firma Digital y Cifrado Prof. Wílmer Pereira USB / UCAB Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Principios de código seguro en Java Mientras las aplicaciones sean locales la seguridad no es mayor problema ... Sin embargo el desafio está en las aplicaciones que se instalan desde un servidor remoto (por ejemplo los applets ...) Java ofrece para ejecutar código remoto: (1) Lenguaje fuertemente tipeado, sin permitir apuntadores (2) Cargador de clases controlado y configurable. (3) Comprobaciones automática de los .class (bytecodes) (4) Administrador de seguridad y mecanismos de permisos. (5) Autentificación y autorización. (6) Firma de código. (7) Manipulación de certificados digitales. (8) Cifrado con algoritmos de clave simétrica y de pública. Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Modelo Cliente/Servidor Máquina remota Máquina local Petición Sockets RMI (interfaz) Respuesta Cliente Sockets RMI (demonio) Servidor Todos los servicios sobre Internet funcionan bajo esta arquitectura El medio de envío para petición/respuesta es la red Un servidor debe poder manejar varios usuarios concurrentemente Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Páginas Web Dinámicas (lado del servidor) Máquina local Browser Petición URL Máquina remota HTTP Respuesta Página HTML Cliente PHP ASP JSP Base de Datos Servidor Una aplicación corre del lado del servidor Construye una página Web con los datos extraídos de la BD El browser sólo visualiza páginas HTML Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Página Web Dinámica (lado del cliente) Máquina local Browser javascript applets activeX flash Petición URL Respuesta Página HTML + Aplicación Cliente Máquina remota HTTP Servidor Una aplicación corre del lado del cliente Construye una página Web ejecutando una aplicación que viajó desde el servidor El browser debe ser capaz de ejecutar aplicaciones (plug-ins) Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Seguridad del código movil Las implementaciones más conocidas son: Applets ActiveX Flash Javascript ... Applets: Propuesta de Java para ejecutar en JVM del navegador. Inicialmente tenían todos los derechos pero ... Ahora a partir de Java2 tienen tres restricciones No pueden acceder a recursos locales No puede correr ningun programa localmente Sólo pueden conectarse con el servidor ... ActiveX: Desarrollado de Microsoft sin ninguna restricción sólo la que impone el conocimiento de quien es el creador Esto se logra gracias a la firma de código y Authenticode Javascript: lanzado al mercado por Netscape corre código del lado cliente ... phishing usa una de sus funcionalidades para engañar ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (2) Seguridad con el cargador de clases Cargan los .class (bytecodes) necesarios desde las librerías que se importan. Normalmente el proceso es transparente salvo si se define un cargador de clases propio Cargadores: Arranque (rt.jar en C) Extensión (todos los API según el CLASSPATH) Aplicación (específicos del programa) Los applets, servlets y fragmentos de RMI tiene cargadores especiales Para definir un cargador de clases propio basta con extender de la clase ClassLoader y reescribir el método findClass Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (3) Verificación del bytecode Comprobaciones de los .class pues pueden ser modificados con editores hexadecimales. Siempre se realiza y sólo no se hace por solicitud expresa en la compilación java -noverify Prueba Algunas de las verificaciones: Variables deben recibir valores iniciales No se deben violar las reglas de acceso a datos y métodos privados La pila de ejecución no debe desbordarse Evita casting no autorizado ... No se deberían saltar las verificaciones del bytecode y menos aún si el código proviene de una máquina remota Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (4) Administrador de seguridad y permisos Comprueba ciertas propiedades de ejecución después de que se carga la clase y se verifican los bytecode. Por defecto las aplicaciones locales no instalan administrador de seguridad Algunas de las comprobaciones se realizan sobre el hilo actual: Si puede o no crear un cargador de clases Si puede o no acceder a los archivos locales Si puede o no abrir una conexión remota vía sockets Si puede o no acceder al portapapeles … Por ejemplo el método exit llama a checkExit para saber si puede aprobar la solicitud. En caso contrario lanza una excepción: SecurityException Instalar un administrador de seguridad específico es con setSecurityManager de la clase System Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (4) Formato de permisos Inicialmente se negaba a los applets el acceso de todos los recursos locales. A partir de jdk1.1 si el applet estaba firmado se le daban todos los derechos. Esta política de todo o nada se modificó con los archivos de permisos Un permiso se implanta en el código con: FilePermission p = new FilePermission(“/tmp/*”,”read,write”); o en el archivo de permisos como: permission java.io.FilePermission “/tmp/*” “read,write”; La sintaxis completa de un archivo como miAplic.policy: grant codeBase “http://www.horstmann.com/classes” { permission java.io.FilePermission “/tmp/*” “read,write”; }; y se ejecuta habiendo obtenido el bytecode de miAplic.class: java -Djava.security.policy=miAplic.policy miAplic Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (4) Archivos de permisos Las ubicaciones de los archivos de permisos standards son: El archivo java.policy del directorio principal de Java El archivo .java.policy (oculto) situado en el directorio actual Para que ejecute solamente el archivo de permisos se coloca == java -Djava.security.policy==miAplic.policy miAplic así no verifica los archivos de permisos standards Para instalar un nuevo administrador de seguridad se debe colocar: System.setSecurityManager(new SecurityManager()); La sintaxis general del archivo de permisos es: grant URL { permission nombreClase objetivo listaAcciones; ... }; Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (4) Ejemplo de archivos de permisos Clases que se pueden asegurar: java.io.FilePermission java.net.SocketsPermission java.awt.AWTPermission java.net.NETPermission ... Los objetivos serán diferentes dependiendo de la clase: directorios, intervalos de puertos, URL de máquinas, etc ... Ejemplo: grant codeBase “http://www.horstmann.com/classes” { permission java.io.FilePermission “/tmp/*” “read,write”; permission java.net.SocketsPermission “*.horstmann.com:8000-8999” “connect”; }; Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (5) Autentificación y Autorización Maneja autentificación con Kerberos, conexiones UNIX, con certificados, etc. Se usa archivo de permisos y archivo de autentificación (roles para autorizar): PruebaAuten.policy grant principal com.sun.security.auth.UnixPrincipal “harry” { permission java.util.PropertyPermission “user.*” “read”; ... }; jass.config Conexion1 { com.sun.security.auth.module.UnixLoginModule required; com.whizzbang.auth.module.KeyStoreLoginModule sufficient; } ... En la corrida deben tener exito un módulo required o al menos un opcional: java -classpath conexion.jar -Djava.security.policy=PruebaAuten.policy -Djava.security.auth.login.config=jaas.config PruebaAuth Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (6) Compendio Para calcular compendio se crea una instancia: MessageDigest alg = MessageDigest.getInstance(“SHA-1”); Se agrega texto al mensaje en un ciclo con ... alg.update((byte) ch); ... y se calcula el compendio con: byte[] hash = alg.digest(); Su principal uso sería para: Integridad de datos. Almacenamiento seguro de passwords Extensión de password para hacerlo menos ingenuo Forma parte de la firma digital ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (6) Firma digital Inicialmente se deben generar las claves pública y privada. Para ello se deben usar números aleatorios seguros: KeyPairGenerator genclaves = KeyPairGenerator.getInstance(“DSA”); SecureRandom aleseg = new SecureRandom(); aleseg.setSeed(...); genclaves.initialize(512,aleseg); KeyPair claves = genclaves.generateKeyPair(); PublicKey clavePub = claves.getPublic(); PrivateKey clavePriv = claves.getPrivate(); ... Signature algsign = Signature.getInstance(“DSA”); algsign.initSign(clavepriv); ... algsign.update(bytes); ... byte[] signatura = algsign.sign(); Para verificar la firma en el destinatario (con la clave pública del emisor ...) Signature algverif = Signature.getInstance(“DSA”); algverif.initVerify(clavepub); ... algverif.update(bytes); ... boolean comprobacion = algverif.verify(signatura); Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (7) Certificados digitales en Java Java usa el formato X509v3 que se guarda en formato binario. Los certificados clase 1 apenas se verifican con e-mail y los clase 3 requieren un notario ... en ambos casos es con una CA externa keytool administra el almacen de claves, donde keytool keytool keytool keytool keytool -genkey -keystore almacen.store -alias wilmer -export -keystore almacen1.store -alias wilmer -file wilmer.cert -printcert -file wilmer.cert -import -keystore almacen2.store -alias wilmer -file wilmer.cert -list -v -keystore jre/lib/security/cacerts Para firmar un documento empaquetado con jarsigner jar cvf documento.jar documento.txt jarsigner -keystore almacen.store documento.jar wilmer Finalmente para verificar el documento firmado jarsigner -verify -keystore almacen.store documento.jar Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (7) Firmar certificados en Java Los certificados generados por keytool son autofirmados. Podría firmarse creando una CA interna o propia ... Inicialmente se crea una CA autofirmada que se importará a todos los clientes keytool -genkey -keystore ACME.store -alias ACMEraiz keytool -export -keystore ACME.store -alias ACMEraiz -file CApropia.cert ACME debe tener un programa para firmar certificados pues tiene ACME.store y la contraseña del almacen de claves. La llamada sería: java FirmaCerts -keystore ACME.store -alias ACMEraiz -infile wilmer.cert -outfile wilmerFirmadoACME.cert Ahora cada usuario de la CA interna debe tener ACMraiz para verificar los certificados de sus colegas. keytool no permite explicitamente firmar certificados pero si dejarlos preparados para ser firmado por una CA externa con la opción -certreq Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (7) Código para firmar en Java Inicialmente se crea un almacen de claves KeyStore almacen = KeyStore.getInstance(“JKS”,“UCAB”); Se carga el almacen con la clave del almacen y se recupera la clave privada de ACME con el passphrase: ... almacen.load(in,claveAlmacen); ... ... PrivateKey emisorClavePrivada = (PrivateKey) almacen.getKey(alias,passphrase); ... Se crea el nuevo certificado que será firmado: CertificateFactory fabrica = CertificateFactory.getInstance(“X.509”); X509Certificate certEntrada = (X509Certificate) fabrica.generateCertificate(in); Hay dos tipos de certificados, el de java.security y el de java.security.cert. Se debe usar el último ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (7) Código para firmar en Java Se recuperan los datos del certificado de ACME byte[] bytesCertEntrada = certEntrada.getTBSCertificate(); X509Certificate certEmisor = (X509certificate) almacen.getCertificate(“ACMEraiz”); Principal emisor = certEmisor.getSubjectDN(); String algSigEmisor = certEmisor.getSigAlgName(); Lo que sigue no es standard, pertenece a la clase sun.security.x509 y se firma el certificado del cliente: X509CertInfo info = new X509CertInfo(bytesCertEntrada); info.set(X509CertInfo.ISSUER, new certificateIssuerName((X509Name) emisor)); X509CertImpl certSalida = new X509CertImpl(info); certSalida.sign(emisorClavePrivada,algSigEmisor); certSalida.derEncode(out); Es probable que se genere en el futuro un API standard ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (7) Verificar código firmado Se debe ... Utilizar autentificación para verificar de donde proviene el código Ejecutar el código con las normas de permisos dependiendo del origen Se dispone inicialmente de un archivo AppletFirmado.jar firmado y se crea un almacen certs.store con el certificado de ACME. Con esto se define el archivo de permiso applet.policy: keystore “file://home/midir.certs.store”, “JKS” grant signedBy “ACMEraiz” { permission java.io.FilePermission “<<ALL FILES>>”, “read”; }; Para correrlo desde appletviewer: appletviewer -J-DJava.security.policy=applet.policy AppletFirmado.html Para correrlo desde el browser debe agregarse a deployement.properties deployment.user.security.policy=file:///home/midir/applet.policy Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (8) Cifrado simétrico Se genera la clave con números aleatorios seguros: KeyGenerator genclaves = KeyGenerator.getInstance(“AES”); SecureRandom aleatorio = new SecureRandom(); genclaves.init(aleatorio); Key clave = genclaves.generateKey(); Se crea la instancia necesaria para cifrar con, por ejemplo, AES y usar update para cifrar texto en claro que se deja en un archivo de salida: Cipher cifrado = Cipher.getInstance(“AES”); int tamanoBloque = cifrado.getBlockSize(); byte[] bytesEntrada = new byte[tamanoBloque]; ... //leer bytesEntrada int tamanoSalida = cifrado.getOutputSize(longitudEntrada); byte[] bytesSalida = new byte[tamanoSalida]; int longitudSalida = cifrado.update(bytesEntrada,0,tamanoSalida); ... //Escribir los bytesSalida Se puede cifrar y descifrar por flujo continuo ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica (8) Cifrado asimétrico Ante el problema de la distribución de la clave simétrica y la gran cantidad (una para uso o cliente) se estila el uso de los algoritmos de clave pública. Se usan juntos pues los algoritmos de clave simétrica son mucho más rápidos. Se crea un par de claves: pública y privada. Se genera una clave simétrica que se cifra con la clave pública del destinatario: Key clave = ... // clave AES a compartir durante la sesion Key clavePublica = ... // clave publica RSA del destinatario Cipher cifrado = Cipher.getInstance(“RSA”); cifrado.init(Cipher.WRAP_MODE,clavePublica); byte[] claveCifrada = cifrado.wrap(clave); Finalmente se envía por la red un archivo la clave simétrica encriptada con WRAP y texto cifrado con la clave AES ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica Cifrado con JSSE (Java Secure Sockets Extension) Usando SSL (Sockets Secure Layer) como base para asegurar aplicaciones. Ofrece cifrado simétrico, autentificación del servidor con certificados, integridad y opcionalmente autentificación del cliente. Key clave = ... // clave AES a compartir durante la sesion Key clavePublica = ... // clave publica RSA del destinatario Cipher cifrado = Cipher.getInstance(“RSA”); cifrado.init(Cipher.WRAP_MODE,clavePublica); byte[] claveCifrada = cifrado.wrap(clave); Finalmente se envía por la red un archivo la clave simétrica encriptada con WRAP y texto cifrado con la clave AES ... Universidad Simón Bolívar Prof. Wílmer Pereira Especialización en Telematica