How to use openssl_encrypt encryption method?

Asked

Viewed 4,450 times

6

It’s been a week that I search in everything that is site but I can not understand, I’m very curious about the use of this function but I can not find anything that explains in a simple way, someone can help me please ?

  • 2

    Why does the description of the question have only this small piece of text? What is the doubt?

  • @Wallacemaxters apparently he edited, I don’t know why.

  • @Inkeliz you explained very well, I preferred to take out of my text some things that I found unnecessary and confusing

2 answers

6


This response has been completely modified in order to correct errors in the examples and avoid misuse of this resource. In addition, the use of PHP 7.1 and PHP 7.2 features, previously unavailable.


The openss_encrypt, as well as the openssl_public_encrypt, are encryption methods that are reversible. They enable the holder of the private key to view the original text. This may seem obvious, but there are cryptographic methods where the purpose is precisely to prevent recovery of the original text.


A key for both sides:

The openssl_encrypt allows you to use various types of symmetric ciphers, for example DES, 3DES, AES, CAMELLIA. Symmetric ciphers require that both involved (who sends and receives) have knowledge of the same key.

You are able to see all "supported" ciphers using:

var_dump( openssl_get_cipher_methods() );

In general most people use what the NIST defines, that is, they use AES (and in the past they used DES and then 3DES). In fact when using AES you are using Rijndael with parameters defined by NIST, Rijndael won the same competition.

CAMELLIA is a European standard (defined by CRYPTREC and NESSIE), equivalent to AES, it has less use and consequently also fewer attacks known to date.

Anyway, trust whoever you want and use the cipher you want and fulfill with the desired goal.

Unauthenticated:

This method prevents a person without possession of the key and just looking at the $TextoCifrado get to know what was written. However, you DO NOT prevent that person from making changes to the text. An example would be the person changing the IV, she can do this and this would "flip" a few bits of the first block, which could still make the text understandable to the human, sometimes changing the meaning of the text.

AES-CBC:

// Não dê CTRL+C e CTRL+V neste código, ELE TEM PROBLEMAS DEPENDENDO DO SEU USO!
// Não ignore isto, leia acima!

$TextoClaro = 'Um texto muito bacana e muito legal';
$Cifra =  'AES-256-CBC';

// $Chave =  random_bytes(32);
$IV = random_bytes(openssl_cipher_iv_length($Cifra)); 

$TextoCifrado = openssl_encrypt($TextoClaro, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV);

echo $Resultado = base64_encode($IV.$TextoCifrado);

Explanations:

The $TextoClaro and $Cifra I believe they are self-explanatory, in themselves you can already know what they refer to.

Already the $Chave, $IV and OPENSSL_RAW_DATA may seem strange.

  • $Chave: First we need to distinguish what is a key and a password. This is not a password, passwords are weak, passwords are a set of characters selected by a human. Never use passwords as keys. Keys must be strong, in this case 256 bits, from a secure source and from a generated one that is safe (the random_bytes was included in PHP 7 and it is a CSPRNG). It must be secret and kept secret, the encryption will only work as long as no one knows this key.

  • $IV: The IV is a initialization vector, it is used to perform the first operation against the first block of the cipher, in most ciphers. This ensures that even two identical messages generate different results as long as the IR is different. Precisely because of this property the IV should be unique and should never repeat with the same key, but there is no need to be secret and not even be random. The IV can be passed along with the $TextoCifrado, as is done above.

  • OPENSSL_RAW_DATA: It is used for the result to be in bytes (rather than encoded in Base64), this is done because we join the $IV at the $TextoCifrado, therefore it makes no sense to use a Base64 inside another. This same parameter could also receive the OPENSSL_ZERO_PADDING, if this were used the $TextoClaro should have a multiple block length (16 in this case), since the padding would not be done automatically.

The $Resultado is exactly the BASE64([IV]+[Texto Cifrado]), supposing that + either concatenation.

Decrypt:

To "decrypt" we use the openssl_decrypt:

$Resultado = base64_decode($Resultado);

$TextoCifrado = mb_substr($Resultado, openssl_cipher_iv_length($Cifra), null, '8bit');
$Cifra =  'AES-256-CBC';

