Security & Privacy
Construcción de app chat con cifrado E2EE y cumplimiento GDPR
53 min
cómo hacer una aplicación de chat compatible con el gdpr introducción ¡hola comunidad de back4app! este es un tutorial de invitado del equipo de virgil security, inc https //virgilsecurity com/?utm source=back4app\&utm medium=blog\&utm campaign=e2eechat somos la tecnología criptográfica detrás de la mensajería cifrada de extremo a extremo de twilio https //www twilio com/blog/2016/05/introducing end to end encryption for twilio ip messaging with virgil security html nuestros amigos de back4app nos pidieron que te mostremos cómo construir una aplicación de chat cifrada de extremo a extremo sobre back4app en esta publicación, te guiaremos a través de los pasos para hacer que una simple aplicación de mensajería de back4app para android sea cifrada de extremo a extremo ¿estás listo? pd si no te importan los detalles, simplemente salta al final de la publicación y descarga el producto final ¿qué es el cifrado de extremo a extremo? primero, comencemos con un rápido repaso de qué es e2ee (cifrado de extremo a extremo) y cómo funciona e2ee es simple cuando escribes un mensaje de chat, se cifra en tu dispositivo móvil (o en tu navegador) y se descifra solo cuando tu compañero de chat lo recibe y quiere mostrarlo en la ventana de chat el mensaje permanece cifrado mientras viaja por wi fi e internet, a través de la nube / servidor web, en una base de datos, y de regreso a tu compañero de chat en otras palabras, ninguna de las redes o servidores tiene idea de lo que ustedes dos están chateando lo que es difícil en el cifrado de extremo a extremo es la tarea de gestionar las claves de cifrado de manera que solo los usuarios involucrados en el chat puedan acceder a ellas y nadie más y cuando escribo “nadie más”, lo digo en serio incluso los empleados de tu proveedor de nube o incluso tú, el desarrollador, quedan fuera; no se permiten errores accidentales https //techcrunch com/2017/11/29/meet the man who deactivated trumps twitter account/ o miradas forzadas legalmente escribir criptografía, especialmente para múltiples plataformas, es difícil generar números verdaderamente aleatorios, elegir los algoritmos correctos y seleccionar los modos de cifrado adecuados son solo algunos ejemplos que hacen que la mayoría de los desarrolladores levanten las manos al aire y terminen simplemente no haciéndolo esta publicación de blog te mostrará cómo ignorar todos estos detalles molestos y encriptar de extremo a extremo de manera rápida y sencilla utilizando el sdk de virgil para una introducción, así es como actualizaremos la aplicación de mensajería de back4app para que sea encriptada de extremo a extremo durante el registro generaremos las claves privadas y públicas individuales para los nuevos usuarios (recuerda la clave pública del destinatario encripta los mensajes y la clave privada correspondiente del destinatario los desencripta) antes de enviar mensajes, encriptarás los mensajes de chat con la clave pública del destinatario después de recibir mensajes, desencriptarás los mensajes de chat con la clave privada del destinatario publicaremos las claves públicas de los usuarios en el servicio de tarjetas de virgil para que los usuarios de chat puedan buscarse entre sí y encriptar mensajes el uno para el otro las claves privadas permanecerán en los dispositivos de los usuarios mantenlo simple esta es la implementación más simple posible de un chat e2ee y funciona perfectamente para aplicaciones de chat simples entre 2 usuarios donde las conversaciones son efímeras y está bien perder el historial de mensajes si se pierde un dispositivo con la clave privada en él ¡está bien, suficiente charla! vamos a ponernos a codificar comenzaremos guiándote a través de la configuración de la aplicación de android, luego, agregaremos algo de código para hacer que la aplicación sea cifrada de extremo a extremo requisitos previos para completar este tutorial, necesitas android studio una aplicación creada en back4app sigue el tutorial crear nueva aplicación para aprender cómo crear una aplicación en back4app regístrate para una cuenta de virgil security (crearemos la aplicación más tarde) configuramos la aplicación de mensajería “limpia” de back4app 1 configura tu servidor de aplicaciones comencemos con el despliegue de la función en la nube para esto, necesitarás encontrar main js main js y package json package json en el directorio scripts scripts ; abre main js main js con tu editor favorito 1 1) obtén las credenciales de back4app abre dashboard dashboard de tu aplicación > configuración de la aplicación configuración de la aplicación > seguridad y claves seguridad y claves en main js main js , reemplaza parse app id parse app id con tu id de aplicación id de aplicación y parse rest api key parse rest api key con tu clave api rest clave api rest 1 2) obtén credenciales de virgil crea una aplicación en virgil dashboard https //dashboard virgilsecurity com/ abre tu nueva aplicación virgil, navega a la sección e3kit y genera un env env archivo en la sección e3kit en la barra lateral izquierda copia los valores de app id app id , app key app key , y app key id app key id del archivo env env reemplace los valores copiados en su main js main js archivo en los campos correspondientes ( main js main js de scripts scripts directorio) 1 3) desplegar función de código en la nube abra el “tablero” de back4app de su aplicación > “núcleo” > funciones de código en la nube; haz clic en +agregar y selecciona tu main js y package json (del directorio de scripts), después mueve ambos a la carpeta de la nube; haz clic en desplegar 2 iniciar la aplicación de demostración de back4app kotlin limpia no olvides configurar primero la función de código en la nube de back4app es una parte obligatoria de esta demostración después de esto, sigue los siguientes pasos 2 1) importar proyecto en android studio abre android studio > archivo archivo > nuevo nuevo > proyecto desde control de versiones proyecto desde control de versiones > git git url del repositorio de git https //github com/virgilsecurity/chat back4app android revisa la clean chat kt clean chat kt rama ¡importante! selecciona el tipo de árbol de archivos “proyecto” se utilizará a lo largo de todo el tutorial 2 2) configurar las credenciales de back4app en el proyecto abre el “tablero” de back4app de tu aplicación > “configuraciones de la aplicación” > “seguridad y claves”; ve al /app/src/main/res/values/strings xml /app/src/main/res/values/strings xml archivo en tu proyecto de android y reemplaza your back4app app id your back4app app id con tu id de aplicación id de aplicación y your back4app client key your back4app client key con tu clave de cliente clave de cliente 2 3) configurar db abre back4app “dashboard” > “core” > “database browser” > “crear una clase” y crea clases de personalizado personalizado tipo llamado mensaje mensaje y chatthread chatthread ; 2 4) configurar consulta en vivo regresa a tu cuenta de back4app https //dashboard back4app com/apps/#!/admin presiona el configuración del servidor configuración del servidor botón en tu aplicación encuentra el bloque “alojamiento web y consulta en vivo” abre la configuración de consulta en vivo y verifica la activar alojamiento activar alojamiento opción elige un nombre para tu subdominio para activar la consulta en vivo para las 2 clases que creaste mensaje mensaje y chatthread chatthread copia el nuevo nombre de tu subdominio y haz clic en el botón guardar regresa a /app/src/main/res/values/strings xml /app/src/main/res/values/strings xml y pega “nombre de subdominio” que ingresaste arriba en el back4app live query url back4app live query url en lugar de “tunombredesubdominio” después de estos pasos, podrás presionar el botón ejecutar en android studio y hacer que el ejemplo funcione usa un emulador o un dispositivo real para probarlo 3 ejecutar la demostración limpia para ver el resultado de ejecutar la versión limpia de la demostración, necesitarás registrar 2 usuarios; iniciar una conversación entre ellos y enviar un par de mensajes; 3\ abre back4app “dashboard” > “core” > “database browser” > “message” si todo funcionó, deberías ver la aplicación de mensajería de chat apareciendo registra dos usuarios y envía algunos mensajes entre ellos deberías ver nuevos datos apareciendo en la mensaje mensaje clase ten en cuenta que puedes ver en el servidor de qué están chateando tus usuarios siguiente cierra tu interfaz de chat y pasa al siguiente paso agregar cifrado e2ee ¡ahora, cifremos esos mensajes de extremo a extremo! al final de esta parte, así es como se verán tus mensajes de chat en el servidor ¿puedes notar la diferencia? ¿cómo llegamos allí? obviamente, necesitamos implementar cifrado de extremo a extremo, lo que significa que nuestra aplicación necesita generar el par de claves privada y pública como parte del registro almacenar la clave privada en el almacenamiento de claves en el dispositivo del usuario publicar la clave pública en el servicio de tarjetas de virgil como una “tarjeta virgil” para que otros usuarios la descarguen y cifren mensajes con ella cifrar mensajes con la clave pública y firmar con la clave privada; descifrar mensajes con la clave privada y verificar con la clave pública para esto, necesitaremos agregar e3kit a nuestra aplicación de demostración limpia y algo más de código para implementar todo lo que se describió anteriormente pero antes de comenzar, aclaremos dos términos importantes para ti ¿qué es una tarjeta virgil y una clave privada? tarjeta virgil las tarjetas virgil llevan las claves privadas de los usuarios las tarjetas virgil se publican en el servicio de tarjetas de virgil (imagina que este servicio es como una guía telefónica) para que otros usuarios las recuperen alice necesita recuperar la clave pública de bob para cifrar un mensaje para bob usando esa clave clave privada una parte privada de la clave de cifrado recuerda, las claves privadas pueden descifrar datos que fueron cifrados usando la clave pública correspondiente 1 agregar e3kit a la demostración limpia de e3kit back4app kotlin en el nivel de la aplicación ( módulo app módulo app ) gradle en /app/build gradle /app/build gradle añade (pero no sincronices los scripts de gradle todavía) añade lo siguiente al final de tu nivel de proyecto /build gradle /build gradle ( proyecto chat back4app android proyecto chat back4app android ) ahora puedes sincronizar los scripts de gradle; actualiza tu appvirgil appvirgil clase añadiendo nuevos campos presione alt+ enter alt+ enter para importar las bibliotecas necesarias en la clase 2 autenticar usuarios con back4app cloud code en /virgilsecurity/virgilback4app/model/ /virgilsecurity/virgilback4app/model/ directorio, crea clases de datos authenticateresponse authenticateresponse y virgiljwtresponse virgiljwtresponse que representan respuestas de las funciones de cloud code en /virgilsecurity/virgilback4app/util/ /virgilsecurity/virgilback4app/util/ crea un authrx authrx objeto que implementa llamadas a funciones de cloud code (no olvides importar todas las bibliotecas necesarias después) 1 object authrx { 2 3 / 4 you can call it only after successful \[authenticate] 5 / 6 fun virgiljwt(sessiontoken string) = single create\<string> { emitter > 7 val requestparams = mutablemapof\<string, string>() apply { 8 put("sessiontoken", sessiontoken) 9 } 10 11 parsecloud callfunctioninbackground\<map\<string, any>>( 12 key virgil jwt, 13 requestparams 14 ) { virgiljwt, exception > 15 if (exception == null) 16 emitter onsuccess(virgiljwt\[key token] tostring()) 17 else 18 emitter onerror(exception) 19 20 } 21 } 22 23 private const val key virgil jwt = "virgil jwt" 24 private const val key token = "token" 25 } 3 almacenar virgil jwt localmente el token de virgil recibido de las funciones de cloud code necesita ser almacenado localmente actualicemos preferencias preferencias clase en /virgilsecurity/virgilback4app/util/ /virgilsecurity/virgilback4app/util/ definir constante agregar funciones setvirgiltoken setvirgiltoken , virgiltoken virgiltoken y clearvirgiltoken clearvirgiltoken kotlin fun setvirgiltoken(virgiltoken string) { with(sharedpreferences edit()) { putstring(key virgil token, virgiltoken) apply() } } fun virgiltoken() string? { with(sharedpreferences) { return getstring(key virgil token, null) } } fun clearvirgiltoken() { with(sharedpreferences edit()) { remove(key virgil token) apply() } } 1 virgil token should be reset on logout let's add `preferences instance(this) clearvirgiltoken()` line into `initdrawer` function of `threadslistactivity` class (in ` /virgilsecurity/virgilback4app/chat/contactslist/`) 2 kotlin 3 private fun initdrawer() { 4 5 nvnavigation setnavigationitemselectedlistener { item > 6 r id itemlogout > { 7 dldrawer closedrawer(gravitycompat start) 8 presenter disposeall() 9 showbaseloading(true) 10 // new code >> 11 preferences instance(this) clearvirgiltoken() 12 // << new code 13 14 } 15 } 16 } 4 modificar el registro de usuario e3kit se encarga de tus claves privadas y públicas para generarlas durante el proceso de registro, necesitaremos hacer lo siguiente en /virgilsecurity/virgilback4app/util/ /virgilsecurity/virgilback4app/util/ crea rxethree rxethree clase ahora, añade initethree initethree función que inicializa la instancia de e3kit en rxethree rxethree clase 1 import com virgilsecurity android common model ethreeparams 2 import com virgilsecurity android ethree interaction ethree 3 4 5 fun initethree(identity string, verifyprivatekey boolean = false) 6single\<ethree> = single create { e > 6 val params = ethreeparams(identity, {preferences virgiltoken()!!}, context) 7 val ethree = ethree(params) 8 if (verifyprivatekey) { 9 if (ethree haslocalprivatekey()) { 10 e onsuccess(ethree) 11 } else { 12 e onerror(keyentrynotfoundexception()) 13 } 14 } else { 15 e onsuccess(ethree) 16 } 17 } añadir registerethree registerethree función que registra un nuevo usuario en rxethree rxethree clase e3kit genera un par de claves durante un registro la clave privada generada se almacena en el almacenamiento local, y la clave pública se publica en los servicios de virgil como una tarjeta de virgil 1 import com android virgilsecurity virgilback4app appvirgil 2 import com virgilsecurity common callback oncompletelistener 3 import io reactivex completable 4 5 6 fun registerethree() completable = completable create { e > 7 appvirgil ethree register() addcallback(object oncompletelistener { 8 override fun onerror(throwable throwable) { 9 e onerror(throwable) 10 } 11 12 override fun onsuccess() { 13 e oncomplete() 14 } 15 16 }) 17 } hagamos algunas actualizaciones a loginpresenter loginpresenter clase (en /virgilsecurity/virgilback4app/auth/) /virgilsecurity/virgilback4app/auth/) agregar rxethree rxethree campo 1 private val rxethree = rxethree(context) actualiza la requestsignup requestsignup función para ejecutar el registro con e3kit 1 fun requestsignup(identity string, onsuccess () > unit, onerror (throwable) > unit) { 2 val password = generatepassword(identity tobytearray()) 3 4 val disposable = rxparse signup(identity, password) 5 subscribeon(schedulers io()) 6 observeon(schedulers io()) 7 // new code >> 8 tosingle { parseuser getcurrentuser() } 9 flatmap { authrx virgiljwt(it sessiontoken) } 10 map { preferences setvirgiltoken(it) } 11 flatmap { rxethree initethree(identity) } 12 map { appvirgil ethree = it } 13 flatmap { rxethree registerethree() tosingle { unit } } 14 // << new code 15 observeon(androidschedulers mainthread()) 16 // updated code >> 17 subscribeby( 18 onsuccess = { 19 onsuccess() 20 }, 21 onerror = { 22 onerror(it) 23 } 24 ) 25 // << updated code 26 27 compositedisposable += disposable 28 } 5 modificar funciones de inicio de sesión ahora, hagamos cambios en el requestsignin requestsignin método de loginpresenter loginpresenter clase (en /virgilsecurity/virgilback4app/auth/) /virgilsecurity/virgilback4app/auth/) 1 fun requestsignin(identity string, 2 onsuccess () > unit, 3 onerror (throwable) > unit) { 4 5 val password = generatepassword(identity tobytearray()) 6 7 val disposable = rxparse login(identity, password) 8 subscribeon(schedulers io()) 9 observeon(schedulers io()) 10 // new code >> 11 flatmap { authrx virgiljwt(it sessiontoken) } 12 map { preferences setvirgiltoken(it) } 13 flatmap { rxethree initethree(identity, true) } 14 map { appvirgil ethree = it } 15 // << new code 16 observeon(androidschedulers mainthread()) 17 // updated code >> 18 subscribeby( 19 onsuccess = { 20 onsuccess() 21 }, 22 onerror = { 23 onerror(it) 24 } 25 ) 26 // << updated code 27 28 compositedisposable += disposable 29 } 6 obtener la lista de chats existentes a continuación, agregue funciones que manejen la inicialización de e3kit en threadslistfragment threadslistfragment clase (en /virgilsecurity/virgilback4app/chat/contactslist/) /virgilsecurity/virgilback4app/chat/contactslist/) 1 private fun oninitethreesuccess() { 2 presenter requestthreads(parseuser getcurrentuser(), 3 20, 4 page, 5 const tablenames created at criteria, 6 ongetthreadssuccess, 7 ongetthreadserror) 8 } 9 10 private fun oninitethreeerror(throwable throwable) { 11 showprogress(false) 12 if (adapter itemcount == 0) 13 tverror visibility = view\ visible 14 15 utils toast(activity, utils resolveerror(throwable)) 16 } actualizar postcreateinit postcreateinit función para inicializar e3kit 1 override fun postcreateinit() { 2 3 presenter = threadslistfragmentpresenter(activity) 4 5 showprogress(true) 6 // updated code >> 7 if (appvirgil isethreeinitialized()) { 8 presenter requestthreads(parseuser getcurrentuser(), 9 20, 10 page, 11 const tablenames created at criteria, 12 ongetthreadssuccess, 13 ongetthreadserror) 14 } else { 15 presenter requestethreeinit(parseuser getcurrentuser(), oninitethreesuccess, oninitethreeerror) 16 } 17 // << updated code 18 } y agrega el siguiente código en threadslistfragmentpresenter threadslistfragmentpresenter clase en virgilsecurity virgilback4app chat contactslist/ virgilsecurity virgilback4app chat contactslist/ agregar campo 1 private val rxethree = rxethree(context) y función 1 fun requestethreeinit(currentuser parseuser, onsuccess () > unit, onerror (throwable) > unit) { 2 val disposable = rxethree initethree(currentuser username) 3 subscribeon(schedulers io()) 4 observeon(androidschedulers mainthread()) 5 subscribeby( 6 onsuccess = { 7 appvirgil ethree = it 8 onsuccess() 9 }, 10 onerror = { 11 onerror(it) 12 } 13 ) 14 15 compositedisposable += disposable 16 } en este punto, podemos registrar/iniciar sesión a un usuario y crear un nuevo chat con otro usuario ahora añadamos cifrado para nuestros mensajes 7 cifrado y descifrado de mensajes añadamos findcard findcard función a rxethree rxethree clase (en /virgilsecurity/virgilback4app/util/ /virgilsecurity/virgilback4app/util/ ) que nos ayudará a obtener la última tarjeta de virgil por nombre de usuario 1 fun findcard(identity string) single\<card> = single create { e > 2 appvirgil ethree finduser(identity) addcallback(object onresultlistener\<card> { 3 override fun onerror(throwable throwable) { 4 e onerror(throwable) 5 } 6 7 override fun onsuccess(result card) { 8 e onsuccess(result) 9 } 10 11 }) 12 } cuando se abre un chat, debemos obtener la tarjeta de virgil del destinatario editar postcreateinit postcreateinit de chatthreadfragment chatthreadfragment clase (en /virgilsecurity/virgilback4app/chat/thread/ /virgilsecurity/virgilback4app/chat/thread/ ) reemplazando 1 presenter requestmessages(thread, 2 50, 3 page, 4 const tablenames created at criteria, 5 ongetmessagessuccess, 6 ongetmessageserror) código con uno nuevo 1 presenter requestcard(recipientid, 2 ongetcardsuccess, 3 ongetcarderror) y añade dos funciones 1 private fun ongetcardsuccess(card card) { 2 showprogress(false) 3 adapter interlocutorcard = card 4 presenter requestmessages(thread, 5 50, 6 page, 7 const tablenames created at criteria, 8 ongetmessagessuccess, 9 ongetmessageserror) 10 } 11 12 private fun ongetcarderror(t throwable) { 13 if (t is virgilcardisnotfoundexception || t is virgilcardserviceexception) { 14 utils toast(this, 15 "virgil card is not found \nyou can not chat with user without virgil card") 16 activity onbackpressed() 17 } 18 showprogress(false) 19 srlrefresh isrefreshing = false 20 locksendui(lock = false, lockinput = false) 21 22 utils toast(this, utils resolveerror(t)) 23 } ahora cambiemos chatthreadpresenter chatthreadpresenter añadir campos 1 private val ethree = appvirgil ethree 2 private lateinit var usercard card 3 private val rxethree = rxethree(context) agrega una función que obtenga una tarjeta virgil del destinatario 1 fun requestcard(identity string, 2 onsuccess (card) > unit, 3 onerror (throwable) > unit) { 4 5 val disposable = rxethree findcard(identity) 6 subscribeon(schedulers io()) 7 observeon(androidschedulers mainthread()) 8 subscribeby( 9 onsuccess = { 10 usercard = it 11 onsuccess(it) 12 }, 13 onerror = { 14 onerror(it) 15 } 16 ) 17 18 compositedisposable += disposable 19 } agrega cifrado de mensajes salientes en requestsendmessage requestsendmessage función 1 fun requestsendmessage(text string, 2 thread chatthread, 3 onsuccess () > unit, 4 onerror (throwable) > unit) { 5 6 val encryptedtext = ethree authencrypt(text, usercard) 7 val disposable = rxparse sendmessage(encryptedtext, thread) 8 9 } agrega la descifrado de todos los mensajes entrantes en chatthreadrvadapter chatthreadrvadapter clase (en /virgilsecurity/virgilback4app/chat/thread/) /virgilsecurity/virgilback4app/chat/thread/) agregar campos 1 private var ethree ethree = appvirgil ethree 2 lateinit var interlocutorcard card implementar la descifrado de mensajes en onbindviewholder onbindviewholder función 1 override fun onbindviewholder(viewholder recyclerview\ viewholder, position int) { 2 when (viewholder) { 3 is holdermessageme > { 4 val decryptedtext = ethree authdecrypt(items\[position] body) 5 viewholder bind(decryptedtext) 6 } 7 is holdermessageyou > { 8 val decryptedtext = ethree authdecrypt(items\[position] body, interlocutorcard) 9 viewholder bind(decryptedtext) 10 } 11 } 12 } 8 ejecutar la demostración completa de cifrado de extremo a extremo ahora, para ver el resultado de nuestra demostración completamente cifrada de extremo a extremo, sigue estos pasos nuevamente cerrar sesión del usuario anterior registrarse 2 nuevos usuarios; iniciar una conversación entre ellos y enviar un par de mensajes; abrir back4app “dashboard” > “core” > “database browser” > “message” ¡importante! tienes que cerrar sesión del usuario actual y registrar dos nuevos usuarios, después de eso puedes comenzar el chat e2ee con esos dos nuevos usuarios la razón es que tus dos primeros usuarios no tienen virgil card virgil card ’s, así que no puedes usar cifrar\descifrar para ellos { blockquote tip} ¡hecho! ahora puedes ver que los mensajes de los usuarios están cifrados y solo pueden ser accedidos en la aplicación por los propios usuarios cumplimiento de hipaa y gdpr el cifrado de extremo a extremo es una forma de cumplir con los requisitos técnicos de hipaa (la ley de portabilidad y responsabilidad de seguros de salud de los estados unidos de 1996) y gdpr (el reglamento general de protección de datos de la unión europea) si necesitas más detalles, regístrate para obtener una cuenta de virgil https //developer virgilsecurity com/account/signup?utm source=back4app\&utm medium=blog\&utm campaign=e2eechat , únete a nuestra comunidad de slack y contáctanos allí estamos felices de discutir tus propias circunstancias de privacidad y ayudarte a entender lo que se requiere para cumplir con los requisitos técnicos de hipaa y gdpr ¿a dónde ir desde aquí? proyecto final https //github com/virgilsecurity/chat back4app android/ si te perdiste piezas del rompecabezas, abre la rama del proyecto e2ee puedes insertar tus credenciales de aplicación en este código (como hiciste durante el artículo) y construir el proyecto puedes encontrar más información sobre lo que puedes construir con virgil security aquí https //virgilsecurity com/?utm source=back4app\&utm medium=blog\&utm campaign=e2eechat