Hace algún tiempo, en el marco del proyecto era necesario limitar la cantidad simultánea de computadoras que tienen acceso a una aplicación web que se ejecuta dentro de la red de área local del cliente.
La decisión de usar tokens USB de hardware para identificar la computadora vino por sí sola. La elección se basaba en el Rutoken EDS: funciona sin controladores, para trabajar en una aplicación web solo se necesita un complemento para el navegador, emitido por el desarrollador. Dado que el token debe identificar la computadora, no el usuario, trabajar con ella debe ser completamente "transparente": si existe, entonces el sistema simplemente funciona en silencio sin hacerle preguntas innecesarias al usuario.
Se decidió: al iniciar sesión en el sistema, firme las credenciales de usuario con una firma no calificada de un certificado almacenado en Rutoken utilizando el complemento Rootoken y verifique el servidor. Después de un inicio de sesión exitoso utilizando el complemento, verifique la presencia física del mismo token y, en ausencia de este, cierre la sesión del sistema. En el marco del proyecto mencionado, esto fue suficiente.
Es necesario crear su propia Autoridad de Certificación (CA) para el intercambio de mensajes firmados, o más bien, para la transferencia de mensajes firmados del cliente al servidor. Los certificados de cliente deben ubicarse en tokens USB en contenedores de clave privada, y la verificación de firma debe realizarse en el servidor utilizando OpenSSL
Entonces, la tarea:
instalación y configuración en el servidor Linux de la CA. Implemente certificados de cliente que identifiquen computadoras en tokens USB .
Para resolverlo, necesitarás:
- Creamos un directorio donde se ubicará la CA y copiamos la configuración de OpenSSL incluida con el sistema (hay /etc/ssl/openssl.cnf en las últimas versiones de Ubuntu)
- Configuramos "nuestro" openssl.cnf:
a) Agregue al comienzo de las directivas de archivo para conectar el motor de token:
openssl_conf = openssl_def [ openssl_def ] engines = engine_section [ engine_section ] rtengine = gost_section [ gost_section ] dynamic_path = /path/to/rutoken/openssl/connector/librtengine.so MODULE_PATH = /path/to/rutoken/pkcs11/librtpkcs11ecp.so RAND_TOKEN = pkcs11:manufacturer=Aktiv%20Co.;model=Rutoken%20ECP default_algorithms = CIPHERS, DIGEST, PKEY, RAND
b) descomentar la línea
# req_extensions = v3_req # The extensions to add to a certificate request
c) en la sección [v3_req] , especifique los siguientes parámetros:
subjectSignTool = ASN1:FORMAT:UTF8,UTF8String: extendedKeyUsage=emailProtection keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
d) en la sección [v3_ca], elimine la opción crítica del parámetro basicConstraints:
basicConstraints = CA:true
Para que? Respuesta honesta: no lo sé. Sin embargo, todos los ejemplos de certificados raíz que descargué al intentar descubrir el tema no tenían un signo crítico. Estoy haciendo la pregunta "¿para qué?" colegas más experimentados.
e) opcionalmente, establezca los valores predeterminados que se ofrecerán al emitir certificados autofirmados y generar solicitudes para emitir certificados de clientes. Estos parámetros están en la sección [req_distinguished_name]
El parámetro con el postfix predeterminado es el valor predeterminado. Un ejemplo:
countryName = Country Name (2 letter code) countryName_default = AU countryName_min = 2 countryName_max = 2
Cuando el sistema le pide que ingrese el parámetro countryName , le indicará entre corchetes que dejará el valor AU por defecto.
Esto completa la configuración de la configuración de OpenSSL. Queda por indicar a OpenSSL que es necesario usarlo. Para hacer esto, configure la variable de entorno OPENSSL_CONF:
export OPENSSL_CONF=/path/to/your/openssl.cnf
- Creamos una estructura de directorio donde se almacenará la información sobre nuestra CA.
Para hacer esto, vaya al directorio creado con openssl.cnf recién editado y realice los siguientes pasos:
a) crear subdirectorios en él:
demoCA
demoCA / privado
demoCA / newcerts
Nota: el nombre demoCA se detalla en la sección [CA_default] del archivo openssl.cnf . Puede cambiarlo (en el paso 2) y luego trabajar con él en lugar de demoCA.
b) en el directorio demoCA, cree un archivo index.txt vacío y un archivo en serie , que abrimos con un editor de texto y escribimos la línea 01 allí. Este es el contador de certificados emitidos. Después de la emisión de cada próximo certificado, el valor en este archivo aumenta en uno. - Opcionalmente formateamos nuestro token utilizando la utilidad rtAdmin. Ahora todo está listo para la implementación de la CA.
El algoritmo de acción es ampliamente simple:
a) emitimos el certificado raíz del centro de certificación utilizando el algoritmo GOST:
- generar una clave privada para emitir un certificado CA autofirmado
- generar un certificado X509 autofirmado con la clave generada
b) en cada uno de los tokens USB
- generar un par de claves (el denominado contenedor de claves privadas)
- generar una solicitud de firma de certificado utilizando la clave de token generada
- emitir un certificado para esta solicitud
- guardar el certificado en el token en el contenedor de clave privada
La siguiente es una implementación de este algoritmo para un solo token:
Generación de clave privada para el certificado de CA (utilizamos el algoritmo GOST):
openssl genpkey -algorithm gost2012_256 -pkeyopt paramset:A -outform PEM -out demoCA/private/cakey.pem
Emitimos un certificado CA autofirmado:
<b>openssl req -new -x509 -key demoCA/private/cakey.pem -out demoCA/certs/cacert.pem -extensions v3_ca -days +3650 -outform PEM
Nota: indicamos en la línea de comando que es necesario usar las extensiones v3_ca de la configuración openssl_cnf. Es allí donde está nuestra CA. Validez de 10 años. Una cosa común para CA. Pero más es posible.
En el proceso de emisión de un certificado, el sistema le pedirá que ingrese los valores de los parámetros que se encuentran en la sección [req_distinguished_name] de nuestro archivo openssl.cnf
Ahora comenzamos las operaciones de token. Si el token es nuevo o está formateado con valores predeterminados, entonces el PIN del usuario es 12345678. Procedo de la suposición de que esto es exactamente así. De lo contrario, debe especificar el PIN de usuario correcto y, en general, tratar de asegurarse de que en los ejemplos a continuación los nombres de los objetos que ya existen en el token no se superpongan con los ingresados.
En primer lugar, generaremos un par de claves. OpenSSL no puede realizar esta operación en Rutoken, por lo que utilizaremos la utilidad pkcs11-tool del paquete OpenSC:
pkcs11-tool --module /path/to/your/librtpkcs11ecp.so --login --pin 12345678 --keypairgen --key-type GOSTR3410:A --id 303030303031 --label 'client01'
Nota importante: especificamos la identificación 303030303031. Cada dos dígitos de esta identificación no son más que el código ASCII de los caracteres "0" y "1", respectivamente. Para operaciones con OpenSSL, se verá como "id = 000001"
Generar una solicitud de certificado:
openssl req -utf8 -new -keyform engine -key 'pkcs11:id=000001' -engine rtengine -out demoCA/newcerts/client01.csr
Si todo se hizo correctamente, entonces el sistema
- solicitar un PIN
- solicitará los parámetros del nombre del certificado (de la sección [req_distinguished_name] )
- emitirá un archivo de solicitud de firma de certificado
Con esta solicitud, firmamos un certificado de cliente ( en el ejemplo, el certificado es válido por 1825 días. Es importante que este período no exceda el período de validez de su certificado raíz ):
openssl ca -utf8 -days +1825 -keyfile demoCA/private/cakey.pem -cert demoCA/certs/cacert.pem -in demoCA/newcerts/client01.csr -outdir demoCA/newcerts -out demoCA/certs/client01.pem
El sistema mostrará el certificado, preguntará sobre la decisión de firmarlo (responda “y”) y sobre la decisión de guardar el nuevo certificado (nuevamente responda “y”).
Guardamos el certificado recibido para el token:
pkcs11-tool --module /path/to/your/librtpkcs11ecp.so --login --pin 12345678 --id=303030303031 -w demoCA/certs/client01.pem -y cert
Eso es todo.
Probar el "milagro" creado. Para hacer esto, firmamos y verificamos la firma de la frase "¡Hola, mundo!":
echo Hello,world! | openssl cms -nodetach -sign -signer demoCA/certs/client01.pem -keyform engine -inkey "pkcs11:id=000001" -engine rtengine -binary -noattr -outform PEM | openssl cms -verify -CAfile demoCA/certs/cacert.pem -inform PEM
Si todo se hace correctamente, el sistema solicitará un PIN, firmará el mensaje, luego verificará la firma y, si tiene éxito, mostrará el mensaje original y el resultado de la verificación ("éxito")
Observación Volviendo a la tarea de título y firmando con el complemento, debe tenerse en cuenta que, de forma predeterminada, el complemento da el resultado de firmar no en formato PEM, sino en formato DER, codificado en base64. Por lo tanto, para verificar la firma, primero debe decodificar desde base64 y, al verificar, especifique el formato DER de entrada.
Buena suerte