Mini tutorial para encriptar datos con certificados digitales en PHP.


Estoy trabajando en un proyecto en el cual es muy sensible la seguridad de los datos que se manejan, por lo que necesito encriptar y desencriptar datos con tecnologías de criptografía asimétrica, o más concretamente con certificados digitales. El tema en realidad es bastante simple, paso a explicarles los pasos necesarios.

En primer lugar necesitamos tener los certificados digitales que luego se usarán para encriptar y desencriptar los datos. Encriptaremos con la llave pública y desencriptaremos con la llave privada, para que de esta manera nuestros datos (que pueden ser guardados en una base de datos) no tengan ningún tipo de valor sin que se cuente con la llave privada y su correspondiente password.

Generación de los certificados o llaves pública y privada.

Vamos a generar un certificado firmado por nosotros mismos. Para ello necesitamos tener instalado el OpenSSL y entonces tipeamos:

pablot$ openssl req -new -x509 -out certificado.pem

Una vez hecho esto seguiremos los pasos que se ven a continuación como salida del comando anterior y completaremos la información solicitada según corresponda.
Generating a 1024 bit RSA private key
.............................++++++
..........++++++
writing new private key to 'privkey.pem'
Enter PEM pass phrase: <acá podemos ingresar una clave>
Verifying - Enter PEM pass phrase: <y acá demos repetir la clave anterior>
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:AR
State or Province Name (full name) [Some-State]:Santa Fe
Locality Name (eg, city) []:Rosario
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Mi empresa
Organizational Unit Name (eg, section) []:IT
Common Name (eg, YOUR name) []:José Perez
Email Address []:

Una vez hecho esto vamos a tener nuestro certificado en el archivo certificado.pem y nuestra llave privada en el archivo privkey.pem. Aca vemos el contenido de los dos archivos:

Este es el contenido de certificado.pem
-----BEGIN CERTIFICATE-----
MIIDADCCAmmgAwIBAgIBADANBgkqhkiG9w0BAQQFADBkMQswCQYDVQQGEwJBUjER
MA8GA1UECBMIU2FudGEgRmUxEDAOBg1VBAcTB1Jvc2FyaW8xEzARBgNVBAoTCk1p
IGVtcHJlc2ExCzAJBgNVBAsTAklUMQ4wDAYDVQQDEwVQYWJsbzAeFw0wODA2Mjcw
ODI4MzBaFw0wODA3MjcwODI4MzBaMGQxCzAJBgNVBAYTAkFSMREwDwYDVQQIEwhT
YW50YSBGZTEQMA4GA1UEBxMHUm9zYXJpbzETMBEGA1UEChMKTWkgZW1wcmVzYTEL
MAkGA1UECxMCSVQxDjAMBgNVBAMTBVBhYmxvMIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQC9Hh9+H5+sBUCJklI+U5H7jVILZT21D9LtHqxeVtaBZUheLHJ1tJH6
rCKH/rToMYgNHPthHORQFif5+RMZJxev7o2F16osfwwkF4ak1+2xP7mTOPpT7n1o
t7DUZPNzhV4W9U2wGG7s8k8vX1XZ7KjQSsm0M1Wi7TJLUk+r2z/S7QIDAQABo4HB
MIG+MB0GA1UdDgQWBBTi3lAeHK6M0E0tXmxThxQcYGL5zDCBjgYDVR0jBIGGMIGD
gBTi3lAeHK6M0E0tXmxThxQcYGL5zKFopGYwZDELHAkGA1UEBhMCQVIxETAPBgNV
BAgTCFNhbnRhIEZlM2RAwDgYDVQQHEwSb3NhcmlvMRMwEQYDVQQKEwpNaSBlbXBy
ZXNhMQswCQYDVQQLEwJJVDEOMAwGA1UEAxMFUGFibG+CAQAwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQQFAAOBgQB+F7QLGre/v8tu0UZzBCauuygGjPk2KYddJC5/
gcaV5xpgHoyxIXkYkwzfuV+v+S33Ju+mTmXczt5UgPztYOxFdocGFUF0QBs6VGfk
uVSsANaT3TVS8lF/dqiy0M8e0/rsT4PdCvidalvZNMOEcHAl+7TALLzg53FU2bF2
O+Wujw==
-----END CERTIFICATE-----

Este es el contenido de privkey.pem
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,7269B348F1BAA2D9

