php - Two-way encryption: I need to store passwords that can be retrieved -


i creating application store passwords, user can retrieve , see. passwords hardware device, checking against hashes out of question.

what need know is:

  1. how encrypt , decrypt password in php?

  2. what safest algorithm encrypt passwords with?

  3. where store private key?

  4. instead of storing private key, idea require users enter private key time need password decrypted? (users of application can trusted)

  5. in ways can password stolen , decrypted? need aware of?

personally, use mcrypt others posted. there more note...

  1. how encrypt , decrypt password in php?

    see below strong class takes care of you:

  2. what safest algorithm encrypt passwords with?

    safest? of them. safest method if you're going encrypt protect against information disclosure vulnerabilities (xss, remote inclusion, etc). if gets out, attacker can crack encryption (no encryption 100% un-reversible without key - @nulluserexception points out not entirely true. there encryption schemes impossible crack such onetimepad).

  3. where store private key?

    what use 3 keys. 1 user supplied, 1 application specific , other user specific (like salt). application specific key can stored anywhere (in config file outside of web-root, in environmental variable, etc). user specific 1 stored in column in db next encrypted password. user supplied 1 not stored. then, you'd this:

    $key = $userkey . $serverkey . $usersuppliedkey; 

    the benefit there, 2 of keys can compromised without data being compromised. if there's sql injection attack, can $userkey, not other 2. if there's local server exploit, can $userkey , $serverkey, not third $usersuppliedkey. if go beat user wrench, can $usersuppliedkey, not other 2 (but again, if user beaten wrench, you're late anyway).

  4. instead of storing private key, idea require users enter private key time need password decrypted? (users of application can trusted)

    absolutely. in fact, that's way it. otherwise you'd need store unencrypted version in durable storage format (shared memory such apc or memcached, or in session file). that's exposing additional compromises. never store unencrypted version of password in except local variable.

  5. in ways can password stolen , decrypted? need aware of?

    any form of compromise of systems let them view encrypted data. if can inject code or filesystem, can view decrypted data (since can edit files decrypt data). form of replay or mitm attack give them full access keys involved. sniffing raw http traffic give them keys.

    use ssl traffic. , make sure nothing on server has kind of vulnerabilities (csrf, xss, sql injection, privilege escalation, remote code execution, etc).

edit: here's php class implementation of strong encryption method:

/**  * class handle secure encryption , decryption of arbitrary data  *  * note not straight encryption.  has few other  *  features in make encrypted data far more secure.  note  *  other implementations used decrypt data have same exact  *  operations.    *  * security benefits:  *  * - uses key stretching  * - hides initialization vector  * - hmac verification of source data  *  */ class encryption {      /**      * @var string $cipher mcrypt cipher use instance      */     protected $cipher = '';      /**      * @var int $mode mcrypt cipher mode use      */     protected $mode = '';      /**      * @var int $rounds number of rounds feed pbkdf2 key generation      */     protected $rounds = 100;      /**      * constructor!      *      * @param string $cipher mcrypt_* cypher use instance      * @param int    $mode   mcrypt_mode_* mode use instance      * @param int    $rounds number of pbkdf2 rounds on key      */     public function __construct($cipher, $mode, $rounds = 100) {         $this->cipher = $cipher;         $this->mode = $mode;         $this->rounds = (int) $rounds;     }      /**      * decrypt data provided key      *      * @param string $data encrypted datat decrypt      * @param string $key  key use decryption      *       * @returns string|false returned string if decryption successful      *                           false if not      */     public function decrypt($data, $key) {         $salt = substr($data, 0, 128);         $enc = substr($data, 128, -64);         $mac = substr($data, -64);          list ($cipherkey, $mackey, $iv) = $this->getkeys($salt, $key);          if (!hash_equals(hash_hmac('sha512', $enc, $mackey, true), $mac)) {              return false;         }          $dec = mcrypt_decrypt($this->cipher, $cipherkey, $enc, $this->mode, $iv);          $data = $this->unpad($dec);          return $data;     }      /**      * encrypt supplied data using supplied key      *       * @param string $data data encrypt      * @param string $key  key encrypt      *      * @returns string encrypted data      */     public function encrypt($data, $key) {         $salt = mcrypt_create_iv(128, mcrypt_dev_urandom);         list ($cipherkey, $mackey, $iv) = $this->getkeys($salt, $key);          $data = $this->pad($data);          $enc = mcrypt_encrypt($this->cipher, $cipherkey, $data, $this->mode, $iv);          $mac = hash_hmac('sha512', $enc, $mackey, true);         return $salt . $enc . $mac;     }      /**      * generates set of keys given random salt , master key      *      * @param string $salt random string change keys each encryption      * @param string $key  supplied key encrypt      *      * @returns array array of keys (a cipher key, mac key, , iv)      */     protected function getkeys($salt, $key) {         $ivsize = mcrypt_get_iv_size($this->cipher, $this->mode);         $keysize = mcrypt_get_key_size($this->cipher, $this->mode);         $length = 2 * $keysize + $ivsize;          $key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);          $cipherkey = substr($key, 0, $keysize);         $mackey = substr($key, $keysize, $keysize);         $iv = substr($key, 2 * $keysize);         return array($cipherkey, $mackey, $iv);     }      /**      * stretch key using pbkdf2 algorithm      *      * @see http://en.wikipedia.org/wiki/pbkdf2      *      * @param string $algo   algorithm use      * @param string $key    key stretch      * @param string $salt   random salt      * @param int    $rounds number of rounds derive      * @param int    $length length of output key      *      * @returns string derived key.      */     protected function pbkdf2($algo, $key, $salt, $rounds, $length) {         $size   = strlen(hash($algo, '', true));         $len    = ceil($length / $size);         $result = '';         ($i = 1; $i <= $len; $i++) {             $tmp = hash_hmac($algo, $salt . pack('n', $i), $key, true);             $res = $tmp;             ($j = 1; $j < $rounds; $j++) {                  $tmp  = hash_hmac($algo, $tmp, $key, true);                  $res ^= $tmp;             }             $result .= $res;         }         return substr($result, 0, $length);     }      protected function pad($data) {         $length = mcrypt_get_block_size($this->cipher, $this->mode);         $padamount = $length - strlen($data) % $length;         if ($padamount == 0) {             $padamount = $length;         }         return $data . str_repeat(chr($padamount), $padamount);     }      protected function unpad($data) {         $length = mcrypt_get_block_size($this->cipher, $this->mode);         $last = ord($data[strlen($data) - 1]);         if ($last > $length) return false;         if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {             return false;         }         return substr($data, 0, -1 * $last);     } } 

