Una introducción práctica a la infraestructura web de clave pública y los contratos inteligentes con la excusa de mejorar la consulta del estado de revocación de un certificado digital.
Cuando accedemos a un sitio web a través de nuestro navegador, esperamos que la información que intercambiamos con ese sitio sea confidencial e integra. No queremos que terceros vean nuestras credenciales de acceso al banco o que alguien sepa cuántas criptomonedas estamos comprando en nuestro exchange favorito. Por eso añadimos la “s” de seguro al “http” cada vez que introducimos una dirección web en la barra de direcciones de nuestro navegador. De hecho, hoy en día se suele añadir de forma automática, casi siempre.
Para lograr esa comunicación segura la información intercambiada se hace ininteligible para todo aquel que no tenga la clave. A esto se le llama cifrar la información. Tanto las peticiones de información que hace el navegador al sitio web, como la información de respuesta que el sitio web envía al navegador de vuelta están cifradas.
La clave secreta es acordada entre las dos partes, navegador y sitio web. A esa clave se le llama clave de sesión, porque se renegociará entre las partes cada poco tiempo. Si imaginamos que las claves son colores, la información cifrada por el servidor con un color sólo podrá ser descifrada por el navegador usando ese mismo color y ningún otro. Un adversario, alguien escuchando, sólo podrá ver la información intercambiada si conoce el color utilizado o es capaz de deducirlo. Este tipo de cifrado que usa una sóla clave se llama simétrico. Es muy adecuado porque el cifrado simétrico es muy rápido y seguro. Además nos permite, con ciertas cautelas, cifrar grandes cantidades de información.
Para lograr establecer una clave secreta compartida en presencia de terceros, las dos partes implicadas utilizan un protocolo, unas reglas de intercambio de clave. Siguiendo con la analogía de los colores, este protocolo se basa en la idea de que es fácil mezclar dos colores pero muy difícil separarlos después. Las dos partes se ponen de acuerdo públicamente en un color común inicial. Cada uno mezcla ese color común con su propio color secreto para obtener un color mezclado que enviará a la otra parte. Finalmente cada parte mezclará el color recibido con su color secreto obteniendo un color compartido igual que el de la otra parte. La siguiente figura lo explica muy bien:
En resumen, ambas partes mezclan el color común con los los dos colores secretos, uno de cada parte.
Este protocolo permite acordar el uso de una clave compartida de forma segura. Pero, en su versión básica o anónima, no garantiza con quién estoy intercambiando información. Es decir, no es autenticado. No se comprueba que las partes implicadas sean quienes dicen ser. Eso permitiría a un atacante colocarse entre las dos partes sin que ninguna de ellas se dé cuenta, creyendo ambas que sólo están ellas dos en la comunicación. Esto se llama ataque de intermediario o de hombre en medio.
Una solución para saber con quién estamos hablando, es que los mensajes enviados estén firmados digitalmente.
La firma digital es una de las aplicaciones de la criptografía asimétrica. En lugar de usar la misma clave para cifrar y descifrar una información, la criptografía asimétrica usa dos. Es decir, tenemos un par de claves vinculadas de tal forma que lo que cifra una de las claves sólo puede ser descifrado por la otra y ninguna otra. La clave que se usa para cifrar información es una clave secreta, privada. La otra es la clave pública, se la podemos entregar a todo el mundo. Con la clave pública que puede descifrar la información que he cifrada con la clave privada.
Hay que recalcar que en realidad estamos enviando dos objetos. Por un lado el mensaje público y por otro una firma de ese mensaje. Si cambio el mensaje la firma no será válida. La firma es el mensaje cifrado con la clave privada. Pero, la criptografía de clave asimétrica es lenta, muy lenta, y no puede cifrar mensajes muy largos. Por eso lo que se cifra, lo que se firma, es una representación del mensaje, llamada huella digital. Esta representación es otro mensaje de un tamaño fijo más pequeño, pero lo suficientemente grande para evitar colisiones. De esta forma todo mensaje, de cualquier tamaño, puede representarse por esa huella digital. El cambio de una sola letra en el mensaje cambiará completamente su huella.
Esto quiere decir que aquí no hay confidencialidad de ningún tipo. Todo el mundo puede descifrar mis mensajes cifrados. Sólo tienen que usar la clave pública, que es pública. Tiene sentido, ya que se trata de una firma. Lo que sí garantiza es que sólo yo pude cifrar ese mensaje, como controlador exclusivo de esa clave secreta. Y es lo que equivale a una firma.
En resumen para firmar un mensaje calculamos su huella digital y la ciframos obteniendo un mensaje que llamaremos firma. Para verificar una firma la desciframos y verificamos que el valor obtenido es la huella digital del mensaje.
Los navegadores tienen que obtener las claves públicas de las web a las que accedemos y estar seguros de que son auténticas para lograr establecer una comunicación segura. Cuando no podemos verificar si las claves públicas corresponden a la web a la que estamos intentando acceder nuestro tráfico podría ser interceptado y manipulado por un tercero sin darnos cuenta.
Un certificado digital vincula una clave pública con una identidad y otros datos. En el caso de un certificado de autenticación web la identidad se refiere al nombre de dominio. Además los certificados digitales incluyen la fecha desde la que es válido, que suele ser la fecha de emisión, y la fecha hasta será válido.
Todos los datos que aparecen juntos en un certificado son firmados digitalmente por una autoridad de certificación (AC), también llamada entidad emisora. La AC se encarga de verificar la validez de todos esos datos incluidos en el certificado antes de emitirlo, antes de firmarlo.
La AC firma digitalmente el certificado usando su propio certificado. Como todo certificado es emitido por una AC se crea una cadena de confianza de autoridades de certificación. Esta cadena acaba cuando una AC emite un certificado que se verifica a sí mismo y que se llama certificado raíz.
Establecer la identidad de un certificado sin una autoridad de certificación es un viejo anhelo. Hasta hoy no lo hemos logrado, pero estamos cerca.
Todos los navegadores vienen preconfigurados con una serie de AC que permiten verificar y aceptar certificados de autenticación de sitios web emitidos por esas AC. Cada navegador tiene su propio procedimiento para añadir o eliminar las AC que vienen preconfiguradas. Un certificado emitido por una AC puede ser aceptado en un navegador y no en otro, y esto suele crear problemas extraños. Al final, un puñado de personas responsables de cada navegador deciden qué AC son incluidas en ese “pequeño” listado de AC confiables que garantizan nuestra seguridad en las comunicaciones. Siempre ha sido así en el mundo humano.
Las AC pueden emitir certificados para cualquier dominio a su discreción, como hizo Symantec emitiendo certificado “de prueba” con el dominio de google. Esos certificados podrían haber sido usados para lanzar ataques de phising muy efectivos.
Con el poder que tienen las AC las hace objetivos muy apetecibles para los hackers, como lo fue DigiNotar. También los gobiernos pueden abusar de su poder como hizo Siria intentando un ataque de intermediario con FaceBook.
Una AC puede ser deshonesta, o puede estar comprometida de alguna forma. Sea queriendo o sin querer, una AC comprometida podrá emitir certificados con claves públicas falsas, para cualquier dominio. Todo navegador que confie en esa AC maliciosa aceptará sin pestañear los certificados por ella emitidos. Un atacante puede falsificar certificados para cualquier web que desee y no habrá forma de diferenciarlos de certificados auténticos.
La infraestructura de clave pública son las políticas y procedimientos, el software, el hardware que se necesitan para gestionar certificados digitales. Gestionar quiere decir cómo emitirlos, usarlos, almacenarlos, revocarlos, eliminarlos…
Algunas de las funciones de una AC pueden ser delegadas. Por ejemplo, la AC delega en la autoridad de registro (AR) la verificación de los datos de identidad que aparecen en un certificado.
Las entidades de validación (AV) proporcionan los servicios necesarios para comprobar que un certificado sigue siendo válido en un momento determinado. El certificado tiene entre sus datos un periodo de validez. No hace falta una AV para verificar si el certificado está dentro de ese periodo, más allá de tener un reloj correctamente sincronizado. Pero sí en los casos en los que el certificado o alguno de sus datos dejan de ser válidos por alguna otra razón.
El modelo de infraestructura pública es frágil. Por una parte cualquier AC puede emitir un certificado para cualquier dominio. Por otra parte, no hay forma de saber qué certificados ha emitido una AC. Para reducir el problema se han propuesto muchas soluciones, que no han funcionado. Actualmente se usa un registro público transparente y verificable de la emisión de certificados de cada AC para reducir los certificados emitidos por error o malintencionadamente.
El tiempo de vida de un certificado es relativamente largo, en el orden de meses. Algún dato del certificado puede dejar de ser correcto, se puede perder el control de la clave privada asociada al certificado, la entidad emisora puede haber sido comprometida, se ha perdido… Pero el certificado es un objeto inmutable. Por eso se establecen distintos mecanismos que permiten consultar el estado del certificado. Por lo menos el estado que tenía hace poco tiempo, en el orden de horas.
Un certificado está “revocado” cuando un pierde su validez por otras causas que no sean la expiración.
Los servicios para verificar el estado de revocación de un certificado los proporciona la AV y los medios para acceder a ellos suelen ir en el propio certificado. Estos servicios son otro talón de aquiles de la infraestructura de clave pública.
Un navegador no puede estar seguro de que los servidores de la AV serán accesibles para verificar el estado de un certificado. Por ejemplo, un portal captivo sólo permite autenticarse en una dirección HTTPS pero bloquea el tráfico a cualquier otra dirección, incluyendo los servicios de consulta de revocación del certificado. Si el navegador no acepta el certificado cuando no pueda verificar su estado no se podrá acceder al sitio web, dejando de funcionar. Por otra parte, un corte, por pequeño que sea, de los servicios de consulta de estado supone un punto único de fallo.
Por eso muchos navegadores consideran los errores de red a la hora de consultar el estado de revocación de un certificado como fallos que simplemente son ignorados. Además los costes de la verificación son mayores tiempos de conexión, páginas más lentas y posibles problemas de privacidad porque las AV pueden registrar los dominios consultados y desde qué ip… Por eso algunos navegadores utilizan un mecanismo que envía las listas de certificados revocados de todas la AC a cada navegador, que complica ligeramente las cosas a un atacante.
Uno de los primeros servicios para la consulta del estado de revocación de un certificado fue utilizar una lista (CRL). En esa lista aparece el identificador del certificado junto a un estado de revocación. Si el certificado no aparece en la lista será válido. Los certificados suelen incluir uno o varios servicios desde los que descargar esas listas.
Las listas se actualizan cada cierto tiempo para reflejar los cambios. Uno de los problemas con estas listas es que crecen de manera desmedida con el tiempo, pueden ser problemáticas respecto a la privacidad de datos, o simplemente no están lo suficientemente actualizadas…
Este pequeño caos con las listas de revocación desembocó en la creación de un nuevo servicio que permitiera comprobar el estado de un certificado en concreto directamente (OCSP).En lugar de descargar una lista cada cierto tiempo los navegadores pueden realizar las consultas cada vez y sólo para el certificado que están usando. Pero esto creó una dependencia mucho mayor entre la AV y el navegador. Los ataques de denegación de servicio son más fáciles. La disponibilidad del servicio se vuelve más crítica. La privacidad empeora…
Para aliviar alguno de estos problemas se establecen protocolos para mandar el estado de revocación junto con el certificado del sitio web pero crean sus propios problemas.
Cada día aparecen nuevas propuestas para la consulta del estado de revocación de un certificado, como solución específica o englobada en una solución más general.
La cadena de bloques (blockchain) es un registro público y descentralizado de datos que se actualiza, sólo añadiendo entradas en forma de transacciones, mediante el consenso de todos los participantes. Los datos se almacenan de forma redundante en muchos sitios. Bitcoin, el precursor de la blockchain, permite mantener un registro público de los intercambios de valor entre los participantes. Es muy difícil de censurar ya que no tiene una entidad central que lo controle. Será muy difícil prohibirlo sin que se considere un ataque a la propia libertad de expresión y el derecho a comunicarnos. Ethereum por su parte hace uso de una blockchain para mantener un consenso no sólo sobre la información, sino sobre pequeñas aplicaciones que manipulan esa información, convirtiéndose en una especie de ordenador global público compartido. Ethereum llama a esas pequeñas aplicaciones contratos inteligentes.
La blockchain proporciona nuevos casos de uso que previamente no se podían resolver de forma satisfactoria Proporciona inmutabilidad, una vez escrito un registro en un bloque no se puede alterar porque requeriría alterar todos los bloques anteriores que requieren el consenso de todos los participantes. Es un sistema descentralizado que evita muchos ataques como la denegación de servicio. Es transparente, la blockchain es visible públicamente. Es rápida y más barata, en relación a sistemas equivalentes con las mismas garantías.
Vamos a explorar las posibilidades de utilizar blockchain como registro del estado de revocación de los certificados emitidos por una AC. Lo llamaremos Decentralized Certificate Revocation List (DCRL).
Definiremos y desplegaremos un contrato inteligente de Ethereum para gestionar la lista. Definiremos una nueva extensión del certificado que incluya la dirección del contrato inteligente. Crearemos unos certificados de ejemplo e implementaremos las herramientas para revocar un certificado y para consultar su estado.
Un nodo de ethereum es una aplicación que permite interactuar con Ethereum. Una vez instalada permite verificar las transacciones de cada bloque y enviar a la red nuevas transacciones. Hay distintas implementaciones de clientes de Ethereum. Aquí vamos a usar el cliente geth. Un nodo puede lanzarse con distintos modos. El modo completo (full) almacena todos los datos de la blockchain, participa en la validación de bloques, verifica bloques y estados… El modo ligero sólo valida los datos de las cabeceras de los bloques y se usa en dispositivos embebidos o de pocos recursos.
Nosotros vamos a utilizar el modo “fast” que sólo utiliza el estado más reciente de la blockchain.
Descargamos el cliente de la web de Ethereum. Dejamos para la reflexión cómo estaremos seguros de que hemos accedido a la web auténtica y que hemos descargado un paquete legítimo.
https://geth.ethereum.org/downloads/
Por ejemplo usando wget para una distribución linux 64bits
wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.25-e7872729.tar.gz
Descomprimimos el paquete y accedemos al directorio
tar xvf geth-linux-amd64-1.9.25-e7872729.tar.gz
cd geth-linux-amd64-1.9.25-e7872729
y lanzamos el nodo utilizando esta línea de comandos
$ geth --goerli --syncmode "light" --http --
http.corsdomain="https://remix.ethereum.org" --allow-insecure-unlock
Algunos de los parámetros importantes de geth
–http | Activa el servicio JSON-RPC, que permite a otras aplicaciones usar el nodo vía API |
–http.corsdomain | Sólo se permitirá el acceso a la API desde “https://remix.ethereum.org“, que será el entorno con el que desplegamos el contrato inteligente |
–goerli | Utilizaremos la red de pruebas Görli. Ethereum dispone de varias redes de pruebas y una de producción. Görli es una red proof-of-authority que funciona con clientes Parity, Geth y otros |
–syncmode | Utilizaremos el modo de sincronización “light”, por defecto es “fast” |
–datadir | El directorio donde se almacenarán los datos de la blockchain. Ocupará unos 12GB si utilizamos un nodo “fast” y 500MB si usamos un nodo “light”. Si no no especificamos usará el directorio por defecto que es ~/.ethereum/goerli |
–cache | Aumentar a 2048 la memoria mejora el rendimiento en caso de sincronización “fast” |
–allow-insecure-unlock | Permite desbloquear un monedero al mismo tiempo que se usa la API, lo que no es recomendable fuera de desarrollo. |
console | Lanza una consola JavaScript para interactuar con el nodo |
El modo “light” es experimental pero suficientemente estable como para nuestro cometido.
INFO [02-12|09:27:12.511] Starting Geth on Görli testnet...
[...]
WARN [02-12|09:27:12.689] Light client mode is an experimental feature
Al lanzar el nodo comenzará a sincronizarse con la red automáticamente. Recuerda que esa sincronización llevará varias horas en el caso de que el modo sea “fast”.
Para interactuar con el nodo desde otra terminal utilizamos el comando attach:
$ geth --goerli attach
Welcome to the Geth JavaScript console!
[..]
To exit, press ctrl-d
Al introducir en consola el comando eth.syncing obtendremos el estado de sincronización del nodo o false si está completamente sincronizado. eth es un objeto que permite interactuar con los contratos inteligentes y la propia blockchain.
> eth.syncing
{
currentBlock: 213539,
highestBlock: 4264547,
knownStates: 511823,
pulledStates: 502449,
startingBlock: 0
}
Con el comando eth.blockNumber obtendremos el último bloque procesado por nuestro nodo.
> eth.blockNumber
4269342
Para enviar transacciones a la blockchain necesitamos unas credenciales además del nodo. Las credenciales son una clave pública y otra privada. A partir de la clave pública obtendremos un número de cuenta o dirección(address). Utilizando mensajes firmados digitalmente con nuestra clave privada enviamos operaciones en forma de transacciones a la blockchain.
El sitio donde guardamos esas claves se llama cartera, billetera o monedero (wallet) y tiene asociado un balance, unos ether, que nos permitirán enviar las transacciones a la red. Crear una nueva cuenta no requiere de ningún tipo de autorización. Ethereum es una red no permisionada.
No tenemos ninguna cuenta en nuestro nodo local. Para añadir una usamos el objeto personal que se encarga de interactuar con las cuentas del nodo:
> personal.newAccount()
Establecemos una contraseña, que podemos dejar en blanco para las pruebas. Para obtener las cuentas que tenemos escribimos:
> personal.listAccounts
["0x673ce1bda1ca6a7d969b8f44e44d78ce9ef11691"]
aunque también podemos usar eth.accounts, más habitual. El balance de la cuenta reside en la blockchain, no en la cuenta y por tanto para obtenerlo utilizamos método getBalance del objeto eth, que nos dará los Wei en la cuenta.
> eth.getBalance(eth.accounts[0])
0
Para añadir fondos accedemos a alguno de los servicios gratuitos de suministro para la red Görli. Introduciremos nuestra dirección y amablemente nos transferirán una pequeña cantidad de ethers. En producción utilizaremos algún exchange para adquirir ethers a cambio de euros o dólares.
Por ejemplo, podemos acceder a https://goerli-faucet.slock.it/
En unos instantes tendremos los fondos disponibles en nuestra cuenta.
Ethereum ejecutar pequeñas aplicaciones mediante una máquina virtual (EVM). Las aplicaciones pueden entenderse como una máquina de estados distribuida. La EVM permite modificar esa máquina de estados global mediante instrucciones de bajo nivel u opcodes. Existen varios lenguajes de programación que compilan a instrucciones de EVM. El más usado es Solidity. Es un lenguaje orientado a objetos con tipos específicos, modificadores de funciones para crear precondiciones, gestión de eventos…
Nuestro contrato inteligente es muy sencillo. Contiene la lista de certificados que hayan sido revocados. Si aparecen en la lista han sido revocados, sin más información inicial. Cada AC gestionará el contrato inteligente que gestiona la lista
Los certificados se localizan mediante su identificador de sujeto (Subject Key Identifier, SKID). El SKID es una extensión de los certificados que se calcula a partir de su clave pública.
Extensions:
2.5.29.14 (Subject key identifier)
Identifier: ee:09:e5:4e:30:60:2b:fa:c7:52:78:86:b9:b9:9e:fb:a6:7f:20:0b
El código del contrato inteligente tiene un mapa que asocia un entero de 256bits con un valor booleano. Tiene dos funciones, una para la consulta del estado de una certificado y otra para revocar un certificado, ambos identificados mediante el SKID. El método revoke sólo podrá ser llamado por el dueño del contrato.
pragma solidity >=0.7.0 <0.8.0;
contract DCRL {
address owner;
constructor() {
owner = msg.sender;
}
mapping(uint256 => bool) revoked;
function revoke(uint256 id) public {
require(owner == msg.sender);
revoked[id]=true;
}
function has(uint256 id) public view returns (bool){
return revoked[id];
}
}
Para desarrollar contratos inteligentes podemos usar entornos como truffle, que nos permite tener un ciclo de desarrollo sano. En este artículo usaremos directamente un editor online llamado Remix, que nos permitirá compilar, ejecutar, analizar y desplegar el contrato a través de nuestro nodo local.
Accederemos a https://remix.ethereum.org/?#gist=505a93da7b10ed3438a0dcc68832b337 donde veremos el entorno Remix como se muestra a continuación, con el contrato DCRL.sol precargado desde gist.
A la izquierda podemos acceder al árbol del proyecto, el compilador, el acceso al nodo para enviar transacciones y contratos y el gestor de complementos. En el centro tenemos el panel principal y debajo una terminal.
Podemos editar el contrato, cada vez que lo guardemos se autocompilará mostrando los posibles errores de sintaxis.
Para desplegar el contrato accederemos a la pestaña “Deploy & Run Transactions”. Estableceremos como entorno nuestro nodo local. Para ello seleccionamos “Web3 Provider” en Environment. Dejaremos el “Web3 Provider Endpoint” en su valor por defecto “http://127.0.0.1:8545”. Tras ello aparecerán las cuentas de nuestro nodo junto con su balance en la sección “Account”. Seleccionaremos la que tenga fondos disponibles si tenemos más de una.
Desde la consola de nuestro nodo local desbloquearemos la cuenta para poder firmar la transacción usando el método unlockAccount de personal:
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x673ce1bda1ca6a7d969b8f44e44d78ce9ef11691
Passphrase:
true
A continuación pulsaremos el botón “Deploy” desde el entorno.
Desplegar un contrato es enviar una transacción a la red Ethereum a través de nuestro nodo local. El deploy no es instantáneo, la red tiene que añadir la transacción a un bloque y llegar a un consenso sobre su validez. Una vez llegado al acuerdo nuestro contrato está disponible globalmente en una dirección.
Podemos ver el estado de las transacciones usando un explorador de transacciones. Podemos obtener directamente una url de acceso en la consola del entorno:
https://goerli.etherscan.io/tx/…
Una vez desplegado podemos interactuar con el contrato directamente desde el propio entorno de desarrollo, donde aparecerán los distintos métodos disponibles en el contrato.
Si introducimos un id en el método “has” y pulsamos el botón del método lanzaremos la llamada obteniendo el resultado esperado en el campo “decoded output” de la consola del entorno:
{
"0": "bool: false"
}
Si ahora utilizamos el método revoke, tras desbloquear la cuenta, con un parámetro y consultamos de nuevo su estado con el método has obtendremos true indicando que el certificado con ese SKID ha sido revocado.
Como ejercicio demostrar que otra cuenta no puede actualizar la lista, crear usuario, etc.
Programar correctamente un contrato inteligente es un trabajo especialmente delicado. No solamente por los bugs que puedan tener sino por su propio diseño. El software ha salvado muchas veces la situación. Si la perseverance tiene un problema de software, se le enviará una actualización. Si tiene un problema de hardware, probablemente se arreglará también con una actualización de software. Estamos tan acostumbrados a las actualizaciones de software que olvidamos las consecuencias de un software que no se puede actualizar. Un contrato desplegado en la blockchain es inmutable. Sin embargo, necesitamos cierto grado de mutabilidad para corregir posibles bugs o realizar mejoras.
Un contrato inteligente es “actualizable” creando un nuevo contrato y transfiriendo el estado del viejo contrato al nuevo. Pero migrar el estado puede ser caro, la dirección del contrato cambiará y el periodo de tiempo en el que el viejo y nuevo contrato coexistan es un gran problema.
La alternativa es usar un contrato (proxy o representante) para almacenar el estado y redirigir las llamadas a otro contrato llamado implementación que almacena la lógica de negocio. De esta manera podemos actualizar la lógica de negocio sin tener que realizar migraciones más allá de indicarle al proxy qué lógica usar.
Proyectos como OpenZeppelin proporcionan un framework para simplificar la programación de contratos inteligentes actualizables.
Pero hay que advertir que reducir el esfuerzo de programar adecuadamente porque el software se puede actualizar es un error de consecuencias muy graves. Y en el ámbito de los contratos inteligentes son habituales los robos o bloqueos irreversibles de fondos.
Un contrato inteligente debe utilizar todas las herramientas a su alcance para garantizar su seguridad. Incluyendo auditorías, análisis estáticos y dinámicos de código, tests…
Desde el propio entorno Remix podemos usar plugins que proporcionan análisis básicos de seguridad y estilo. Por ejemplo, usando Solint y Solidity Static Analysis obtenemos los siguientes informes:
Los informes obtenidos son sólo eso, informes con sugerencias o aspectos a revisar. No sería la primera vez que hacer caso a ciegas a estos análisis provoca un problema muy serio. Pero tampoco se deben ignorar las advertencias a la ligera. Discutir cada advertencia con otras personas competentes y con calma suele ser lo más recomendable.
Incorporaremos la dirección del contrato inteligente que acabamos de desplegar a los certificados mediante la definición de una nueva extensión.
El estándar ASN.1 se utiliza para la descripción de estructuras de datos de forma independiente a la máquina. La estructura de los certificados se suele expresar utilizando esta sintaxis. Nuestra extensión será la siguiente:
DCRL EXTENSION ::= {
SYNTAX DCRLURIList
IDENTIFIED BY id-dcrl }
DCRLURIList ::= SEQUENCE OF DCRLURI
DCRLURI ::= IA5String
Los OIDS son una secuencia de números separados por punto, jerárquicos que permiten identificar objetos. En esta extensión utilizaremos:
id-enterprise OBJECT IDENTIFIER ::= {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1)}
id-espublico OBJECT IDENTIFIER::= {id-enterprise espublico(47281)}
id-esfirma-rd OBJECT IDENTIFIER::= {espublico esfirma-rd(555)}
id-certs OBJECT IDENTIFIER ::= {id-esfirma-rd certs(1)}
id-dcrl OBJECT IDENTIFIER ::= {id-certs dcrl(1)}
Que corresponden a la siguiente tabla
1.3.6.1.4.1.47281 | Espublico |
1.3.6.1.4.1.47281.555 | ESFIRMA I+D+i Identifier |
1.3.6.1.4.1.47281.555.1 | x509Cert |
1.3.6.1.4.1.47281.555.1.1 | Decentralized Certificate Revocation Log (DCRL) |
La extensión contendrá una lista, una secuencia, de identificadores de recursos uniforme (URI). Cada URI definirá el acceso a un servicio de consulta de una blockchain.
Definiremos la URI de acceso al contrato inteligente DCRL de la red como
dcrl:ethereum:
En nuestro caso la URI de consulta para el contrato inteligente que acabamos de desplegar será:
dcrl:ethereum:0xd807991602EC9269e233e54188a5b9A6Ed904645
Para poder visualizar la extensión DCRL de un certificado fácilmente añadiremos la lógica al proyecto XRAY-509. Añadimos el procesador de la extensión DCRLProc,
public class DCRLProc extends BaseExtensionProc {
@Override
public Item processContent(Context ctx, Extension ext) throws IOException {
Item out = new Item();
if (ext.extnValue != null) {
DCRLSequence i = new DCRLSequence();
i.decode(ext.extnValue.from, ext.extnValue.value, true);
int index = 0;
for (BerIA5String name : i.seqOf) {
out.prop(ItemHelper.index(index++), new Item(new TaggedString("uri").addIndexTag(index), new TaggedString(String.valueOf(name)).addTag("type", "IA5String")));
}
}
return out;
}
}
Añadiremos el procesado a la lista de extensiones conocidas en el método registerExtensions de la clase Context.
registerExtension("1.3.6.1.4.1.47281.555.1.1",new DCRLProc());
Como ejercicio realizar el testing adecuado a esta nueva funcionalidad
Emitir un certificado digital consiste en generar un par de claves, crear una solicitud de firma de certificado (CSR) y enviar el CSR a la AC para que genere el certificado.
Vamos a lanzar nuestra propia AC de pruebas basada en OpenSSL. Para ello crearemos un certificado raíz.
$ openssl genrsa -out ca.key 2048
[..]
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:Aragon
Locality Name (eg, city) []:Zaragoza
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Espublico
Organizational Unit Name (eg, section) []:ESFIRMA I+D+i
Common Name (e.g. server FQDN or YOUR name) []:TEST CA
Email Address []:
Utilizando la herramienta de visualización podemos ver la estructura detallada del certificado generado:
$ xray-cert ca.crt
Version: 2 (v3)
SerialNumber: 9563599858331462912 (hex: 0x84b8bb66a9d2ad00, bits: 72)
Signature:
algo: 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
params: NULL
Issuer:
2.5.4.6 (countryName,C): ES (type: printableString)
2.5.4.8 (stateOrProvinceName,ST): Aragon (type: utf8String)
2.5.4.7 (localityName,L): Zaragoza (type: utf8String)
2.5.4.10 (organizationName): Espublico (type: utf8String)
2.5.4.11 (organizationalUnitName): ESFIRMA I+D+i (type: utf8String)
2.5.4.3 (commonName,CN): TEST CA (type: utf8String)
Subject:
2.5.4.6 (countryName,C): ES (type: printableString)
2.5.4.8 (stateOrProvinceName,ST): Aragon (type: utf8String)
2.5.4.7 (localityName,L): Zaragoza (type: utf8String)
2.5.4.10 (organizationName): Espublico (type: utf8String)
2.5.4.11 (organizationalUnitName): ESFIRMA I+D+i (type: utf8String)
2.5.4.3 (commonName,CN): TEST CA (type: utf8String)
Validity:
Duration: 1 month 2 days
NotBefore: 2021-02-12T13:20:32Z
NotAfter: 2021-03-14T13:20:32Z
[...]
Preparamos la configuración de la CA para emitir certificados con soporte DCRL.
Creamos el fichero ca.conf:
[ ca ]
default_ca = test_ca
[ test_ca ]
serial = ./serial
database = ./index.txt
new_certs_dir = ./newcerts
certificate = ./ca.crt
private_key = ./ca.key
default_md = sha256
default_days = 365
policy = test_policy
[ test_policy ]
countryName = supplied
stateOrProvinceName = supplied
organizationName = supplied
organizationalUnitName = optional
y el fichero cert.extensions.conf en el que definiremos la extensión DCRL con la URI de nuestro contrato inteligente.
basicConstraints=CA:FALSE
subjectAltName=@my_subject_alt_names
subjectKeyIdentifier = hash
1.3.6.1.4.1.47281.555.1.1=ASN1:SEQUENCE:DCRLExt
[DCRLExt]
uri=IA5STRING:dcrl:ethereum:0xd807991602EC9269e233e54188a5b9A6Ed904645
[ my_subject_alt_names ]
DNS.1 = test.esfirma.com
Generamos las claves y el CSR para el certificado final. Para ello vamos a añadir un fichero cert.conf con los datos que nos gustaría que aparecieran en el certificado:
[ req ]
default_bits = 2048
default_keyfile = cert.key
encrypt_key = no
default_md = sha256
prompt = no
utf8 = yes
distinguished_name = the_distinguished_name
req_extensions = the_extensions
[ the_distinguished_name ]
C = ES
ST = Aragon
L = Zaragoza
O = Espublico
OU = Esfirma I+D+i
[ the_extensions ]
basicConstraints=CA:FALSE
subjectAltName=@the_subject_alt_names
subjectKeyIdentifier = hash
[ the_subject_alt_names ]
DNS.1 = test.esfirma.com
Creamos el CSR para enviarselo a la AC
$ openssl req -new -out cert.csr -config cert.conf
La AC verificará la información contenida en el CSR, validará la identidad del solicitante y emitirá un certificado.
$ openssl ca -config ca.conf -out cert.crt -extfile cert.extensions.conf -in cert.csr
El nuevo certificado contendrá una extensión DCRL.
$ xray-cert cert.crt
[...]
Issuer:
2.5.4.6 (countryName,C): ES (type: printableString)
2.5.4.8 (stateOrProvinceName,ST): Aragon (type: utf8String)
2.5.4.7 (localityName,L): Zaragoza (type: utf8String)
2.5.4.10 (organizationName): Espublico (type: utf8String)
2.5.4.11 (organizationalUnitName): ESFIRMA I+D+i (type: utf8String)
2.5.4.3 (commonName,CN): TEST CA (type: utf8String)
Subject:
2.5.4.6 (countryName,C): ES (type: printableString)
2.5.4.8 (stateOrProvinceName,ST): Aragon (type: utf8String)
2.5.4.10 (organizationName): Espublico (type: utf8String)
2.5.4.11 (organizationalUnitName): Esfirma I+D+i (type: utf8String)
[...]
Extensions:
2.5.29.19 (Basic constraints)
None
2.5.29.17 (Subject alternative name)
DNSName: test.esfirma.com (type: IA5String)
2.5.29.14 (Subject key identifier)
Identifier: e2:a4:1b:59:70:00:ac:93:93:8b:4e:1d:88:cc:77:3e:
6c:69:01:f4
1.3.6.1.4.1.47281.555.1.1 (DCRL - Decentralized Certificate Revocation Log)
[0] uri: dcrl:ethereum:0xd807991602EC9269e233e54188a5b9A6Ed904645 (type: IA5String)
[...]
Las conclusiones
La confianza es la clave. Para el sistema mostrado en este artículo confiamos en la lista de certificados raíz que viene preconfigurada con el navegador, en los servidores DNS que tenemos configurados para resolver la dirección IP de un nombre de dominio. Así como los nodos iniciales que nos permiten arrancar la red de Ethereum.
En general es mejor evitar sistemas centralizados que son más vulnerables que sus equivalentes descentralizados. Las nuevas tecnologías descentralizadas reemplazarán inevitablemente poco a poco las viejas estructuras.
¡La redescentralización ha comenzado!
|
David se enfrascó tanto en los ordenadores, que se le pasaban las noches programando de claro en claro, y los días de turbio en turbio, y así, del poco dormir y del mucho programar, se le secó el cerebro, de manera que vino a perder el juicio. Desde entonces está en el departamento de I+D+i de esPublico Tecnología...