//$Chave = pack('H*', 'be3494ff4904fd83bf78e3cec0d38ddbf48d0a6a666be05420667a5a7d2c4e0d');
$IV = mb_substr($Resultado, 0, openssl_cipher_iv_length($Cifra), '8bit');

echo $TextoClaro = openssl_decrypt($TextoCifrado, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV);

The $Resultado stands to be the $Resultado of the previous operation. In addition $Chave must be the same as the previous operation, do not use this key used in the example! The $TextoCifrado and the $IV will be extracted using the mb_substr since both were concatenated in the same Base64.


Authenticated:

One way to prevent "malleability" is to guarantee integrity, obviously. This is using AES-GCM (available in PHP 7.1), using an HMAC (available in PHP 5.6) or using Chacha20-Poly1305 (available in PHP 7.2 or via PECL), I will mention them all here.

Chacha20-Poly1305 (requires PHP 7.2 or PECL):

If you prefer not to use the AES as I do... You have the option to use the ChaCha20-Poly1305 using Libsodium, which is available in PHP 7.2. It will ensure that the text has not been modified, to use it is very simple:

$TextoClaro = 'Um texto muito bacana e muito legal';
$TextoPublico = ''; // Opcional, ele pode ser um ID de usuário, por exemplo, que NÃO SERÁ criptografado!

//$Chave =  random_bytes(32);
$IV = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);

$TextoCifrado = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($TextoClaro, $TextoPublico, $IV, $Chave);

echo $Resultado = base64_encode($IV . $TextoCifrado);

Then to recover the text:

$Resultado = base64_decode($Resultado);

$TextoCifrado = mb_substr($Resultado, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit');
$IV = mb_substr($Resultado, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, '8bit');

$TextoPublico = '';
//$Chave = pack('H*', 'be3494ff4904fd83bf78e3cec0d38ddbf48d0a6a666be05420667a5a7d2c4e0d');

echo $TextoClaro = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($TextoCifrado, $TextoPublico, $IV, $Chave);

Note, if you use $TextoPublico you will have to make some adaptations.

AES-GCM (requires PHP 7.1):

Everyone’s standard, you can use the AES-GCM in place of AES-CBC, in the meantime you must inform a new parameter, the $Tag. Unlike Libsodium, example above, it does not automatically add the TAG to the end of the string, so in the end it will be:

$Cifra = 'AES-256-GCM';
//...

$TextoCifrado = openssl_encrypt($TextoCifrado, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV, $TAG);

echo $Resultado = $IV.$TAG.$TextoCifrado;

To $Tag will ensure that the text has not been changed, it uses GMAC. You can also include another parameter, after the $Tag to include some unencrypted information, for example id user:

$TextoPublicoAdicional = 123;

openssl_encrypt($TextoCifrado, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV, $TAG, $TextoPublicoAdicional);

The $ID_DO_USUARIO will be in clear text (will not be encrypted), but will be covered by GMAC, or if it is modified will be noticed. There is no mystery to decrypt, just use the $IV and $TAG contained in the $Resultado and use the openssl_decrypt.

AES-CBC + HMAC (requires PHP 5.6):

If you can’t use any of this, you can use HMAC and compare to see if the text has been changed or not, so you need to use another key and make a result HMAC:

$TextoClaro = 'Um texto muito bacana e muito legal';
$Cifra =  'AES-256-CBC';

//$ChaveHMAC = random_bytes(32);
//$Chave = random_bytes(32);

$IV = random_bytes(openssl_cipher_iv_length($Cifra)); 

$TextoCifrado = openssl_encrypt($TextoClaro, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV);

$HMAC = hash_hmac('sha384', $IV . $TextoCifrado, $ChaveHMAC, true);

$Resultado = base64_encode($HMAC . $IV . $TextoCifrado);

This will contain the 384 bits (48 bytes) of $Resultado text HMAC, in this case are using sha384, you can use other hash algorithms. This will ensure that any changes in the text will make the HMAC hash invalid.

Then to check the result, to decrypt:

$Resultado = base64_decode($Resultado);

$TextoCifrado = mb_substr($Resultado, 48 + openssl_cipher_iv_length($Cifra), null, '8bit');
$Cifra =  'AES-256-CBC';

//$Chave = pack('H*', 'be3494ff4904fd83bf78e3cec0d38ddbf48d0a6a666be05420667a5a7d2c4e0d');
//$ChaveHMAC = pack('H*', '4f55622d36ca62310f3b85ba933d84e65cd3241e4cabe08802fb611da713ef9b');

$IV = mb_substr($Resultado, 48, openssl_cipher_iv_length($Cifra), '8bit');

$HMAC = mb_substr($Resultado, 0, 48, '8bit');

if(!hash_equals(hash_hmac('sha384', $IV . $TextoCifrado, $ChaveHMAC, true), $HMAC)){
  echo "O texto foi alterado";
  exit();
}

echo $TextoClaro = openssl_decrypt($TextoCifrado, $Cifra, $Chave, OPENSSL_RAW_DATA, $IV);

Note that I am placing the IV inside the HMAC, this is extremely important.


NOTE:

In no case are you protected from replay-Attack, a third party may store the encrypted texts (as well as the tag and the iv) and at a future time send that information again. One way to contain this is to include a sequential number ($i++) within the message, this also identifies the loss of messages. There is another option which is to include a date-time, however the date-time problem is that an agent can attack the NTP servers used to get the correct time, for example, in this situation would automatically invalidate legitimate messages.

  • The variable $Chave is commented on in your first code. Why? I didn’t understand it very well. Is this key unique? I ask her once and save to use later in the next transactions? Why when I give one echo in it, strange characters appear to me.

  • 1

    Because the $Chave should be the "your key", depending on the goal, it should be uniformly random. Is this key unique? She must be unique, anyone who knows the key can decipher the message. I ask her once and keep it? Depends, do you want to have access to this content in the future or will it be sent only once? If you want to redeem the message at a future point store the key. Why when I give one echo strange characters appear? Because UTF-8/ASCII does not have all the bittable bytes, example: 0x00 is void and the 0xFF does not exist in ASCII, so it shows a character "wrong"

2

Two different keys:

If you want to use asymmetric encryption you seek the use of openssl_public_encrypt, on it you can use a public key. You can generate a private key using openssl_pkey_new, so you can disclose your public key and allow third parties to send you messages using the public key, where only you, with the private key will know the content of it.

The problem of openssl_public_encrypt is that it is very precarious, by default uses a broken padding (using OPENSSL_PKCS1_PADDING instead of PENSSL_PKCS1_OAEP_PADDING) and even the PENSSL_PKCS1_OAEP_PADDING it uses SHA1]. In addition to not supporting ECC, it is restricted to the use of RSA, at least until now.

