Introduccion
Hoy en día, la seguridad de la información es una de las prioridades de la industria de TI, especialmente teniendo en cuenta que para escuchar el tráfico en nuestro tiempo, prácticamente no se necesita conocimiento especializado: en Internet hay suficiente software y manuales detallados.
Por lo tanto, configuré y resolví el problema de escribir un servicio para intercambiar archivos diseñados de tal manera que estuviera protegido de manera máxima contra ataques de "hombre en el medio": los archivos transmitidos a través del servicio no deberían ir desde la tarjeta de red del dispositivo final en forma no cifrada, y descifrado, en consecuencia, tuvo que tener lugar en la máquina del destinatario final.
Selección de herramienta
Para escribir el lado del servidor de la aplicación, elegí el lenguaje de programación Java en combinación con el marco de desarrollo web Wicket. Este no es el único conjunto de herramientas en el que fue posible implementar las tareas, pero esta pila es suficiente para resolverlas, y Wicket, entre otras cosas, proporciona una interfaz muy conveniente para trabajar con componentes de páginas web. La interfaz no implicaba la presencia de algo complicado en ella: no me propuse la tarea de desarrollar una interfaz hermosa y amigable, la lógica de la aplicación era una prioridad más alta, por lo que me limité al paquete clásico HTML / CSS / JS.
El servidor se ejecuta en Ubunty 18.04, en el contenedor Docker, pero la configuración del servidor se analizará con más detalle a continuación, utilizando el contenedor de servlet Apache Tomcat. Otras tecnologías necesarias para construir una aplicación de este tipo, si es necesario, se mencionarán durante el artículo.
Configuración del servidor
Como ya se mencionó, la aplicación se encuentra en un servidor Linux, en particular, en Ubunty 18.04. Para aislar el sistema de otras aplicaciones, así como para simplificar la implementación, se decidió contener el proceso en contenedores, con la ayuda de Doker, asignar a la aplicación una cierta cantidad de CPU y memoria, abrir la lista de puertos y cerrar el resto. No describiré en detalle la instalación y configuración del servicio Docker, pero debo mencionar los puntos clave.
Se supone que todos los comandos se ejecutan con privilegios de administrador.
Para comenzar, era necesario instalar e iniciar el servicio correspondiente, con comandos
Después de eso, descargue y ejecute la imagen del sistema operativo; no necesitamos nada más del contenedor en este momento:
El estilo de la solicitud de entrada debería haber cambiado: ahora estamos en el contenedor y podemos trabajar sin temor por el entorno real. Quizás algunos paquetes a los que estamos acostumbrados en una máquina real en el contenedor no serán suficientes, pero el procedimiento para instalarlos no es diferente de instalar un paquete en una máquina real, por lo que no me centraré en esto. Se supone además que todas las acciones se realizan dentro del contenedor.
Configuración del contenedor de servlets de Tomcat
Entonces, en este momento, asumimos que estamos trabajando en un sistema operativo limpio, donde todo está por defecto. Para poder alojar una aplicación Java en él, debe instalar jdk y jvm directamente en él, y establecer variables de entorno con rutas de instalación. En consecuencia, aquí solo daré comandos clave, con más detalle el proceso de instalación de Java en Linux puede considerarse en la red.
Para instalar JDK y JRE, y luego ajustar la variable de entorno JAVA_HOME, debe ingresar estos comandos en consecuencia:
Luego puede ir directamente a la instalación de Tomcat.
La clave a la que debe prestar atención, otorgando derechos de administrador y administrador, de forma predeterminada, no puede administrar aplicaciones de forma remota, no en la máquina local. De lo contrario, todo es estándar: descargar e instalar el paquete e iniciar el servicio.
Escribir un programa
El programa, como se mencionó anteriormente, consistirá en un servidor y una parte del cliente. Consideraremos brevemente el lado del cliente; el archivo Editor y las funciones de cifrado son de máximo interés.
Sin embargo, lo primero es lo primero.
Comencemos con el lado del cliente.
Según lo planeado, debería dar acceso a la lista de archivos almacenados en el servidor. Como se suponía que la solución era barata, transferí la lista de archivos en forma de texto para un cuadro de texto inactivo, y adjunté un formulario en la parte inferior para seleccionar el nombre del archivo a abrir.
Fig. 1 - Interfaz de almacenamiento de archivosCuando ingresa un nombre de archivo en la línea de entrada, el archivo se abre en otra ventana, en el editor. Código Java para la página que enumera los archivos:
File directory = new File(wayToDirs); String files = new String(); directory.mkdirs(); for (File i : directory.listFiles()) files+=i.getName()+"\n"; TextArea listOfFiles = new TextArea("files", Model.of("")); listOfFiles.setEnabled(false); listOfFiles.setDefaultModelObject(files); add(listOfFiles);
El HTML correspondiente es aún más trivial:
<textarea wicket:id = "files" class="files" id = "files" /> <form wicket:id="selector" > <textarea wicket:id="name" class="input"/> <button wicket:id="opener" class="input" style="width: 10%"></button> </form>
Como ya se mencionó anteriormente, la página con el editor y el cifrado representan directamente el interés. Allí, se lleva a cabo la apertura, modificación, cifrado y descifrado del archivo requerido, así como su almacenamiento.
Fig. 2 - interfaz de editorComencemos con el lado del servidor: es más fácil de entender. Desde el punto de vista del servidor, la aplicación debe hacer clic en la tecla para aceptar texto de la ventana de entrada de texto, aceptar el nombre del archivo de la línea correspondiente y guardar el documento resultante. En realidad, esto es lo que sucede en el listado a continuación.
TextField wayToSaveFile = new TextField("wayToSaveFile", Model.of("")); Button saveButton = new Button("save") { @Override public void onSubmit() { super.onSubmit(); File file = new File(DirectoryInterface.wayToDirs+"/" + wayToSaveFile.getInput()); try { FileWriter fw = new FileWriter(file); fw.write(textArea.getDefaultModelObject().toString()); fw.close(); } catch (Exception e) { System.out.println(e.getMessage()); debud.setDefaultModelObject(e.getMessage()); } } };
Pero al momento de guardar, el archivo ya está encriptado. Para hacer esto, los botones "Cifrar" y "Descifrar" se implementan en la parte del cliente, que se utilizan para el cifrado y descifrado. El cifrado se realiza de acuerdo con el algoritmo de Vigenere. Diré un poco más al respecto por separado, pero se muestra en las funciones.
En consecuencia, el código del cliente se ve así:
<script> function encryptFun(){ let outputString = ""; let inputString = document.getElementById("text").value; let password = document.getElementById("passwordTextField").value; for (let i = 0; i<inputString.toString().length-1; i++) outputString+=(String.fromCharCode((inputString.toString().charCodeAt(i)+256-password.toString().charCodeAt(i%password.toString().length))%256)); document.getElementById("text").value = outputString; return outputString; }; function decryptFun(){ let outputString = ""; let inputString = document.getElementById("text").value; let password = document.getElementById("passwordTextField").value; for (let i = 0; i<inputString.toString().length-1; i++) outputString+=(String.fromCharCode((inputString.toString().charCodeAt(i)+256+password.toString().charCodeAt(i%password.toString().length))%256)); document.getElementById("text").value = outputString; return outputString; }; </script> … <!— - <input wicket:id="wayToSaveFile"class="input"> <button wicket:id = "save" class="input">Save</button> <input id = "passwordTextField" class="input"> <button id = "encrypt" onclick="encryptFun()" class="input"></button> <button id = "decrypt" onclick="decryptFun()" class="input"></button>
Por lo tanto, después de presionar el botón de cifrado, el texto en el campo de entrada se reemplaza con el texto devuelto por la función de cifrado, y viceversa.
La versión actual de la aplicación no determina automáticamente si el texto está cifrado, por lo que cualquier texto que no esté conectado se considera cifrado.
Algoritmo de cifrado
Elegí el algoritmo de cifrado de Vigenere, que es una variación del cifrado de César con un cambio variable por palabra clave. El código fuente de entrada y la clave de longitud arbitraria. Luego, el miembro i-ésimo del texto fuente se desplaza por el número de secuencia en el alfabeto del símbolo de contraseña j-ésima, donde j = i% (longitud de la contraseña). El algoritmo no es el más óptimo o resistente al análisis, sin embargo, con una longitud de contraseña suficiente proporciona cierta fiabilidad.
Según los materiales de la literatura sobre criptoanálisis, el cifrado de Vigenere "difumina" las características de las frecuencias de aparición de caracteres en el texto, pero quedan algunas características de la aparición de caracteres en el texto. La principal desventaja del cifrado Vigenere es que su clave se repite. Por lo tanto, un simple criptoanálisis de un cifrado se puede construir en dos etapas:
- Búsqueda de longitud de clave. Puede analizar la distribución de frecuencia en texto cifrado con diezmado diferente. Es decir, tome un texto que incluya cada 2ª letra del texto cifrado, luego cada 3ª, etc. Tan pronto como la distribución de frecuencia de las letras sea muy diferente del uniforme (por ejemplo, por entropía), podemos hablar sobre la longitud de la clave encontrada.
- Criptoanálisis. Un conjunto de l cifrados César (donde l es la longitud de la clave encontrada), que individualmente se pueden descifrar fácilmente.
Las pruebas de Friedman y Kassiski pueden ayudar a determinar la longitud de la clave.
Una discusión más detallada del truco de Vizhener está nuevamente fuera del alcance de este artículo.
Conclusiones
En el marco de este artículo, se escribió un algoritmo para escribir una aplicación que proporciona almacenamiento seguro y transmisión de archivos de texto a través de la red que requieren acceso compartido.
Algunas de las decisiones expresadas anteriormente son controvertidas o no óptimas, pero se indican la lógica básica y las cosas fundamentales.
github.com/Toxa-p07a1330/encriptedStorage