note i'm using function added in php 5.6: hash_equals. if you're on lower 5.6, can use substitute function implements timing-safe comparison function using double hmac verification:

function hash_equals($a, $b) {     $key = mcrypt_create_iv(128, mcrypt_dev_urandom);     return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key); } 

usage:

$e = new encryption(mcrypt_blowfish, mcrypt_mode_cbc); $encrypteddata = $e->encrypt($data, $key); 

then, decrypt:

$e2 = new encryption(mcrypt_blowfish, mcrypt_mode_cbc); $data = $e2->decrypt($encrypteddata, $key); 

note used $e2 second time show different instances still decrypt data.

now, how work/why use on solution:

  1. keys

    • the keys not directly used. instead, key stretched standard pbkdf2 derivation.

    • the key used encryption unique every encrypted block of text. supplied key therefore becomes "master key". class therefore provides key rotation cipher , auth keys.

    • important note, $rounds parameter configured true random keys of sufficient strength (128 bits of cryptographically secure random @ minimum). if going use password, or non-random key (or less random 128 bits of cs random), must increase parameter. suggest minimum of 10000 passwords (the more can afford, better, add runtime)...

  2. data integrity

    • the updated version uses encrypt-then-mac, far better method ensuring authenticity of encrypted data.
  3. encryption:

    • it uses mcrypt perform encryption. suggest using either mcrypt_blowfish or mcrypt_rijndael_128 cyphers , mcrypt_mode_cbc mode. it's strong enough, , still fast (an encryption , decryption cycle takes 1/2 second on machine).

now, point 3 first list, give function this:

function makekey($userkey, $serverkey, $usersuppliedkey) {     $key = hash_hmac('sha512', $userkey, $serverkey);     $key = hash_hmac('sha512', $key, $usersuppliedkey);     return $key; } 

you stretch in makekey() function, since it's going stretched later, there's not huge point doing so.

as far storage size, depends on plain text. blowfish uses 8 byte block size, you'll have:

  • 16 bytes salt
  • 64 bytes hmac
  • data length
  • padding data length % 8 == 0

so 16 character data source, there 16 characters of data encrypted. means actual encrypted data size 16 bytes due padding. add 16 bytes salt , 64 bytes hmac , total stored size 96 bytes. there's @ best 80 character overhead, , @ worst 87 character overhead...

i hope helps...

note: 12/11/12: updated class better encryption method, using better derived keys, , fixing mac generation...


Comments

Popular posts from this blog

linux - Mailx and Gmail nss config dir -

c# - Is it possible to remove an existing registration from Autofac container builder? -

php - Mysql PK and FK char(36) vs int(10) -