So I will not mention the use of Openssl and its RSA. All examples are using Libsodium, natively supported in PHP 7.2.

Authenticated:

You can use the crypto_box and the crypto_box_open to encrypt a message and maintain integrity, and be able to identify who sent the message.

In this case you need two pairs of keys (a pair of keys for Alice, a pair of keys for Bob), each will know three keys, as follows:

$AliceKP = sodium_crypto_box_keypair();

This will go a pair of keys (Keypair, KP), for Alice. BOB will have to do the same:

$BobKP = sodium_crypto_box_keypair();

Soon, we make:

$AliceKP = sodium_crypto_box_keypair();

// Chave pública de Alice (que será compartilhada com Bob):
$AlicePK = sodium_crypto_box_publickey($AliceKP); // 8c133d34045ac249dac1f572f3e452f1644166cb56b9f23d75f132dae56d2477

// Chave privada de Alice:
$AliceSK = sodium_crypto_box_secretkey($AliceKP); // ae70f388f19b9d7739f342df31abe722a7791f9bd1831e9a4db703112aad2696

Bob needs to send us his public key for that:

$BobKP = sodium_crypto_box_keypair();

// Chave pública de Bob (que será compartilhada com Alice):
$BobPK = sodium_crypto_box_publickey($BobKP); // d8b91792ce11783fa4b32bfcb3f636c3785d0589e35a2b3b30aec5019c690d61

// Chave privada de Bob:
$BobPK = sodium_crypto_box_secretkey($BobKP); // 3b505365150b8f42014416ecffa546ef4e6196d69d8c4a1782458ebb3117776e