RC+B4fUTgQx5qCGmC1VXvlErXmnpbqE+DLGq8dqZYlwTksKnDnMv32uRc3rAeyNc
Eg+aNU6KWhEnu3WYfcHTJsW4R3aILNX2vKF/zOXHHSxBA1zJRgyMzqKrRPfB4BEj
5Enn48ehgG/DwKBcXcQSsNAQ121qJf4QBG3rj6H6jE8wNcIiV7AI5ZKcOB25V/9q
Q7IgplPKX+PfF/piQUDIjNDQ1Nfrfn59qdewkxRQiCoEeJKwXpn6je2JCLDTV5cz
4ZUc5hT/1IePjWHY8TkRnma4v4obBCd8N3fEqkTP5nLBhGAcor9RuBcusmKyhlxt
ftiWTgBs6k15HuCcmzIuARQ/PE5elJMeGYJfrcby40QPLcTdlz9wqR2ULmpAUxTe
g9e1EKTOTAGjF+oUpCrDEIN5Txru6Q8hDQ6NuV9b0baeFJare7dlcqrzcjm2Lwin
Sq3N78xdOiA4kMJ1AKDM6cBXbFvZ92XeUFEZPH53wDC+aiNc4urlqew06uwAgHDu
Br60ODWnhw3babNWQaM5nxAIs5nR8DJZmdzrj2c4zYkYKLmcoaJzsSTGn466kqce
96F4503ev2+/iCgSh9h8lFU9JRRsQnbs4IaxemboHU5MY5Fu3h7MZXSejgUbZ2Z7
3rI9T/VQUyJyxcuzHIKeEJNMwxwUBE+3xqffluZXAxPP8GuyWHSn9owErCPg9RhU
xeBl+MYZi2zzSscdVZ6ZxDbsNRYiGlAdqPBWofv+UTej7ch0vggrhjzkONuGTJn/
IiJB4QUAjkiuDCtZR8OVutxrebmPNnRZmiFHx8L7QYw=
-----END RSA PRIVATE KEY-----

NOTA: Cabe destacar que si queremos tener un certificado digital otorgado por una entidad certificante como Verisign, debemos generar un CSR o Certificate Sign Request (con el comando openssl req -new -out cert.csr) que debe ser enviado (el archivo cert.csr) a la autoridad certificante para que, previo pago, nos devuelva un certificado como el que tenemos en el archivo certificado.pem.

Ahora que ya tenemos el certificado y nuestra llave privada, vamos a ver un ejemplo para encriptar datos con la llave pública que luego podrían ser grabados en una base de datos.


<?php

$texto_plano = "Dato supersecreto que pone en riesgo la seguridad nacional";


$llave_publica = "file://<path_donde_esta_el_archivo>certificado.pem";
openssl_public_encrypt($texto_plano, $texto_encriptado, $llave_publica);

// Si imprimimos esto, vemos "basura" que en realidad es el dato ya encriptado.
// Es por esto que probablemente nos convenga por ejemplo codificarlo en base64 con
// $texto_encriptado = base64_encode($texto_encriptado) antes de grabarlo
// en un campo de una base de datos
//echo $texto_encriptado;
?>

Y ahora cuando tengamos que desencriptar los datos tendremos que usar la llave privada que es la única que podrá desencriptarlos.

<?php
$fp=fopen("./privkey.pem","r");
$llave_privada = fread($fp,8192);
fclose($fp);

// El segundo parametro es la clave que ingresamos al crear el certificado
// si es que optamos por hacerlo.
// Si antes codificamos en base64 el texto encriptado, ahora hay que acordarse
// de hacerle un $texto_encriptado = base64_decode($texto_encriptado) antes de
// desencriptarlo.
$res = openssl_get_privatekey($llave_privada,"miclave");
openssl_private_decrypt($texto_encriptado, $texto_desencriptado, $res);

//Esto debe dar como salida el mismo texto que antes estaba en la variable $texto_plano
echo $texto_desencriptado;
?>

¡Y listo!, ya esta, no hay nada más. Ya sabemos lo que tenemos que hacer antes de grabar un dato crítico en la base de datos y lo que tenemos que hacer luego de recuperarlo de la base para poder desencriptarlo.

De esta manera la base de datos será solo basura para cualquiera que no tenga la llave privada y su correspondiente clave.

Aclaración importante: El largo de la cadena de datos a encriptar esta limitado por el tamaño de la clave, por lo cual para una clave de 1024 bits como la del ejemplo OpenSSL permite encriptar hasta 936 bits, o sea 936/8=117 caracteres. En consecuencia para claves de 1024 bits solo podremos encriptar hasta 117 caracteres, pero el límite se incrementa si usamos claves de 2048 o 4096 bits.

Por otro lado se recomienda una clave de al menos 2048 bits, pero teniéndo OpenSSL la posibilidad de generar claves de 4096 bits, yo diría que sea de 4096 bits😉

