I have a PHP endpoint that is used by several websites. While POSTing data to the endpoint the sites use encrypted headers to identify various information. The sites use the standard PHP mcrypt_ecb function, and the endpoint decrypts the variables like this:
$var = trim(mcrypt_ecb(MCRYPT_DES, $key, base64_decode($input), MCRYPT_DECRYPT)));
I don't really have much chance of altering the PHP endpoint, although I could conceivably make another endpoint if mcrypt_ecb doesn't play nicely.
I now have a PhoneGap app, which also needs to send the encrypted headers. Phonegap can only really use javascript to POST to a remote server. I've looked at the Google CryptoJS to try to acheive this, along side a base64 library. Note that the key is the same as the $key var above.
var encrypted = CryptoJS.DES.encrypt(user.email, key).toString();
var base64 = base64.encode(encrypted);
The problem is that the encrypted string (and I have tried without the base64 encode/decode) when passed through mcrypt_ecb is garbage. Apparently there are different methods of triple DES encryption?
How would I create a mcrypt_ecb compatible encrypted string in JavaScript?
As per: http://php.net/manual/en/function.mcrypt-decrypt.php
Warning This function (mcrypt-decrypt) has been DEPRECATED as of PHP 7.1.0. Relying on this function is highly discouraged.
Then I had to investigate a lot until I discover a way to encrypt using openssl via javascript and decrypt using php.
The only problem I got is when I send a long text. That's because RSA, by definition, supports strings of limited lenght.
https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security/33445#33445
RSA, as defined by PKCS#1, encrypts "messages" of limited size. With the commonly used "v1.5 padding" and a 2048-bit RSA key, the maximum size of data which can be encrypted with RSA is 245 bytes. No more.
i.e. If I use private_key_bits of 1024 I can send
"José compró en Perú una vieja zampoña. Excusándose, Sofía tiró su whisky al desagüe de la banqueta."
nothing longer. If I use private_key_bits of 512 I can send
"José compró en Perú una vieja zampoña. Excusánd"
nothing longer.
On long strings JavaScript console reports: "Message too long for RSA"
Then if you want to encrypt long strings you must compress and split them before javascript encryption and after decryption join and uncompress on php, I think zlib is a good solution for split/join because it is supported on javascript and php.
Maybe is better to use an AES library instead of this one like https://github.com/brainfoolong/cryptojs-aes-php
My working code is as follows:
<?php
//------------------------------------------------------------
// Global Settings.
//------------------------------------------------------------
ini_set('display_errors', 1);
error_reporting(E_ALL);
$directorio = "/path/to/key/directory/apache/writable/";
$nombre_base = "llaves_php";
//------------------------------------------------------------
// Initialization.
//------------------------------------------------------------
$encabezado_html = "";
$cuerpo_html = "";
//------------------------------------------------------------
// Loading keys
//------------------------------------------------------------
list($privateKey, $pubKey) =
cargar_llaves_RSA($directorio, $nombre_base);
//------------------------------------------------------------
// Form that uses javascript to encrypt data.
// (it uses only the public key)
//------------------------------------------------------------
$librerias_html = "
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/".
"jquery/3.2.1/jquery.min.js'></script>
<script type='text/javascript'
src='lib/jsencrypt.js'></script>
";
$pubKey_html = htmlentities($pubKey);
$datos_html = "
<h2>Cifrando con Javascript</h2>
<input type='text' id='mensaje' />
<br />
<button id='ENVIAR'>Enviar</button>
<br />
<textarea id='pubkey' style='display: none;'>".
$pubKey_html.
"</textarea>
<script type='text/javascript'>
$('#ENVIAR').click(function () {
var codificador = new JSEncrypt();
codificador.setKey($('#pubkey').val());
var cifrado = codificador.encrypt($('#mensaje').val());
window.open('?mensaje=' + encodeURIComponent(cifrado)
, '_top');
});
</script>
";
//------------------------------------------------------------
// Decrypting using php (it uses only the privateKey)
//------------------------------------------------------------
if (isset($_REQUEST['mensaje'])) {
openssl_private_decrypt( base64_decode($_REQUEST['mensaje'])
, $descifrado
, $privateKey);
$datos_html.= "
<h2>Descifrando con PHP</h2>
".$descifrado."
";
}
//------------------------------------------------------------
// HTML DISPLAY
//------------------------------------------------------------
$encabezado_html.= "<title>Receptor de mensaje cifrado</title>"
. $librerias_html;
$cuerpo_html.= $datos_html;
$contenido = "<head>$encabezado_html</head><body>$cuerpo_html</body>";
$contenido = "<html>$contenido</html>";
print $contenido;
//============================================================
//============================================================
// Functions
//============================================================
//============================================================
//------------------------------------------------------------
function cargar_llaves_RSA($directorio, $nombre_base) {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PROPÓSITO: Genera o carga desde archivos las llaves RSA
// ENTRADAS:
// $directorio: Directorio donde se encuentran los archivos.
// $nombre_base: Nombre, sin extensión, de los archivos con
// las llaves.
// SALIDAS:
//------------------------------------------------------------
if ( !file_exists($directorio.$nombre_base.".crt")
|| !file_exists($directorio.$nombre_base.".pub")) {
list($privateKey, $pubKey) = crear_llaves_RSA($directorio.$nombre_base);
} else {
//------------------------------------------------------------
// CARGA DE LLAVES RSA ARCHIVADAS
//------------------------------------------------------------
$privateKey = file_get_contents($directorio.$nombre_base.".crt");
if (!$privKey = openssl_pkey_get_private($privateKey))
die('Loading Private Key failed');
$pubKey = file_get_contents($directorio.$nombre_base.".pub");
}
return array($privateKey, $pubKey);
}
//------------------------------------------------------------
function crear_llaves_RSA($ruta_base) {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PROPÓSITO:
// generacion de llaves RSA en php
// ENTRADAS:
// $ruta_base: Ruta de los archivos a generar sin extensión.
// SALIDAS:
// Se generarán dos archivos, uno con la llave privada con
// extensión .crt, el otro con llave pública con extensión
// .pub; la función retorna tanto la llave pública como la
// privada en un arreglo.
//------------------------------------------------------------
$config = array(
"private_key_bits" => 1024,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$llavePrivadaCruda = openssl_pkey_new($config);
openssl_pkey_export_to_file($llavePrivadaCruda, $ruta_base.".crt");
$privateKey = file_get_contents($ruta_base.".crt");
openssl_pkey_export($llavePrivadaCruda, $privKey);
$pubKeyData = openssl_pkey_get_details($llavePrivadaCruda);
$pubKey = $pubKeyData["key"];
file_put_contents($ruta_base.".pub", $pubKey);
openssl_free_key($llavePrivadaCruda);
return array($privateKey, $pubKey);
}
//------------------------------------------------------------
function Mostrar($valor) {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PROPÓSITO: Genera el código HTML para presentar una
// variable embebida en la página.
// ENTRADAS:
// $valor: el valor a presentar.
// SALIDAS: código html que permite visualizar la variable.
//------------------------------------------------------------
$retorno = htmlentities(stripslashes(var_export($valor, true)));
$retorno = "<pre>$retorno</pre>";
return $retorno;
}
?>
Directory tree must looks like:
├── script.php
└── lib
└── jsencrypt.js
and a directory writable by php outside of public zone named
/path/to/key/directory/apache/writable/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With