In the end Bob will pass to Alice the $BobPK and Alice will share the $AlicePK for Bob. Now we can encrypt the text:

$AliceKP = sodium_crypto_box_keypair();

// Chave privada de Alice:
$AliceSK = pack('H*', 'ae70f388f19b9d7739f342df31abe722a7791f9bd1831e9a4db703112aad2696');

// Chave pública de Bob:
$BobPK = pack('H*', 'd8b91792ce11783fa4b32bfcb3f636c3785d0589e35a2b3b30aec5019c690d61');

// Cria par de chaves entre Alice e Bob:
$AliceParaBobKP = sodium_crypto_box_keypair_from_secretkey_and_publickey(
    $AliceSK,
    $BobPK
);

// Gera um número único (equivalente ao IV):
$Nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);

// Criptografa o texto:
$TextoCifrado = sodium_crypto_box(
    'Meu texto muito bacana, muito legal',
    $Nonce,
    $AliceParaBobKP
);

echo $Resultado = base64_encode($Nonce . $TextoCifrado); // nCfABXic/JghqSaXHuUvWYdZ1LrDVkOCQLleSw51LtRtOpy1D+XH8XMZMD6djFGJUC/aCsxO5j7ogd90WTmadI6sWklmpu/bRi5m

Now Alice can share with Bob her $Resultado. Bob can read the text from Alice using:

$Resultado = base64_decode('nCfABXic/JghqSaXHuUvWYdZ1LrDVkOCQLleSw51LtRtOpy1D+XH8XMZMD6djFGJUC/aCsxO5j7ogd90WTmadI6sWklmpu/bRi5m');

$TextoCifrado = mb_substr($Resultado, SODIUM_CRYPTO_BOX_NONCEBYTES, null, '8bit');
$Nonce = mb_substr($Resultado, 0, SODIUM_CRYPTO_BOX_NONCEBYTES, '8bit');

// Chave privada de Bob:
$BobSK = pack('H*', '3b505365150b8f42014416ecffa546ef4e6196d69d8c4a1782458ebb3117776e');

// Chave pública de Alice:
$AlicePK = pack('H*', '8c133d34045ac249dac1f572f3e452f1644166cb56b9f23d75f132dae56d2477');

// Cria par de chaves entre Bob e Alice (a ordem mudou!):
$AliceParaBobKP = sodium_crypto_box_keypair_from_secretkey_and_publickey(
    $BobSK,
    $AlicePK
);

$TexoClaro = sodium_crypto_box_open(
    $TextoCifrado,
    $Nonce,
    $AliceParaBobKP
);

echo $Resultado = ($TexoClaro ?: 'Erro ocorrreu');

Anonymity:

If you do not want to have to exchange public keys or the sender does not have a pair of keys, then you can send messages using the crypto_box_seal:

$BobPK = pack('H*', 'd8b91792ce11783fa4b32bfcb3f636c3785d0589e35a2b3b30aec5019c690d61');

$TextoCifrado = sodium_crypto_box_seal(
    'Um texto bacana, mas anonimo',
    $BobPK
);

echo $Resultado = base64_encode($TextoCifrado); // bgSXKQmj4nc0JYtYYk1rsp6P/OuCqv1ThdRJCokoQBkMY8N57C0gCiVQLT/dkOu0mFVgNc+c1OXJa6nZcFoP6YzGHH6LZRzRLrTPTg==

So Charlie can send a message to Bob without Bob knowing his public key, which means Bob won’t know who sent the message. Bob in turn can open the message using:

$BobPK = pack('H*', 'd8b91792ce11783fa4b32bfcb3f636c3785d0589e35a2b3b30aec5019c690d61');
$BobSK = pack('H*', '3b505365150b8f42014416ecffa546ef4e6196d69d8c4a1782458ebb3117776e');

// Cria par de chaves entre Bob e Bob (sim, ele e ele mesmo):
$BobKP = sodium_crypto_box_keypair_from_secretkey_and_publickey(
    $BobSK,
    $BobPK
);

$TextoClaro = sodium_crypto_box_seal_open(
    base64_decode($Resultado),
    $BobKP
);

echo $TextoClaro;

Browser other questions tagged

You are not signed in. Login or sign up in order to post.