Nota: No intenten capturar el contenido de los archivos certificado.pem y privkey.pem que muestro y usarlos porque son inválidos. Deben generarse los propios. Esto esta hecho a propósito para ¡proteger a los inocentes! para que nadie los copie y los use directamente, ya que el hecho de estar publicados los torna totalmente inútiles desde el punto de vista de seguridad.

  1. Ruben Ibarra
    9 julio, 2008 a las 6:25 pm

    Muy bueno este tutorial me fue de gran ayuda, adicionalmente quisiera saber si tienes informacion de como hacer para validar la firma de los archivos encriptados, es decir saber si fueron firmados por sus respectivos certificados.

  2. pablotrin
    9 julio, 2008 a las 8:21 pm

    Muchas gracias Rubén, la verdad que aún no he investigado ese tema, pero probablemente lo haga y en cuanto tenga algo se los cuento por acá.

  3. Ruben Ibarra
    14 julio, 2008 a las 6:21 pm

    ok gracias, yo estoy investigando y al tener alguna solucion la hare saber por aqui,
    saludos

  4. pandoro
    28 agosto, 2008 a las 5:10 am

    Hola amigos, estoy realizando una aplicacion de gestion que precisa de validaciones de los usuarios mediante certificados digitales, ya sean obtenidos por alguna entidad certificadora o mediante el DNI electronico. La cosa es que tengo la pagina hecha en php y no se este codigo que habeis puesto me serviria para validar ese usuario que desea entrar en la pagina para trabajar.

    Alguien me podria echar una mano?

    Gracias.

    Un saludo.

  5. victor
    12 mayo, 2009 a las 3:45 am

    saludos estoy buscando informacion para preparara mi tessi sobre firmas y certifiocados digitales les agradeceria si alguien pudiera indicarme que temas debria tocar y si tienes informacion que me puedan proporcionar gracias

  6. 28 agosto, 2009 a las 3:25 pm

    hola amigo acabo de probar tu ejemplo Warning: openssl_public_encrypt() [function.openssl-public-encrypt]: key parameter is not a valid public key in C:\xampp\htdocs\probando\index.php on line 8
    copie las mismos certicados que tu generastes

    • pablot
      30 agosto, 2009 a las 11:53 am

      Justamente hiciste lo que no debías😉

      Fijate que sobre el final del post hago la aclaración siobre que no hay que copiar los certificados que yo puse porque son inválidos. Esto esta hecho adrede para evitar que sean usados y den una falsa sensación de seguridad. La clave privada NUNCA debe ser puesta al alcance de nadie, sino se pierde el sentido de utilizar certificados.

      Generá los tuyos propios y te va a funcionar.

      Saludos.

  7. mike
    22 febrero, 2010 a las 5:16 pm

    Que version de openSSL usaste???

    podria instalar openSSL en windows y usar los metodos con asp (puro)

  8. Mike
    11 junio, 2014 a las 3:11 pm

    Hola muy buen tutorial, una pregunta es posible que apartir el siguiente codigo que realizas: $res = openssl_get_privatekey($llave_privada,”miclave”);

    pudieras obtener la llave privada en el siguiente formato :
    —–BEGIN RSA PRIVATE KEY—–
    MIICXAIBAAKBgQDJaimSvq+8X7UqG54ZwzgV3eTC+t42fhU8T0wxAHYDmzvaSb9S
    7EaQcdIEhTFwz78n0PePQLeEEP5RO61cTzPnD6wkf3DND7RKERriv5y5HmPqatVS
    y3Nnnhzn68Wsbd8lZmAUEq05MOXkNAxHXXIkcPDqLQW7kMtyAV0JvbHZewIDAQAB
    AoGAa5vCllo2fcgMfHDA6Ta5kkU44UsyyZ5XLh+Xr151rruTIK7h/yWpFacZ87xN
    i94SXO1yNiD3ayHBHhlqJ7VRHqE/cfoly8WQ/JRY9juzHet36v+BlUpbU7qKuJei
    f3IS4nV+S/HLTwVeDD9ee7XtV6kX4YGoRB641tK1jsSyrgECQQDm1Vz29usBmtZA
    Yvln40JbHt8t5LA73b9k39N2INJV0K1t+RtnSmZjS00t0Y+d/OuBCcqNVWZvpFqO
    MWtHD1v7AkEA31+2VvrXZleN1YDeCNMz44dTRhbtFCZRr/FL8z7DR0GZs8H1Qv/3
    x3CcZSec0w8sZvqgOCldzuSTf2vjwjWAgQJAUyxB6QKjPtrJpD2FXDd4q1h+Vgz2
    qzuFQRWdHg1JfH5KXzvI8fjO16xETOQbeXOub7pVb4N7+3Ie5ulLnDkejQJAFXLm
    WmHJzyz3FJpEN8q3/9AdwewvpbCsRV3BMkFDlRMKhEVKnL1heIgqrFfqaiXZ2KWW
    Pu5R8KzoMOMWQq7KgQJBAKU5fCyeu8emuxLWUEx+ePeDG9sxmcmXW/skO1fxRKfm
    G0jTaNds2EXhb8/EhKzVT327SPmVdkFx/L4POGn2X2s=
    —–END RSA PRIVATE KEY—–

    De antemano agradezco de ayuda y nuevamente felicitarte por tu trabajo.

  1. 27 junio, 2008 a las 12:39 pm
  2. 2 agosto, 2008 a las 9:30 am
  3. 3 enero, 2011 a las 1:43 pm

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: