0

I would like to store email addresses in a database but as it is a shared one, I would like to store them crypted and decrypted when needed.
I thought of Encrypting the first part of it (before the @) so i come up with something like this:
Real email: myemail@gmail.com Encrypted desired : 4n095tOA8PpRq5Nw2tIEp8l47@gmail.com

The proble is that when I use a function like this :

function EncryptVar($sVar){

return  openssl_encrypt($sVar, $this->encryptionMethod, $this->secretHash);

} 

with some secret hash and this following method

$this->secretHash = "25c6c7ff35b9979b151f2136cd1sdftrez";
$this->encryptionMethod = "AES-256-CBC";

I may come up with special characters in the encrypted part, therefore an invalid email address format.
Is there a way to use this method so I have only letters and digits?

Lebach
  • 19
  • 1
  • 6
  • 1
    why do you care if the email address format is valid after you encrypt it first part ? (it's only need to be valid before encryption and after decryption, correct me if I'm wrong) – Rabin May 09 '17 at 11:19
  • Hi @Rabin, as it is a shared database used in some webservices, it is mandatory on their db that the email format is respected and I don't feel like giving them the real email addresses of my customers for ethical and legal reasons. In some encryptions, I have this : "4n095tOA8PpRq5Nw2tIEp8l47m/VRxx5SRL0ZjF01Rc=@gmail.com", which is not a valid email format – Lebach May 09 '17 at 13:21
  • so what ? dose the DB enforce email validation on this field? or are you pushing the data via API which do some validation. if is the later, you can use `bin2hex` (instead base64 encode) which give you something like this. `E27D3DE6D380F0FA51AB9370DAD204A7C978EE6FD5471C794912F4663174D517@gmail.com` – Rabin May 09 '17 at 13:46
  • thanks a lot @Rabin, it actually works pretty good (the 2nd option as I had to send through the API valid emails...). So I send through the webservice a bin2hex of the encrypted value, then if i wanna read it, i first hex2bin it, then decrypt it and it gives me my initial email address. Thanks a lot! – Lebach May 09 '17 at 14:01
  • I'm glad I was able to help. – Rabin May 09 '17 at 14:11

2 Answers2

0

The common way to address this is to encode/decode using base64_encode / base64_decode. That will convert your binary data to an ASCII string.

laurent
  • 88,262
  • 77
  • 290
  • 428
  • Hi @this.lau_, the problem is that I can't let anyone easily decode them for legal and ethical issues, therefore i need to use a private key or secret hash... – Lebach May 09 '17 at 13:25
  • @Lebach, yes I mean first you encrypt it with your function EncryptVar, then you encode this with `base64_encode`. You can strip off the last "=" characters if you want, as the string will still decode fine with base64_decode. But note that "=", "/", etc. are [valid email characters](http://stackoverflow.com/a/2049510/561309) so you don't need to strip them off. – laurent May 09 '17 at 13:37
0

Is there a way to use this method so I have only letters and digits?

This is a great place to use base32-encoding.

A good implementation of this is available in this library.

An example that uses a secure encryption method:

<?php
use ParagonIE\ConstantTime\Base32;

class EmailEncryption
{
    protected $key;

    public function __construct($key)
    {
        if (mb_strlen($key, '8bit') !== 32) {
            throw new Exception('Keys should be 32 bytes generated from /dev/urandom');
        } 
    }

    /**
     * @param string $sVar
     * @return string
     */
    public function encryptVar($sVar)
    {
        $nonce = random_bytes(12);
        $tag = '';
        $encrypted = openssl_encrypt(
            $sVar,
            'aes-256-gcm',
            $this->key,
            OPENSSL_RAW_DATA,
            $nonce,
            $tag
        );
        return Base32::encode($tag . $nonce . $encrypted);
    }

    /**
     * @param string $sVar
     * @return string
     */
    public function decryptVar($sVar)
    {
        $decoded = Base32::decode($sVar);
        $tag = mb_substr($decoded, 0, 16, '8bit');
        $nonce = mb_substr($decoded, 16, 12, '8bit');
        $ciphertext = mb_substr($decoded, 28, null, '8bit');
        $plaintext = openssl_decrypt(
            $ciphertext,
            'aes-256-gcm',
            $this->key,
            OPENSSL_RAW_DATA,
            $nonce,
            $tag
        );
        if (is_bool($plaintext)) {
            throw new Exception('Invalid ciphertext');
        }
        return $plaintext;
    }
}

Usage:

$key = random_bytes(32);
$encrypter = new EmailEncryption($key);

$message = 'test123456789';
$ciphertext = $encrypter->encryptVar($message);
var_dump($ciphertext);
$plaintext = $encrypter->decryptVar($ciphertext);
var_dump($plaintext, $message);

Note: This requires PHP 7.1+, but gives you authenticated encryption.

Community
  • 1
  • 1
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206