/** * Gets the available keys in the keyring. * * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See * the first section of <b>doc/DETAILS</b> in the * {@link http://www.gnupg.org/download/ GPG package} for a detailed * description of how the GPG command output is parsed. * * @param string $keyId optional. Only keys with that match the specified * pattern are returned. The pattern may be part of * a user id, a key id or a key fingerprint. If not * specified, all keys are returned. * * @return array an array of {@link Crypt_GPG_Key} objects. If no keys * match the specified <kbd>$keyId</kbd> an empty array is * returned. * * @throws Exception if an unknown or unexpected error occurs. * Use the <kbd>debug</kbd> option and file a bug report if these * exceptions occur. * * @see Crypt_GPG_Key */ protected function _getKeys($keyId = '') { // get private key fingerprints if ($keyId == '') { $operation = '--list-secret-keys'; } else { $operation = '--list-secret-keys ' . escapeshellarg($keyId); } // According to The file 'doc/DETAILS' in the GnuPG distribution, using // double '--with-fingerprint' also prints the fingerprint for subkeys. $arguments = array('--with-colons', '--with-fingerprint', '--with-fingerprint', '--fixed-list-mode'); $output = ''; $this->engine->reset(); $this->engine->setOutput($output); $this->engine->setOperation($operation, $arguments); $this->engine->run(); $code = $this->engine->getErrorCode(); switch ($code) { case self::ERROR_NONE: case self::ERROR_KEY_NOT_FOUND: // ignore not found key errors break; case self::ERROR_FILE_PERMISSIONS: $filename = $this->engine->getErrorFilename(); if ($filename) { throw new \Exception(sprintf('Error reading GnuPG data file \'%s\'. Check to make ' . 'sure it is readable by the current user.', $filename), $code); } throw new \Exception('Error reading GnuPG data file. Check to make GnuPG data ' . 'files are readable by the current user.', $code); default: throw new \Exception('Unknown error getting keys. Please use the \'debug\' option ', $code); } $privateKeyFingerprints = array(); $lines = explode(PHP_EOL, $output); foreach ($lines as $line) { $lineExp = explode(':', $line); if ($lineExp[0] == 'fpr') { $privateKeyFingerprints[] = $lineExp[9]; } } // get public keys if ($keyId == '') { $operation = '--list-public-keys'; } else { $operation = '--list-public-keys ' . escapeshellarg($keyId); } $output = ''; $this->engine->reset(); $this->engine->setOutput($output); $this->engine->setOperation($operation, $arguments); $this->engine->run(); $code = $this->engine->getErrorCode(); switch ($code) { case self::ERROR_NONE: case self::ERROR_KEY_NOT_FOUND: // ignore not found key errors break; case self::ERROR_FILE_PERMISSIONS: $filename = $this->engine->getErrorFilename(); if ($filename) { throw new \Exception(sprintf('Error reading GnuPG data file \'%s\'. Check to make ' . 'sure it is readable by the current user.', $filename), $code); } throw new \Exception('Error reading GnuPG data file. Check to make GnuPG data ' . 'files are readable by the current user.', $code); default: throw new \Exception('Unknown error getting keys. Please use the \'debug\' option ', $code); } $keys = array(); $key = null; // current key $subKey = null; // current sub-key $lines = explode(PHP_EOL, $output); foreach ($lines as $line) { $lineExp = explode(':', $line); if ($lineExp[0] == 'pub') { // new primary key means last key should be added to the array if ($key !== null) { $keys[] = $key; } $key = new Key(); $subKey = self::parseSubKeyLine($line); $key->addSubKey($subKey); } elseif ($lineExp[0] == 'sub') { $subKey = self::parseSubKeyLine($line); $key->addSubKey($subKey); } elseif ($lineExp[0] == 'fpr') { $fingerprint = $lineExp[9]; // set current sub-key fingerprint $subKey->setFingerprint($fingerprint); // if private key exists, set has private to true if (in_array($fingerprint, $privateKeyFingerprints)) { $subKey->setHasPrivate(true); } } elseif ($lineExp[0] == 'uid') { $string = stripcslashes($lineExp[9]); // as per documentation $userId = self::parseUserIdLine($string); // isRevoked if (strpos($lineExp[1], 'r') !== false) { $userId->setIsRevoked(true); } // isValid if (strpos($lineExp[1], 'i') !== false) { $userId->setIsValid(false); } // isValid if (strpos($lineExp[1], 'd') !== false) { $userId->setDisabled(true); } // validity if (strpos($lineExp[1], 'n') !== false) { //NEVER } elseif (strpos($lineExp[1], 'm') !== false) { //MARGINAL } elseif (strpos($lineExp[1], 'f') !== false) { //FULL } elseif (strpos($lineExp[1], 'u') !== false) { //ULTIMATE } $key->addUserId($userId); } } // add last key if ($key !== null) { $keys[] = $key; } return $keys; }
/** * Decrypts and verifies encrypted, signed data * * @param string $data the encrypted signed data to be decrypted and * verified. * @param boolean $isFile whether or not the data is a filename. * @param string $outputFile the name of the file to which the decrypted * data should be written. If null, the decrypted * data is returned in the results array. * * @return array two element array. The array has an element 'data' * containing the decrypted data and an element * 'signatures' containing an array of * {@link Crypt_GPG_Signature} objects for the signed data. * If the decrypted data is written to a file, the 'data' * element is null. * * @throws Crypt_GPG_KeyNotFoundException if the private key needed to * decrypt the data is not in the user's keyring or it the public * key needed for verification is not in the user's keyring. * * @throws Crypt_GPG_NoDataException if specified data does not contain * GPG signed, encrypted data. * * @throws Crypt_GPG_BadPassphraseException if a required passphrase is * incorrect or if a required passphrase is not specified. See * {@link Crypt_GPG::addDecryptKey()}. * * @throws Crypt_GPG_FileException if the output file is not writeable or * if the input file is not readable. * * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. * Use the <kbd>debug</kbd> option and file a bug report if these * exceptions occur. * * @see Crypt_GPG_Signature */ protected function _decryptAndVerify($data, $isFile, $outputFile) { if ($isFile) { $input = @fopen($data, 'rb'); if ($input === false) { throw new Crypt_GPG_FileException('Could not open input file "' . $data . '" for decrypting and verifying.', 0, $data); } } else { $input = strval($data); if ($input == '') { throw new Crypt_GPG_NoDataException('No valid encrypted signed data found.', self::ERROR_NO_DATA); } } if ($outputFile === null) { $output = ''; } else { $output = @fopen($outputFile, 'wb'); if ($output === false) { if ($isFile) { fclose($input); } throw new Crypt_GPG_FileException('Could not open output file "' . $outputFile . '" for storing decrypted data.', 0, $outputFile); } } $verifyHandler = new Crypt_GPG_VerifyStatusHandler(); $decryptHandler = new Crypt_GPG_DecryptStatusHandler($this->engine, $this->decryptKeys); // If using gpg-agent, set the decrypt pins used by the pinentry $this->_setPinEntryEnv($this->decryptKeys); $this->engine->reset(); $this->engine->addStatusHandler(array($verifyHandler, 'handle')); $this->engine->addStatusHandler(array($decryptHandler, 'handle')); $this->engine->setInput($input); $this->engine->setOutput($output); $this->engine->setOperation('--decrypt'); $this->engine->run(); if ($isFile) { fclose($input); } if ($outputFile !== null) { fclose($output); } $return = array('data' => null, 'signatures' => $verifyHandler->getSignatures()); // if there was any problem decrypting the data, the handler will // deal with it here. try { $decryptHandler->throwException(); } catch (Exception $e) { if ($e instanceof Crypt_GPG_KeyNotFoundException) { throw new Crypt_GPG_KeyNotFoundException('Public key required for data verification not in ', 'the keyring. Either no suitable private decryption key ' . 'is in the keyring or the public key required for data ' . 'verification is not in the keyring. Import a suitable ' . 'key before trying to decrypt and verify this data.', self::ERROR_KEY_NOT_FOUND, $this->engine->getErrorKeyId()); } if ($e instanceof Crypt_GPG_NoDataException) { throw new Crypt_GPG_NoDataException('Cannot decrypt and verify data. No PGP encrypted data ' . 'was found in the provided data.', self::ERROR_NO_DATA); } throw $e; } if ($outputFile === null) { $return['data'] = $output; } return $return; }
/** * Gets the available keys in the keyring * * Calls GPG with the <kbd>--list-keys</kbd> command and grabs keys. See * the first section of <b>doc/DETAILS</b> in the * {@link http://www.gnupg.org/download/ GPG package} for a detailed * description of how the GPG command output is parsed. * * @param string $keyId optional. Only keys with that match the specified * pattern are returned. The pattern may be part of * a user id, a key id or a key fingerprint. If not * specified, all keys are returned. * * @return array an array of {@link Crypt_GPG_Key} objects. If no keys * match the specified <kbd>$keyId</kbd> an empty array is * returned. * * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs. * Use the <kbd>debug</kbd> option and file a bug report if these * exceptions occur. * * @see Crypt_GPG_Key */ protected function _getKeys($keyId = '') { // get private key fingerprints if ($keyId == '') { $operation = '--list-secret-keys'; } else { $operation = '--utf8-strings --list-secret-keys ' . escapeshellarg($keyId); } // According to The file 'doc/DETAILS' in the GnuPG distribution, using // double '--with-fingerprint' also prints the fingerprint for subkeys. $arguments = array('--with-colons', '--with-fingerprint', '--with-fingerprint', '--fixed-list-mode'); $output = ''; $this->engine->reset(); $this->engine->setOutput($output); $this->engine->setOperation($operation, $arguments); $this->engine->run(); $privateKeyFingerprints = array(); foreach (explode(PHP_EOL, $output) as $line) { $lineExp = explode(':', $line); if ($lineExp[0] == 'fpr') { $privateKeyFingerprints[] = $lineExp[9]; } } // get public keys if ($keyId == '') { $operation = '--list-public-keys'; } else { $operation = '--utf8-strings --list-public-keys ' . escapeshellarg($keyId); } $output = ''; $this->engine->reset(); $this->engine->setOutput($output); $this->engine->setOperation($operation, $arguments); $this->engine->run(); $keys = array(); $key = null; // current key $subKey = null; // current sub-key foreach (explode(PHP_EOL, $output) as $line) { $lineExp = explode(':', $line); if ($lineExp[0] == 'pub') { // new primary key means last key should be added to the array if ($key !== null) { $keys[] = $key; } $key = new Crypt_GPG_Key(); $subKey = Crypt_GPG_SubKey::parse($line); $key->addSubKey($subKey); } elseif ($lineExp[0] == 'sub') { $subKey = Crypt_GPG_SubKey::parse($line); $key->addSubKey($subKey); } elseif ($lineExp[0] == 'fpr') { $fingerprint = $lineExp[9]; // set current sub-key fingerprint $subKey->setFingerprint($fingerprint); // if private key exists, set has private to true if (in_array($fingerprint, $privateKeyFingerprints)) { $subKey->setHasPrivate(true); } } elseif ($lineExp[0] == 'uid') { $string = stripcslashes($lineExp[9]); // as per documentation $userId = new Crypt_GPG_UserId($string); if ($lineExp[1] == 'r') { $userId->setRevoked(true); } $key->addUserId($userId); } } // add last key if ($key !== null) { $keys[] = $key; } return $keys; }