<?php /** * Created by PhpStorm. * User: zongbinghuang * Date: 2017/7/31 * Time: 15:13 */ namespace appcommon; use Exception; class BizEncrypt { const IV_SIZE = 16; const CIPHER = 'AES-256-CBC'; private $key;#length:32 public function __construct($key) { $key = (string)$key; if (static::supported($key, self::CIPHER)) { $this->key = $key; } else { throw new Exception('The only supported ciphers are AES-256-CBC with the correct key lengths.'); } } public function encrypt($value) { $iv = self::random_bytes(self::IV_SIZE); $value = openssl_encrypt(serialize($value), self::CIPHER, $this->key, 0, $iv); if ($value === false) { throw new Exception('Could not encrypt the data.'); } $iv = base64_encode($iv); $mac = hash_hmac('sha256', $iv . $value, $this->key); $json = json_encode(compact('iv', 'value', 'mac')); if (!is_string($json)) { throw new Exception('Could not encrypt the data.'); } return base64_encode($json); } public function decrypt($payload) { $payload = $this->getJsonPayload($payload); $iv = base64_decode($payload['iv']); $decrypted = openssl_decrypt($payload['value'], self::CIPHER, $this->key, 0, $iv); if ($decrypted === false) { throw new Exception('Could not decrypt the data.'); } return unserialize($decrypted); } private static function supported($key, $cipher) { $length = mb_strlen($key, '8bit'); return ($cipher === 'AES-256-CBC' && $length === 32); } private function getJsonPayload($payload) { $payload = json_decode(base64_decode($payload), true); if (!$payload || $this->invalidPayload($payload)) { throw new Exception('The payload is invalid.'); } return $payload; } private function invalidPayload($data) { return !is_array($data) || !isset($data['iv']) || !isset($data['value']) || !isset($data['mac']); } private static function random_bytes($bytes) { try { $bytes = self::RandomCompareInteger($bytes); } catch (Exception $ex) { throw new Exception('random_bytes(): $bytes must be an integer'); } if ($bytes < 1) { throw new Exception('Length must be greater than 0'); } $secure = true; $buf = openssl_random_pseudo_bytes($bytes, $secure); if ($buf !== false && $secure && mb_strlen($buf, '8bit') === $bytes) { return $buf; } throw new Exception('Could not gather sufficient random data'); } private static function RandomCompareInteger($number, $fail_open = false) { if (is_numeric($number)) { $number += 0; } if (is_float($number) && $number > ~PHP_INT_MAX && $number < PHP_INT_MAX) { $number = (int)$number; } if (is_int($number) || $fail_open) { return $number; } throw new Exception('Expected an integer.'); } }