Encriptar datos con certificados digitales en PHP.

En un articulo anterior hablamos de métodos para encriptar los datos pero a veces necesitamos mayor seguridad con certificados digitales de más de 2048 bits…

Fuente: https://www.flickr.com/photos/riebart/
Fuente: https://www.flickr.com/photos/riebart/

Antes de nada debemos habilitar las librerías OPENSSL para poder generar los certificados de hasta 4096 bits. Podremos crear unos personalizados validados por nosotros mismos o incluso solicitar unos de pago certificados por entidades externas.

En AGENCIA LA NAVE hemos desarrollado soluciones centradas en la protección de los datos que permiten al cliente generar su propia clave privada y una clave que solo el conoce para conseguir los datos codificados y protegidos.

Para encriptar y desencriptar datos con tecnologías de criptografía asimétrica, o más concretamente con certificados digitales, en PHP podemos emplear la librería OPENSSL, lo cual lo hace bastante simple:

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.

Empezaremos generando los certificados: Vamos a generar un certificado firmado por nosotros mismos. Para ello necesitamos tener instalado el OpenSSL:

$ 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: <elegimos una clave>
Verifying - Enter PEM pass phrase: <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) [ES]:ES
State or Province Name (full name) [Some-State]:Vizcaya
Locality Name (eg, city) []:Bilbao
Organization Name (eg, company) [Internet Widgits Pty Ltd]:AGENCIA LA NAVE
Organizational Unit Name (eg, section) []:NAVENETWORKS SL
Common Name (eg, YOUR name) []:RAUL MATE GALAN
Email Address []:[email protected]

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 = "Texto que deseamos proteger: El PIN del cajero es 12345862";


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

// Si bien el $texto_encriptado ya estará bien lo podemos pasar a base64
// $texto_encriptado = base64_encode($texto_encriptado) para grabarlo
// en una base de datos

// en un campo de una base de datos
//echo $texto_encriptado;
?>

Una vez todo guardado de forma segura no deberemos guardar la “clave” de la firma. Para desencriptar los datos tendremos que usar la llave privada que es la única que podrá desencriptarlos.

<?php

// abrimos la privada
$fp=fopen("./privkey.pem","r");
$llave_privada = fread($fp,8192);
fclose($fp);

// 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;
?>

Ya somos capaces 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.

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. (recomendamos usar 4096 bits)

Esta claro que si queremos encriptar un archivo completo deberemos proceder de forma similar pero utilizando el máximo de caracteres que permita la clave (4096 = 468 caracteres).

Nota: No debéis utilizar los certificados certificado.pem y privkey.pem de los ejemplos, deben generarse los propios porque sino no habría ningún tipo de seguridad.

Enlaces: Encriptar datos de variables

One thought on “Encriptar datos con certificados digitales en PHP.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *