public keys * 'private' => private keys * 'envelope' => resulting envelope keys * ) */ protected $_keys = array( 'public' => array(), 'private' => array(), 'envelope' => array() ); /** * Internal passphrase * * @var string */ protected $_passphrase; /** * Internal compression * * @var array */ protected $_compression; /** * Internal create package * * @var boolean */ protected $_package = false; /** * Class constructor * Available options * 'public' => public key * 'private' => private key * 'envelope' => envelope key * 'passphrase' => passphrase * 'compression' => compress value with this compression adapter * 'package' => pack envelope keys into encrypted string, simplifies decryption * * @param string|array $options Options for this adapter */ public function __construct($options = array()) { if (!extension_loaded('openssl')) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('This filter needs the openssl extension'); } if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (!is_array($options)) { $options = array('public' => $options); } if (array_key_exists('passphrase', $options)) { $this->setPassphrase($options['passphrase']); unset($options['passphrase']); } if (array_key_exists('compression', $options)) { $this->setCompression($options['compression']); unset($options['compress']); } if (array_key_exists('package', $options)) { $this->setPackage($options['package']); unset($options['package']); } $this->_setKeys($options); } /** * Sets the encryption keys * * @param string|array $keys Key with type association * @return Zend_Filter_Encrypt_Openssl */ protected function _setKeys($keys) { if (!is_array($keys)) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Invalid options argument provided to filter'); } foreach ($keys as $type => $key) { if (ctype_print($key) && is_file(realpath($key)) && is_readable($key)) { $file = fopen($key, 'r'); $cert = fread($file, 8192); fclose($file); } else { $cert = $key; $key = count($this->_keys[$type]); } switch ($type) { case 'public': $test = openssl_pkey_get_public($cert); if ($test === false) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception("Public key '{$cert}' not valid"); } openssl_free_key($test); $this->_keys['public'][$key] = $cert; break; case 'private': $test = openssl_pkey_get_private($cert, $this->_passphrase); if ($test === false) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception("Private key '{$cert}' not valid"); } openssl_free_key($test); $this->_keys['private'][$key] = $cert; break; case 'envelope': $this->_keys['envelope'][$key] = $cert; break; default: break; } } return $this; } /** * Returns all public keys * * @return array */ public function getPublicKey() { $key = $this->_keys['public']; return $key; } /** * Sets public keys * * @param string|array $key Public keys * @return Zend_Filter_Encrypt_Openssl */ public function setPublicKey($key) { if (is_array($key)) { foreach($key as $type => $option) { if ($type !== 'public') { $key['public'] = $option; unset($key[$type]); } } } else { $key = array('public' => $key); } return $this->_setKeys($key); } /** * Returns all private keys * * @return array */ public function getPrivateKey() { $key = $this->_keys['private']; return $key; } /** * Sets private keys * * @param string $key Private key * @param string $passphrase * @return Zend_Filter_Encrypt_Openssl */ public function setPrivateKey($key, $passphrase = null) { if (is_array($key)) { foreach($key as $type => $option) { if ($type !== 'private') { $key['private'] = $option; unset($key[$type]); } } } else { $key = array('private' => $key); } if ($passphrase !== null) { $this->setPassphrase($passphrase); } return $this->_setKeys($key); } /** * Returns all envelope keys * * @return array */ public function getEnvelopeKey() { $key = $this->_keys['envelope']; return $key; } /** * Sets envelope keys * * @param string|array $options Envelope keys * @return Zend_Filter_Encrypt_Openssl */ public function setEnvelopeKey($key) { if (is_array($key)) { foreach($key as $type => $option) { if ($type !== 'envelope') { $key['envelope'] = $option; unset($key[$type]); } } } else { $key = array('envelope' => $key); } return $this->_setKeys($key); } /** * Returns the passphrase * * @return string */ public function getPassphrase() { return $this->_passphrase; } /** * Sets a new passphrase * * @param string $passphrase * @return Zend_Filter_Encrypt_Openssl */ public function setPassphrase($passphrase) { $this->_passphrase = $passphrase; return $this; } /** * Returns the compression * * @return array */ public function getCompression() { return $this->_compression; } /** * Sets a internal compression for values to encrypt * * @param string|array $compression * @return Zend_Filter_Encrypt_Openssl */ public function setCompression($compression) { if (is_string($this->_compression)) { $compression = array('adapter' => $compression); } $this->_compression = $compression; return $this; } /** * Returns if header should be packaged * * @return boolean */ public function getPackage() { return $this->_package; } /** * Sets if the envelope keys should be included in the encrypted value * * @param boolean $package * @return Zend_Filter_Encrypt_Openssl */ public function setPackage($package) { $this->_package = (boolean) $package; return $this; } /** * Encrypts $value with the defined settings * Note that you also need the "encrypted" keys to be able to decrypt * * @param string $value Content to encrypt * @return string The encrypted content * @throws Zend_Filter_Exception */ public function encrypt($value) { $encrypted = array(); $encryptedkeys = array(); if (count($this->_keys['public']) == 0) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Openssl can not encrypt without public keys'); } $keys = array(); $fingerprints = array(); $count = -1; foreach($this->_keys['public'] as $key => $cert) { $keys[$key] = openssl_pkey_get_public($cert); if ($this->_package) { $details = openssl_pkey_get_details($keys[$key]); if ($details === false) { $details = array('key' => 'ZendFramework'); } ++$count; $fingerprints[$count] = md5($details['key']); } } // compress prior to encryption if (!empty($this->_compression)) { #require_once 'Zend/Filter/Compress.php'; $compress = new Zend_Filter_Compress($this->_compression); $value = $compress->filter($value); } $crypt = openssl_seal($value, $encrypted, $encryptedkeys, $keys); foreach ($keys as $key) { openssl_free_key($key); } if ($crypt === false) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Openssl was not able to encrypt your content with the given options'); } $this->_keys['envelope'] = $encryptedkeys; // Pack data and envelope keys into single string if ($this->_package) { $header = pack('n', count($this->_keys['envelope'])); foreach($this->_keys['envelope'] as $key => $envKey) { $header .= pack('H32n', $fingerprints[$key], strlen($envKey)) . $envKey; } $encrypted = $header . $encrypted; } return $encrypted; } /** * Defined by Zend_Filter_Interface * * Decrypts $value with the defined settings * * @param string $value Content to decrypt * @return string The decrypted content * @throws Zend_Filter_Exception */ public function decrypt($value) { $decrypted = ""; $envelope = current($this->getEnvelopeKey()); if (count($this->_keys['private']) !== 1) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Please give a private key for decryption with Openssl'); } if (!$this->_package && empty($envelope)) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Please give a envelope key for decryption with Openssl'); } foreach($this->_keys['private'] as $key => $cert) { $keys = openssl_pkey_get_private($cert, $this->getPassphrase()); } if ($this->_package) { $details = openssl_pkey_get_details($keys); if ($details !== false) { $fingerprint = md5($details['key']); } else { $fingerprint = md5("ZendFramework"); } $count = unpack('ncount', $value); $count = $count['count']; $length = 2; for($i = $count; $i > 0; --$i) { $header = unpack('H32print/nsize', substr($value, $length, 18)); $length += 18; if ($header['print'] == $fingerprint) { $envelope = substr($value, $length, $header['size']); } $length += $header['size']; } // remainder of string is the value to decrypt $value = substr($value, $length); } $crypt = openssl_open($value, $decrypted, $envelope, $keys); openssl_free_key($keys); if ($crypt === false) { #require_once 'Zend/Filter/Exception.php'; throw new Zend_Filter_Exception('Openssl was not able to decrypt you content with the given options'); } // decompress after decryption if (!empty($this->_compression)) { #require_once 'Zend/Filter/Decompress.php'; $decompress = new Zend_Filter_Decompress($this->_compression); $decrypted = $decompress->filter($decrypted); } return $decrypted; } /** * Returns the adapter name * * @return string */ public function toString() { return 'Openssl'; } }