[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/Crypt/ -> smime.php (source)

   1  <?php
   2  
   3  require_once 'Horde/Crypt.php';
   4  
   5  /**
   6   * Horde_Crypt_smime:: provides a framework for Horde applications to
   7   * interact with the OpenSSL library and implement S/MIME.
   8   *
   9   * $Horde: framework/Crypt/Crypt/smime.php,v 1.49.2.12 2006/02/03 15:52:55 slusarz Exp $
  10   *
  11   * Copyright 2002-2006 Mike Cochrane <mike@graftonhall.co.nz>
  12   *
  13   * See the enclosed file COPYING for license information (LGPL). If you
  14   * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  15   *
  16   * @author  Mike Cochrane <mike@graftonhall.co.nz>
  17   * @since   Horde 3.0
  18   * @package Horde_Crypt
  19   */
  20  class Horde_Crypt_smime extends Horde_Crypt {
  21  
  22      /**
  23       * Object Identifers to name array.
  24       *
  25       * @var array
  26       */
  27      var $_oids = array(
  28          '2.5.4.3' => 'CommonName',
  29          '2.5.4.4' => 'Surname',
  30          '2.5.4.6' => 'Country',
  31          '2.5.4.7' => 'Location',
  32          '2.5.4.8' => 'StateOrProvince',
  33          '2.5.4.9' => 'StreetAddress',
  34          '2.5.4.10' => 'Organisation',
  35          '2.5.4.11' => 'OrganisationalUnit',
  36          '2.5.4.12' => 'Title',
  37          '2.5.4.20' => 'TelephoneNumber',
  38          '2.5.4.42' => 'GivenName',
  39  
  40          '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
  41  
  42          '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
  43          '2.5.29.15' => 'id-ce-keyUsage',
  44          '2.5.29.17' => 'id-ce-subjectAltName',
  45          '2.5.29.19' => 'id-ce-basicConstraints',
  46          '2.5.29.31' => 'id-ce-CRLDistributionPoints',
  47          '2.5.29.32' => 'id-ce-certificatePolicies',
  48          '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
  49          '2.5.29.37' => 'id-ce-extKeyUsage',
  50  
  51          '1.2.840.113549.1.9.1' => 'Email',
  52          '1.2.840.113549.1.1.1' => 'RSAEncryption',
  53          '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
  54          '1.2.840.113549.1.1.4' => 'md5withRSAEncryption',
  55          '1.2.840.113549.1.1.5' => 'SHA-1WithRSAEncryption',
  56          '1.2.840.10040.4.3' => 'id-dsa-with-sha-1',
  57  
  58          '1.3.6.1.5.5.7.3.2' => 'id_kp_clientAuth',
  59  
  60          '2.16.840.1.113730.1.1' => 'netscape-cert-type',
  61          '2.16.840.1.113730.1.2' => 'netscape-base-url',
  62          '2.16.840.1.113730.1.3' => 'netscape-revocation-url',
  63          '2.16.840.1.113730.1.4' => 'netscape-ca-revocation-url',
  64          '2.16.840.1.113730.1.7' => 'netscape-cert-renewal-url',
  65          '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
  66          '2.16.840.1.113730.1.12' => 'netscape-ssl-server-name',
  67          '2.16.840.1.113730.1.13' => 'netscape-comment',
  68      );
  69  
  70      /**
  71       * Constructor.
  72       *
  73       * @param array $params  Parameter array.
  74       *                       'temp' => Location of temporary directory.
  75       */
  76      function Horde_Crypt_smime($params)
  77      {
  78          $this->_tempdir = $params['temp'];
  79      }
  80  
  81      /**
  82       * Verify a passphrase for a given private key.
  83       *
  84       * @param string $private_key  The user's private key.
  85       * @param string $passphrase   The user's passphrase.
  86       *
  87       * @return boolean  Returns true on valid passphrase, false on invalid
  88       *                  passphrase.
  89       *                  Returns PEAR_Error on error.
  90       */
  91      function verifyPassphrase($private_key, $passphrase)
  92      {
  93          if (is_null($passphrase)) {
  94              $res = openssl_pkey_get_private($private_key);
  95          } else {
  96              $res = openssl_pkey_get_private($private_key, $passphrase);
  97          }
  98  
  99          return is_resource($res);
 100      }
 101  
 102      /**
 103       * Encrypt text using S/MIME.
 104       *
 105       * @param string $text   The text to be encrypted.
 106       * @param array $params  The parameters needed for encryption.
 107       *                       See the individual _encrypt*() functions for
 108       *                       the parameter requirements.
 109       *
 110       * @return string  The encrypted message.
 111       *                 Returns PEAR_Error object on error.
 112       */
 113      function encrypt($text, $params = array())
 114      {
 115          /* Check for availability of OpenSSL PHP extension. */
 116          $openssl = $this->checkForOpenSSL();
 117          if (is_a($openssl, 'PEAR_Error')) {
 118              return $openssl;
 119          }
 120  
 121          if (isset($params['type'])) {
 122              if ($params['type'] === 'message') {
 123                  return $this->_encryptMessage($text, $params);
 124              } elseif ($params['type'] === 'signature') {
 125                  return $this->_encryptSignature($text, $params);
 126              }
 127          }
 128      }
 129  
 130      /**
 131       * Decrypt text via S/MIME.
 132       *
 133       * @param string $text   The text to be smime decrypted.
 134       * @param array $params  The parameters needed for decryption.
 135       *                       See the individual _decrypt*() functions for
 136       *                       the parameter requirements.
 137       *
 138       * @return string  The decrypted message.
 139       *                 Returns PEAR_Error object on error.
 140       */
 141      function decrypt($text, $params = array())
 142      {
 143          /* Check for availability of OpenSSL PHP extension. */
 144          $openssl = $this->checkForOpenSSL();
 145          if (is_a($openssl, 'PEAR_Error')) {
 146              return $openssl;
 147          }
 148  
 149          if (isset($params['type'])) {
 150              if ($params['type'] === 'message') {
 151                  return $this->_decryptMessage($text, $params);
 152              } elseif (($params['type'] === 'signature') ||
 153                        ($params['type'] === 'detached-signature')) {
 154                  return $this->_decryptSignature($text, $params);
 155              }
 156          }
 157      }
 158  
 159      /**
 160       * Verify a signature using via S/MIME.
 161       *
 162       * @param string $text  The multipart/signed data to be verified.
 163       * @param mixed $certs  Either a single or array of root certificates.
 164       *
 165       * @return stdClass  Object with the following elements:
 166       *                   'result' -> Returns true on success;
 167       *                               PEAR_Error object on error.
 168       *                   'cert' -> The certificate of the signer stored
 169       *                             in the message (in PEM format).
 170       *                   'email' -> The email of the signing person.
 171       */
 172      function verify($text, $certs)
 173      {
 174          /* Check for availability of OpenSSL PHP extension. */
 175          $openssl = $this->checkForOpenSSL();
 176          if (is_a($openssl, 'PEAR_Error')) {
 177              return $openssl;
 178          }
 179  
 180          /* Create temp files for input/output. */
 181          $input = $this->_createTempFile('horde-smime');
 182          $output = $this->_createTempFile('horde-smime');
 183  
 184          /* Write text to file */
 185          $fp = fopen($input, 'w+');
 186          fwrite($fp, $text);
 187          fclose($fp);
 188  
 189          $root_certs = array();
 190          if (!is_array($certs)) {
 191              $certs = array($certs);
 192          }
 193          foreach ($certs as $file) {
 194              if (file_exists($file)) {
 195                  $root_certs[] = $file;
 196              }
 197          }
 198  
 199          $ob = &new stdClass;
 200  
 201          if (!empty($root_certs)) {
 202              $result = openssl_pkcs7_verify($input, 0, $output, $root_certs);
 203              /* Message verified */
 204              if ($result === true) {
 205                  $ob->result = true;
 206                  $ob->cert = file_get_contents($output);
 207                  $ob->email = $this->getEmailFromKey($ob->cert);
 208                  return $ob;
 209              }
 210          }
 211  
 212          /* Try again without verfying the signer's cert */
 213          $result = openssl_pkcs7_verify($input, PKCS7_NOVERIFY, $output);
 214  
 215          if (($result === true) || ($result === -1)) {
 216              $ob->result = PEAR::raiseError(_("Message Verified Successfully but the signer's certificate could not be verified."), 'horde.warning');
 217          } else {
 218              $ob->result = PEAR::raiseError(_("Verification failed - this message may have been tampered with."), 'horde.error');
 219          }
 220  
 221          $ob->cert = file_get_contents($output);
 222          $ob->email = $this->getEmailFromKey($ob->cert);
 223  
 224          return $ob;
 225      }
 226  
 227      /**
 228       * Extract the contents from signed S/MIME data.
 229       *
 230       * @param string $data     The signed S/MIME data.
 231       * @param string $sslpath  The path to the OpenSSL binary.
 232       *
 233       * @return string  The contents embedded in the signed data.
 234       *                 Returns PEAR_Error on error.
 235       */
 236      function extractSignedContents($data, $sslpath)
 237      {
 238          $pipes_desc = array(
 239              0 => array('pipe', 'r'),
 240              1 => array('pipe', 'w')
 241          );
 242  
 243          $fp = proc_open($sslpath . ' smime -verify -noverify -nochain', $pipes_desc, $pipes);
 244          if (!is_resource($fp)) {
 245              return PEAR::raiseError(_("OpenSSL error: Could not extract data from signed S/MIME part."), 'horde.error');
 246          }
 247  
 248          $output = '';
 249  
 250          /* $pipes[0] => writeable handle connected to child stdin
 251             $pipes[1] => readable handle connected to child stdout */
 252          fwrite($pipes[0], $data);
 253          fclose($pipes[0]);
 254  
 255          while (!feof($pipes[1])) {
 256              $output .= fgets($pipes[1], 1024);
 257          }
 258          fclose($pipes[1]);
 259          proc_close($fp);
 260  
 261          return $output;
 262      }
 263  
 264      /**
 265       * Sign a MIME_Part using S/MIME.
 266       *
 267       * @param MIME_Part $mime_part  The MIME_Part object to sign.
 268       * @param array $params         The parameters required for signing.
 269       *
 270       * @return MIME_Part  A MIME_Part object that is signed.
 271       *                    Returns PEAR_Error object on error.
 272       */
 273      function signMIMEPart($mime_part, $params)
 274      {
 275          require_once 'Horde/MIME/Part.php';
 276          require_once 'Horde/MIME/Structure.php';
 277  
 278          /* Sign the part as a message */
 279          $message = $this->encrypt($mime_part->toCanonicalString(), $params);
 280  
 281          /* Break the result into its components */
 282          $mime_message = MIME_Structure::parseTextMIMEMessage($message);
 283  
 284          $smime_sign = $mime_message->getPart(2);
 285          $smime_sign->setDescription(_("S/MIME Cryptographic Signature"));
 286          $smime_sign->transferDecodeContents();
 287          $smime_sign->setTransferEncoding('base64');
 288  
 289          $smime_part = &new MIME_Part('multipart/signed');
 290          $smime_part->setContents('This is a cryptographically signed message in MIME format.' . "\n");
 291          $smime_part->addPart($mime_part);
 292          $smime_part->addPart($smime_sign);
 293          $smime_part->setContentTypeParameter('protocol', 'application/pkcs7-signature');
 294          $smime_part->setContentTypeParameter('micalg', 'sha1');
 295  
 296          return $smime_part;
 297      }
 298  
 299      /**
 300       * Encrypt a MIME_Part using S/MIME.
 301       *
 302       * @param MIME_Part $mime_part  The MIME_Part object to encrypt.
 303       * @param array $params         The parameters required for encryption.
 304       *
 305       * @return MIME_Part  A MIME_Part object that is encrypted.
 306       *                    Returns PEAR_Error on error.
 307       */
 308      function encryptMIMEPart($mime_part, $params = array())
 309      {
 310          require_once 'Horde/MIME/Part.php';
 311          require_once 'Horde/MIME/Structure.php';
 312  
 313          /* Sign the part as a message */
 314          $message = $this->encrypt($mime_part->toCanonicalString(), $params);
 315          if (is_a($message, 'PEAR_Error')) {
 316              return $message;
 317          }
 318  
 319          /* Break the result into its components */
 320          $mime_message = MIME_Structure::parseTextMIMEMessage($message);
 321  
 322          $smime_part = $mime_message->getBasePart();
 323          $smime_part->setDescription(_("S/MIME Encrypted Message"));
 324          $smime_part->transferDecodeContents();
 325          $smime_part->setTransferEncoding('base64');
 326          $smime_part->setDisposition('inline');
 327  
 328          /* By default, encrypt() produces a message with type
 329           * 'application/x-pkcs7-mime' and no 'smime-type' parameter. Per
 330           * RFC 2311, the more correct MIME type is 'application/pkcs7-mime'
 331           * and the smime-type should be 'enveloped-data'. */
 332          $smime_part->setType('application/pkcs7-mime');
 333          $smime_part->setContentTypeParameter('smime-type', 'enveloped-data');
 334  
 335          return $smime_part;
 336      }
 337  
 338      /**
 339       * Encrypt a message in S/MIME format using a public key.
 340       *
 341       * @access private
 342       *
 343       * @param string $text   The text to be encrypted.
 344       * @param array $params  The parameters needed for encryption.
 345       * <pre>
 346       * Parameters:
 347       * ===========
 348       * 'type'    =>  'message' (REQUIRED)
 349       * 'pubkey'  =>  public key. (REQUIRED)
 350       * 'email'   =>  E-mail address of recipient. If not present, or not found
 351       *               in the public key, the first e-mail address found in the
 352       *               key will be used instead. (Optional)
 353       * </pre>
 354       *
 355       * @return string  The encrypted message.
 356       *                 Return PEAR_Error object on error.
 357       */
 358      function _encryptMessage($text, $params)
 359      {
 360          $email = null;
 361  
 362          /* Check for required parameters. */
 363          if (!isset($params['pubkey'])) {
 364              return PEAR::raiseError(_("A public SMIME key is required to encrypt a message."), 'horde.error');
 365          }
 366  
 367          /* Create temp files for input/output. */
 368          $input = $this->_createTempFile('horde-smime');
 369          $output = $this->_createTempFile('horde-smime');
 370  
 371          /* Store message in file. */
 372          $fp1 = fopen($input, 'w+');
 373          fputs($fp1, $text);
 374          fclose($fp1);
 375  
 376          if (isset($params['email'])) {
 377              $email = $params['email'];
 378          } else {
 379              $email = $this->getEmailFromKey($params['pubkey']);
 380              if (is_null($email)) {
 381                  return PEAR::raiseError(_("Could not determine the recipient's e-mail address."), 'horde.error');
 382              }
 383          }
 384  
 385          /* Encrypt the document. */
 386          if (openssl_pkcs7_encrypt($input, $output, $params['pubkey'], array('To' => $email))) {
 387              $result = file_get_contents($output);
 388              if (!empty($result)) {
 389                  return $this->_fixContentType($result, 'encrypt');
 390              }
 391          }
 392  
 393          return PEAR::raiseError(_("Could not S/MIME encrypt message."), 'horde.error');
 394      }
 395  
 396      /**
 397       * Sign a message in S/MIME format using a private key.
 398       *
 399       * @access private
 400       *
 401       * @param string $text   The text to be signed.
 402       * @param array $params  The parameters needed for signing.
 403       * <pre>
 404       * Parameters:
 405       * ===========
 406       * 'certs'       =>  Additional signing certs (Optional)
 407       * 'passphrase'  =>  Passphrase for key (REQUIRED)
 408       * 'privkey'     =>  Private key (REQUIRED)
 409       * 'pubkey'      =>  Public key (REQUIRED)
 410       * 'sigtype'     =>  Determine the signature type to use. (Optional)
 411       *                   'cleartext'  --  Make a clear text signature
 412       *                   'detach'     --  Make a detached signature (DEFAULT)
 413       * 'type'        =>  'signature' (REQUIRED)
 414       * </pre>
 415       *
 416       * @return string  The signed message.
 417       *                 Return PEAR_Error object on error.
 418       */
 419      function _encryptSignature($text, $params)
 420      {
 421          /* Check for secure connection. */
 422          $secure_check = $this->requireSecureConnection();
 423          if (is_a($secure_check, 'PEAR_Error')) {
 424              return $secure_check;
 425          }
 426  
 427          /* Check for required parameters. */
 428          if (!isset($params['pubkey']) ||
 429              !isset($params['privkey']) ||
 430              !array_key_exists('passphrase', $params)) {
 431              return PEAR::raiseError(_("A public S/MIME key, private S/MIME key, and passphrase are required to sign a message."), 'horde.error');
 432          }
 433  
 434          /* Create temp files for input/output/certificates. */
 435          $input = $this->_createTempFile('horde-smime');
 436          $output = $this->_createTempFile('horde-smime');
 437          $certs = $this->_createTempFile('horde-smime');
 438  
 439          /* Store message in temporary file. */
 440          $fp = fopen($input, 'w+');
 441          fputs($fp, $text);
 442          fclose($fp);
 443  
 444          /* Store additional certs in temporary file. */
 445          if (!empty($params['certs'])) {
 446              $fp = fopen($certs, 'w+');
 447              fputs($fp, $params['certs']);
 448              fclose($fp);
 449          }
 450  
 451          /* Determine the signature type to use. */
 452          if (isset($params['sigtype']) && ($params['sigtype'] == 'cleartext')) {
 453              $flags = PKCS7_TEXT;
 454          } else {
 455              $flags = PKCS7_DETACHED;
 456          }
 457  
 458          $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
 459  
 460          if (empty($params['certs'])) {
 461              $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags);
 462          } else {
 463              $res = openssl_pkcs7_sign($input, $output, $params['pubkey'], $privkey, array(), $flags, $certs);
 464          }
 465  
 466          if (!$res) {
 467              return PEAR::raiseError(_("Could not S/MIME sign message."), 'horde.error');
 468          }
 469  
 470          $data = file_get_contents($output);
 471          return $this->_fixContentType($data, 'signature');
 472      }
 473  
 474      /**
 475       * Decrypt an S/MIME encrypted message using a private/public keypair
 476       * and a passhprase.
 477       *
 478       * @access private
 479       *
 480       * @param string $text   The text to be decrypted.
 481       * @param array $params  The parameters needed for decryption.
 482       * <pre>
 483       * Parameters:
 484       * ===========
 485       * 'type'        =>  'message' (REQUIRED)
 486       * 'pubkey'      =>  public key. (REQUIRED)
 487       * 'privkey'     =>  private key. (REQUIRED)
 488       * 'passphrase'  =>  Passphrase for Key. (REQUIRED)
 489       * </pre>
 490       *
 491       * @return string  The decrypted message.
 492       *                 Returns PEAR_Error object on error.
 493       */
 494      function _decryptMessage($text, $params)
 495      {
 496          /* Check for secure connection. */
 497          $secure_check = $this->requireSecureConnection();
 498          if (is_a($secure_check, 'PEAR_Error')) {
 499              return $secure_check;
 500          }
 501  
 502          /* Check for required parameters. */
 503          if (!isset($params['pubkey']) ||
 504              !isset($params['privkey']) ||
 505              !array_key_exists('passphrase', $params)) {
 506              return PEAR::raiseError(_("A public S/MIME key, private S/MIME key, and passphrase are required to decrypt a message."), 'horde.error');
 507          }
 508  
 509          /* Create temp files for input/output. */
 510          $input = $this->_createTempFile('horde-smime');
 511          $output = $this->_createTempFile('horde-smime');
 512  
 513          /* Store message in file. */
 514          $fp = fopen($input, 'w+');
 515          fputs($fp, trim($text));
 516          fclose($fp);
 517  
 518          $privkey = (is_null($params['passphrase'])) ? $params['privkey'] : array($params['privkey'], $params['passphrase']);
 519          if (openssl_pkcs7_decrypt($input, $output, $params['pubkey'], $privkey)) {
 520              return file_get_contents($output);
 521          }
 522  
 523          return PEAR::raiseError(_("Could not decrypt S/MIME data."), 'horde.error');
 524      }
 525  
 526      /**
 527       * Sign and Encrypt a MIME_Part using S/MIME.
 528       *
 529       * @param MIME_Part $mime_part   The MIME_Part object to sign and encrypt.
 530       * @param array $sign_params     The parameters required for signing. See
 531       *                               _encryptSignature().
 532       * @param array $encrypt_params  The parameters required for encryption.
 533       *                               See _encryptMessage().
 534       *
 535       * @return MIME_Part  A MIME_Part object that is signed and encrypted.
 536       *                    Returns PEAR_Error on error.
 537       */
 538      function signAndEncryptMIMEPart($mime_part, $sign_params = array(),
 539                                      $encrypt_params = array())
 540      {
 541          include_once 'Horde/MIME/Part.php';
 542  
 543          $part = $this->signMIMEPart($mime_part, $sign_params);
 544          if (is_a($part, 'PEAR_Error')) {
 545              return $part;
 546          }
 547          $part = $this->encryptMIMEPart($part, $encrypt_params);
 548          if (is_a($part, 'PEAR_Error')) {
 549              return $part;
 550          }
 551  
 552          return $part;
 553      }
 554  
 555      /**
 556       * Convert a PEM format certificate to readable HTML version
 557       *
 558       * @param string $cert   PEM format certificate
 559       *
 560       * @return string  HTML detailing the certificate.
 561       */
 562      function certToHTML($cert)
 563      {
 564          /* Common Fields */
 565          $fieldnames = array(
 566              'Email' => _("Email Address"),
 567              'CommonName' => _("Common Name"),
 568              'Organisation' => _("Organisation"),
 569              'OrganisationalUnit' => _("Organisational Unit"),
 570              'Country' => _("Country"),
 571              'StateOrProvince' => _("State or Province"),
 572              'Location' => _("Location"),
 573              'StreetAddress' => _("Street Address"),
 574              'TelephoneNumber' => _("Telephone Number"),
 575              'Surname' => _("Surname"),
 576              'GivenName' => _("Given Name")
 577          );
 578  
 579          /* Netscape Extensions */
 580          $fieldnames += array(
 581              'netscape-cert-type' => _("Netscape certificate type"),
 582              'netscape-base-url' => _("Netscape Base URL"),
 583              'netscape-revocation-url' => _("Netscape Revocation URL"),
 584              'netscape-ca-revocation-url' => _("Netscape CA Revocation URL"),
 585              'netscape-cert-renewal-url' => _("Netscape Renewal URL"),
 586              'netscape-ca-policy-url' => _("Netscape CA policy URL"),
 587              'netscape-ssl-server-name' => _("Netscape SSL server name"),
 588              'netscape-comment' => _("Netscape certificate comment")
 589          );
 590  
 591          /* X590v3 Extensions */
 592          $fieldnames += array(
 593              'id-ce-extKeyUsage' => _("X509v3 Extended Key Usage"),
 594              'id-ce-basicConstraints' => _("X509v3 Basic Constraints"),
 595              'id-ce-subjectAltName' => _("X509v3 Subject Alternative Name"),
 596              'id-ce-subjectKeyIdentifier' => _("X509v3 Subject Key Identifier"),
 597              'id-ce-certificatePolicies' => _("Certificate Policies"),
 598              'id-ce-CRLDistributionPoints' => _("CRL Distribution Points"),
 599              'id-ce-keyUsage' => _("Key Usage")
 600          );
 601  
 602          $cert_details = $this->parseCert($cert);
 603          if (!is_array($cert_details)) {
 604              return '<pre class="fixed">' . _("Unable to extract certificate details") . '</pre>';
 605          }
 606          $certificate = $cert_details['certificate'];
 607  
 608          $text = '<pre class="fixed">';
 609  
 610          /* Subject (a/k/a Certificate Owner) */
 611          if (isset($certificate['subject'])) {
 612              $text .= "<strong>" . _("Certificate Owner") . ":</strong>\n";
 613  
 614              foreach ($certificate['subject'] as $key => $value) {
 615                  if (isset($fieldnames[$key])) {
 616                      $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
 617                  } else {
 618                      $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
 619                  }
 620              }
 621              $text .= "\n";
 622          }
 623  
 624          /* Issuer */
 625          if (isset($certificate['issuer'])) {
 626              $text .= "<strong>" . _("Issuer") . ":</strong>\n";
 627  
 628              foreach ($certificate['issuer'] as $key => $value) {
 629                  if (isset($fieldnames[$key])) {
 630                      $text .= sprintf("&nbsp;&nbsp;%s: %s\n", $fieldnames[$key], $value);
 631                  } else {
 632                      $text .= sprintf("&nbsp;&nbsp;*%s: %s\n", $key, $value);
 633                  }
 634              }
 635              $text .= "\n";
 636          }
 637  
 638          /* Dates  */
 639          $text .= "<strong>" . _("Validity") . ":</strong>\n";
 640          $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not Before"), strftime("%x %X", $certificate['validity']['notbefore']));
 641          $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Not After"), strftime("%x %X", $certificate['validity']['notafter']));
 642          $text .= "\n";
 643  
 644          /* Certificate Owner - Public Key Info */
 645          $text .= "<strong>" . _("Public Key Info") . ":</strong>\n";
 646          $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Public Key Algorithm"), $certificate['subjectPublicKeyInfo']['algorithm']);
 647          if ($certificate['subjectPublicKeyInfo']['algorithm'] == 'rsaEncryption') {
 648              if (Util::extensionExists('bcmath')) {
 649                  $modulus = $certificate['subjectPublicKeyInfo']['subjectPublicKey']['modulus'];
 650                  $modulus_hex = '';
 651                  while ($modulus != '0') {
 652                      $modulus_hex = dechex(bcmod($modulus, '16')) . $modulus_hex;
 653                      $modulus = bcdiv($modulus, '16', 0);
 654                  }
 655  
 656                  if ((strlen($modulus_hex) > 64) &&
 657                      (strlen($modulus_hex) < 128)) {
 658                      str_pad($modulus_hex, 128, '0', STR_PAD_RIGHT);
 659                  } elseif ((strlen($modulus_hex) > 128) &&
 660                            (strlen($modulus_hex) < 256)) {
 661                      str_pad($modulus_hex, 256, '0', STR_PAD_RIGHT);
 662                  }
 663  
 664                  $text .= "&nbsp;&nbsp;" . sprintf(_("RSA Public Key (%d bit)"), strlen($modulus_hex) * 4) . ":\n";
 665  
 666                  $modulus_str = '';
 667                  for ($i = 0; $i < strlen($modulus_hex); $i += 2) {
 668                      if (($i % 32) == 0) {
 669                          $modulus_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
 670                      }
 671                      $modulus_str .= substr($modulus_hex, $i, 2) . ':';
 672                  }
 673  
 674                  $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Modulus"), $modulus_str);
 675              }
 676  
 677              $text .= sprintf("&nbsp;&nbsp;&nbsp;&nbsp;%s: %s\n", _("Exponent"), $certificate['subjectPublicKeyInfo']['subjectPublicKey']['publicExponent']);
 678          }
 679          $text .= "\n";
 680  
 681          /* X509v3 extensions */
 682          if (isset($certificate['extensions'])) {
 683              $text .= "<strong>" . _("X509v3 extensions") . ":</strong>\n";
 684  
 685              foreach ($certificate['extensions'] as $key => $value) {
 686                  if (is_array($value)) {
 687                      $value = _("Unsupported Extension");
 688                  }
 689                  if (isset($fieldnames[$key])) {
 690                      $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $fieldnames[$key], wordwrap($value, 40, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
 691                  } else {
 692                      $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $key, wordwrap($value, 60, "\n&nbsp;&nbsp;&nbsp;&nbsp;"));
 693                  }
 694              }
 695  
 696              $text .= "\n";
 697          }
 698  
 699          /* Certificate Details */
 700          $text .= "<strong>" . _("Certificate Details") . ":</strong>\n";
 701          $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Version"), $certificate['version']);
 702          $text .= sprintf("&nbsp;&nbsp;%s: %d\n", _("Serial Number"), $certificate['serialNumber']);
 703  
 704          foreach ($cert_details['fingerprints'] as $hash => $fingerprint) {
 705              $label = sprintf(_("%s Fingerprint"), String::upper($hash));
 706              $fingerprint_str = '';
 707              for ($i = 0; $i < strlen($fingerprint); $i += 2) {
 708                  $fingerprint_str .= substr($fingerprint, $i, 2) . ':';
 709              }
 710              $text .= sprintf("&nbsp;&nbsp;%s:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s\n", $label, $fingerprint_str);
 711          }
 712          $text .= sprintf("&nbsp;&nbsp;%s: %s\n", _("Signature Algorithm"), $cert_details['signatureAlgorithm']);
 713          $text .= sprintf("&nbsp;&nbsp;%s:", _("Signature"));
 714  
 715          $sig_str = '';
 716          for ($i = 0; $i < strlen($cert_details['signature']); $i++) {
 717              if (($i % 16) == 0) {
 718                  $sig_str .= "\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
 719              }
 720              $sig_str .= sprintf("%02x:", ord($cert_details['signature'][$i]));
 721          }
 722  
 723          return $text . $sig_str . "\n</pre>";
 724      }
 725  
 726      /**
 727       * Extract the contents of a PEM format certificate to an array.
 728       *
 729       * @param string $cert  PEM format certificate
 730       *
 731       * @return array  Array containing all extractable information about
 732       *                the certificate.
 733       */
 734      function parseCert($cert)
 735      {
 736          $cert_split = preg_split('/(-----((BEGIN)|(END)) CERTIFICATE-----)/', $cert);
 737          if (!isset($cert_split[1])) {
 738              $raw_cert = base64_decode($cert);
 739          } else {
 740              $raw_cert = base64_decode($cert_split[1]);
 741          }
 742  
 743          $cert_data = $this->_parseASN($raw_cert);
 744          if (!is_array($cert_data) || ($cert_data[0] == 'UNKNOWN')) {
 745              return false;
 746          }
 747  
 748          $cert_details = array();
 749          $cert_details['fingerprints']['md5'] = md5($raw_cert);
 750          if (Util::extensionExists('mhash')) {
 751              $cert_details['fingerprints']['sha1'] = bin2hex(mhash(MHASH_SHA1, $raw_cert));
 752          }
 753  
 754          $cert_details['certificate']['extensions'] = array();
 755          $cert_details['certificate']['version'] = $cert_data[1][0][1][0][1] + 1;
 756          $cert_details['certificate']['serialNumber'] = $cert_data[1][0][1][1][1];
 757          $cert_details['certificate']['signature'] = $cert_data[1][0][1][2][1][0][1];
 758          $cert_details['certificate']['issuer'] = $cert_data[1][0][1][3][1];
 759          $cert_details['certificate']['validity'] = $cert_data[1][0][1][4][1];
 760          $cert_details['certificate']['subject'] = @$cert_data[1][0][1][5][1];
 761          $cert_details['certificate']['subjectPublicKeyInfo'] = $cert_data[1][0][1][6][1];
 762  
 763          $cert_details['signatureAlgorithm'] = $cert_data[1][1][1][0][1];
 764          $cert_details['signature'] = $cert_data[1][2][1];
 765  
 766          // issuer
 767          $issuer = array();
 768          foreach ($cert_details['certificate']['issuer'] as $value) {
 769              $issuer[$value[1][1][0][1]] = $value[1][1][1][1];
 770          }
 771          $cert_details['certificate']['issuer'] = $issuer;
 772  
 773          // subject
 774          $subject = array();
 775          foreach ($cert_details['certificate']['subject'] as $value) {
 776              $subject[$value[1][1][0][1]] = $value[1][1][1][1];
 777          }
 778          $cert_details['certificate']['subject'] = $subject;
 779  
 780          // validity
 781          $vals = $cert_details['certificate']['validity'];
 782          $cert_details['certificate']['validity'] = array();
 783          $cert_details['certificate']['validity']['notbefore'] = $vals[0][1];
 784          $cert_details['certificate']['validity']['notafter'] = $vals[1][1];
 785          foreach ($cert_details['certificate']['validity'] as $key => $val) {
 786              $year = substr($val, 0, 2);
 787              $month = substr($val, 2, 2);
 788              $day = substr($val, 4, 2);
 789              $hour = substr($val, 6, 2);
 790              $minute = substr($val, 8, 2);
 791              if (($val[11] == '-') || ($val[9] == '+')) {
 792                  // handle time zone offset here
 793                  $seconds = 0;
 794              } elseif (String::upper($val[11]) == 'Z') {
 795                  $seconds = 0;
 796              } else {
 797                  $seconds = substr($val, 10, 2);
 798                  if (($val[11] == '-') || ($val[9] == '+')) {
 799                      // handle time zone offset here
 800                  }
 801              }
 802              $cert_details['certificate']['validity'][$key] = mktime ($hour, $minute, $seconds, $month, $day, $year);
 803          }
 804  
 805          // Split the Public Key into components.
 806          $subjectPublicKeyInfo = array();
 807          $subjectPublicKeyInfo['algorithm'] = $cert_details['certificate']['subjectPublicKeyInfo'][0][1][0][1];
 808          if ($subjectPublicKeyInfo['algorithm'] == 'rsaEncryption') {
 809              $subjectPublicKey = $this->_parseASN($cert_details['certificate']['subjectPublicKeyInfo'][1][1]);
 810              $subjectPublicKeyInfo['subjectPublicKey']['modulus'] = $subjectPublicKey[1][0][1];
 811              $subjectPublicKeyInfo['subjectPublicKey']['publicExponent'] = $subjectPublicKey[1][1][1];
 812          }
 813          $cert_details['certificate']['subjectPublicKeyInfo'] = $subjectPublicKeyInfo;
 814  
 815          if (isset($cert_data[1][0][1][7]) &&
 816              is_array($cert_data[1][0][1][7][1])) {
 817              foreach ($cert_data[1][0][1][7][1] as $ext) {
 818                  $oid = $ext[1][0][1];
 819                  $cert_details['certificate']['extensions'][$oid] = $ext[1][1];
 820              }
 821          }
 822  
 823          $i = 9;
 824          while (isset($cert_data[1][0][1][$i])) {
 825              $oid = $cert_data[1][0][1][$i][1][0][1];
 826              $cert_details['certificate']['extensions'][$oid] = $cert_data[1][0][1][$i][1][1];
 827              $i++;
 828          }
 829  
 830          foreach ($cert_details['certificate']['extensions'] as $oid => $val) {
 831              switch ($oid) {
 832              case 'netscape-base-url':
 833              case 'netscape-revocation-url':
 834              case 'netscape-ca-revocation-url':
 835              case 'netscape-cert-renewal-url':
 836              case 'netscape-ca-policy-url':
 837              case 'netscape-ssl-server-name':
 838              case 'netscape-comment':
 839                  $val = $this->_parseASN($val[1]);
 840                  $cert_details['certificate']['extensions'][$oid] = $val[1];
 841                  break;
 842  
 843              case 'id-ce-subjectAltName':
 844                  $val = $this->_parseASN($val[1]);
 845                  $cert_details['certificate']['extensions'][$oid] = '';
 846                  foreach ($val[1] as $name) {
 847                      if (!empty($cert_details['certificate']['extensions'][$oid])) {
 848                          $cert_details['certificate']['extensions'][$oid] .= ', ';
 849                      }
 850                      $cert_details['certificate']['extensions'][$oid] .= $name[1];
 851                  }
 852                  break;
 853  
 854              case 'netscape-cert-type':
 855                  $val = $this->_parseASN($val[1]);
 856                  $val = ord($val[1]);
 857                  $newVal = '';
 858  
 859                  if ($val & 0x80) {
 860                      $newVal .= empty($newVal) ? 'SSL client' : ', SSL client';
 861                  }
 862                  if ($val & 0x40) {
 863                      $newVal .= empty($newVal) ? 'SSL server' : ', SSL server';
 864                  }
 865                  if ($val & 0x20) {
 866                      $newVal .= empty($newVal) ? 'S/MIME' : ', S/MIME';
 867                  }
 868                  if ($val & 0x10) {
 869                      $newVal .= empty($newVal) ? 'Object Signing' : ', Object Signing';
 870                  }
 871                  if ($val & 0x04) {
 872                      $newVal .= empty($newVal) ? 'SSL CA' : ', SSL CA';
 873                  }
 874                  if ($val & 0x02) {
 875                      $newVal .= empty($newVal) ? 'S/MIME CA' : ', S/MIME CA';
 876                  }
 877                  if ($val & 0x01) {
 878                      $newVal .= empty($newVal) ? 'Object Signing CA' : ', Object Signing CA';
 879                  }
 880  
 881                  $cert_details['certificate']['extensions'][$oid] = $newVal;
 882                  break;
 883  
 884              case 'id-ce-extKeyUsage':
 885                  $val = $this->_parseASN($val[1]);
 886                  $val = $val[1];
 887  
 888                  $newVal = '';
 889                  if ($val[0][1] != 'sequence') {
 890                      $val = array($val);
 891                  } else {
 892                      $val = $val[1][1];
 893                  }
 894                  foreach ($val as $usage) {
 895                      if ($usage[1] == 'id_kp_clientAuth') {
 896                          $newVal .= empty($newVal) ? 'TLS Web Client Authentication' : ', TLS Web Client Authentication';
 897                      } else {
 898                          $newVal .= empty($newVal) ? $usage[1] : ', ' . $usage[1];
 899                      }
 900                  }
 901                  $cert_details['certificate']['extensions'][$oid] = $newVal;
 902                  break;
 903  
 904              case 'id-ce-subjectKeyIdentifier':
 905                  $val = $this->_parseASN($val[1]);
 906                  $val = $val[1];
 907  
 908                  $newVal = '';
 909  
 910                  for ($i = 0; $i < strlen($val); $i++) {
 911                      $newVal .= sprintf("%02x:", ord($val[$i]));
 912                  }
 913                  $cert_details['certificate']['extensions'][$oid] = $newVal;
 914                  break;
 915  
 916              case 'id-ce-authorityKeyIdentifier':
 917                  $val = $this->_parseASN($val[1]);
 918                  if ($val[0] == 'string') {
 919                      $val = $val[1];
 920  
 921                      $newVal = '';
 922                      for ($i = 0; $i < strlen($val); $i++) {
 923                          $newVal .= sprintf("%02x:", ord($val[$i]));
 924                      }
 925                      $cert_details['certificate']['extensions'][$oid] = $newVal;
 926                  } else {
 927                      $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
 928                  }
 929                  break;
 930  
 931              case 'id-ce-basicConstraints':
 932              case 'default':
 933                  $cert_details['certificate']['extensions'][$oid] = _("Unsupported Extension");
 934                  break;
 935              }
 936          }
 937  
 938          return $cert_details;
 939      }
 940  
 941      /**
 942       * Attempt to parse ASN.1 formated data.
 943       *
 944       * @access private
 945       *
 946       * @param string $data  ASN.1 formated data
 947       *
 948       * @return array  Array contained the extracted values.
 949       */
 950      function _parseASN($data)
 951      {
 952          $result = array();
 953  
 954          while (strlen($data) > 1) {
 955              $class = ord($data[0]);
 956              switch ($class) {
 957              case 0x30:
 958                  // Sequence
 959                  $len = ord($data[1]);
 960                  $bytes = 0;
 961                  if ($len & 0x80) {
 962                      $bytes = $len & 0x0f;
 963                      $len = 0;
 964                      for ($i = 0; $i < $bytes; $i++) {
 965                          $len = ($len << 8) | ord($data[$i + 2]);
 966                      }
 967                  }
 968                  $sequence_data = substr($data, 2 + $bytes, $len);
 969                  $data = substr($data, 2 + $bytes + $len);
 970  
 971                  $values = $this->_parseASN($sequence_data);
 972                  if (!is_array($values) || is_string($values[0])) {
 973                      $values = array($values);
 974                  }
 975                  $sequence_values = array();
 976                  $i = 0;
 977                  foreach ($values as $val) {
 978                      if ($val[0] == 'extension') {
 979                          $sequence_values['extensions'][] = $val;
 980                      } else {
 981                          $sequence_values[$i++] = $val;
 982                      }
 983                  }
 984                  $result[] = array('sequence', $sequence_values);
 985                  break;
 986  
 987              case 0x31:
 988                  // Set of
 989                  $len = ord($data[1]);
 990                  $bytes = 0;
 991                  if ($len & 0x80) {
 992                      $bytes = $len & 0x0f;
 993                      $len = 0;
 994                      for ($i = 0; $i < $bytes; $i++) {
 995                          $len = ($len << 8) | ord($data[$i + 2]);
 996                      }
 997                  }
 998                  $sequence_data = substr($data, 2 + $bytes, $len);
 999                  $data = substr($data, 2 + $bytes + $len);
1000                  $result[] = array('set', $this->_parseASN($sequence_data));
1001                  break;
1002  
1003              case 0x01:
1004                  // Boolean type
1005                  $boolean_value = (ord($data[2]) == 0xff);
1006                  $data = substr($data, 3);
1007                  $result[] = array('boolean', $boolean_value);
1008                  break;
1009  
1010              case 0x02:
1011                  // Integer type
1012                  $len = ord($data[1]);
1013                  $bytes = 0;
1014                  if ($len & 0x80) {
1015                      $bytes = $len & 0x0f;
1016                      $len = 0;
1017                      for ($i = 0; $i < $bytes; $i++) {
1018                          $len = ($len << 8) | ord($data[$i + 2]);
1019                      }
1020                  }
1021  
1022                  $integer_data = substr($data, 2 + $bytes, $len);
1023                  $data = substr($data, 2 + $bytes + $len);
1024  
1025                  $value = 0;
1026                  if ($len <= 4) {
1027                      /* Method works fine for small integers */
1028                      for ($i = 0; $i < strlen($integer_data); $i++) {
1029                          $value = ($value << 8) | ord($integer_data[$i]);
1030                      }
1031                  } else {
1032                      /* Method works for arbitrary length integers */
1033                      if (Util::extensionExists('bcmath')) {
1034                          for ($i = 0; $i < strlen($integer_data); $i++) {
1035                              $value = bcadd(bcmul($value, 256), ord($integer_data[$i]));
1036                          }
1037                      } else {
1038                          $value = -1;
1039                      }
1040                  }
1041                  $result[] = array('integer(' . $len . ')', $value);
1042                  break;
1043  
1044              case 0x03:
1045                  // Bitstring type
1046                  $len = ord($data[1]);
1047                  $bytes = 0;
1048                  if ($len & 0x80) {
1049                      $bytes = $len & 0x0f;
1050                      $len = 0;
1051                      for ($i = 0; $i < $bytes; $i++) {
1052                          $len = ($len << 8) | ord($data[$i + 2]);
1053                      }
1054                  }
1055                  $bitstring_data = substr($data, 3 + $bytes, $len);
1056                  $data = substr($data, 2 + $bytes + $len);
1057                  $result[] = array('bit string', $bitstring_data);
1058                  break;
1059  
1060              case 0x04:
1061                  // Octetstring type
1062                  $len = ord($data[1]);
1063                  $bytes = 0;
1064                  if ($len & 0x80) {
1065                      $bytes = $len & 0x0f;
1066                      $len = 0;
1067                      for ($i = 0; $i < $bytes; $i++) {
1068                          $len = ($len << 8) | ord($data[$i + 2]);
1069                      }
1070                  }
1071                  $octectstring_data = substr($data, 2 + $bytes, $len);
1072                  $data = substr($data, 2 + $bytes + $len);
1073                  $result[] = array('octet string', $octectstring_data);
1074                  break;
1075  
1076              case 0x05:
1077                  // Null type
1078                  $data = substr($data, 2);
1079                  $result[] = array('null', null);
1080                  break;
1081  
1082              case 0x06:
1083                  // Object identifier type
1084                  $len = ord($data[1]);
1085                  $bytes = 0;
1086                  if ($len & 0x80) {
1087                      $bytes = $len & 0x0f;
1088                      $len = 0;
1089                      for ($i = 0; $i < $bytes; $i++) {
1090                          $len = ($len << 8) | ord($data[$i + 2]);
1091                      }
1092                  }
1093                  $oid_data = substr($data, 2 + $bytes, $len);
1094                  $data = substr($data, 2 + $bytes + $len);
1095  
1096                  // Unpack the OID
1097                  $plain  = floor(ord($oid_data[0]) / 40);
1098                  $plain .= '.' . ord($oid_data[0]) % 40;
1099  
1100                  $value = 0;
1101                  $i = 1;
1102                  while ($i < strlen($oid_data)) {
1103                      $value = $value << 7;
1104                      $value = $value | (ord($oid_data[$i]) & 0x7f);
1105  
1106                      if (!(ord($oid_data[$i]) & 0x80)) {
1107                          $plain .= '.' . $value;
1108                          $value = 0;
1109                      }
1110                      $i++;
1111                  }
1112  
1113                  if (isset($this->_oids[$plain])) {
1114                      $result[] = array('oid', $this->_oids[$plain]);
1115                  } else {
1116                      $result[] = array('oid', $plain);
1117                  }
1118  
1119                  break;
1120  
1121              case 0x12:
1122              case 0x13:
1123              case 0x14:
1124              case 0x15:
1125              case 0x16:
1126              case 0x81:
1127              case 0x80:
1128                  // Character string type
1129                  $len = ord($data[1]);
1130                  $bytes = 0;
1131                  if ($len & 0x80) {
1132                      $bytes = $len & 0x0f;
1133                      $len = 0;
1134                      for ($i = 0; $i < $bytes; $i++) {
1135                          $len = ($len << 8) | ord($data[$i + 2]);
1136                      }
1137                  }
1138                  $string_data = substr($data, 2 + $bytes, $len);
1139                  $data = substr($data, 2 + $bytes + $len);
1140                  $result[] = array('string', $string_data);
1141                  break;
1142  
1143              case 0x17:
1144                  // Time types
1145                  $len = ord($data[1]);
1146                  $bytes = 0;
1147                  if ($len & 0x80) {
1148                      $bytes = $len & 0x0f;
1149                      $len = 0;
1150                      for ($i = 0; $i < $bytes; $i++) {
1151                          $len = ($len << 8) | ord($data[$i + 2]);
1152                      }
1153                  }
1154                  $time_data = substr($data, 2 + $bytes, $len);
1155                  $data = substr($data, 2 + $bytes + $len);
1156                  $result[] = array('utctime', $time_data);
1157                  break;
1158  
1159              case 0x82:
1160                  // X509v3 extensions?
1161                  $len = ord($data[1]);
1162                  $bytes = 0;
1163                  if ($len & 0x80) {
1164                      $bytes = $len & 0x0f;
1165                      $len = 0;
1166                      for ($i = 0; $i < $bytes; $i++) {
1167                          $len = ($len << 8) | ord($data[$i + 2]);
1168                      }
1169                  }
1170                  $sequence_data = substr($data, 2 + $bytes, $len);
1171                  $data = substr($data, 2 + $bytes + $len);
1172                  $result[] = array('extension', 'X509v3 extensions');
1173                  $result[] = $this->_parseASN($sequence_data);
1174                  break;
1175  
1176              case 0xa0:
1177              case 0xa3:
1178                  // Extensions
1179                  $extension_data = substr($data, 0, 2);
1180                  $data = substr($data, 2);
1181                  $result[] = array('extension', dechex($extension_data));
1182                  break;
1183  
1184              case 0xe6:
1185                  $extension_data = substr($data, 0, 1);
1186                  $data = substr($data, 1);
1187                  $result[] = array('extension', dechex($extension_data));
1188                  break;
1189  
1190              case 0xa1:
1191                  $extension_data = substr($data, 0, 1);
1192                  $data = substr($data, 6);
1193                  $result[] = array('extension', dechex($extension_data));
1194                  break;
1195  
1196              default:
1197                  // Unknown
1198                  $result[] = array('UNKNOWN', dechex($data));
1199                  $data = '';
1200                  break;
1201              }
1202          }
1203  
1204          return (count($result) > 1) ? $result : array_pop($result);
1205      }
1206  
1207      /**
1208       * Decrypt an S?MIME signed message using a public key.
1209       *
1210       * @access private
1211       *
1212       * @param string $text   The text to be verified.
1213       * @param array $params  The parameters needed for verification.
1214       *
1215       * @return string  The verification message.
1216       *                 Returns PEAR_Error object on error.
1217       */
1218      function _decryptSignature($text, $params)
1219      {
1220          return PEAR::raiseError('_decryptSignature() ' . _("not yet implemented"));
1221      }
1222  
1223      /**
1224       * Check for the presence of the OpenSSL extension to PHP.
1225       *
1226       * @return boolean  Returns true if the openssl extension is available.
1227       *                  Returns a PEAR_Error if not.
1228       */
1229      function checkForOpenSSL()
1230      {
1231          if (!Util::extensionExists('openssl')) {
1232              return PEAR::raiseError(_("The openssl module is required for the Horde_Crypt_smime:: class."));
1233          }
1234      }
1235  
1236      /**
1237       * Extract the email address from a public key.
1238       *
1239       * @param string $key  The public key.
1240       *
1241       * @return string  Returns the email address, or null if none is found.
1242       */
1243      function getEmailFromKey($key)
1244      {
1245          $key_info = openssl_x509_parse($key);
1246          if (is_array($key_info) && isset($key_info['subject'])) {
1247              if (isset($key_info['subject']['Email'])) {
1248                  return $key_info['subject']['Email'];
1249              } elseif (isset($key_info['subject']['emailAddress'])) {
1250                  return $key_info['subject']['emailAddress'];
1251              }
1252          }
1253  
1254          return null;
1255      }
1256  
1257      /**
1258       * Convert a PKCS 12 encrypted certificate package into a private key,
1259       * public key, and any additional keys.
1260       *
1261       * @param string $text   The PKCS 12 data.
1262       * @param array $params  The parameters needed for parsing.
1263       * <pre>
1264       * Parameters:
1265       * ===========
1266       * 'sslpath' => The path to the OpenSSL binary. (REQUIRED)
1267       * 'password' => The password to use to decrypt the data. (Optional)
1268       * 'newpassword' => The password to use to encrypt the private key.
1269       *                  (Optional)
1270       * </pre>
1271       *
1272       * @return stdClass  An object.
1273       *                   'private' -  The private key in PEM format.
1274       *                   'public'  -  The public key in PEM format.
1275       *                   'certs'   -  An array of additional certs.
1276       *                   Returns PEAR_Error on error.
1277       */
1278      function parsePKCS12Data($pkcs12, $params)
1279      {
1280          /* Check for availability of OpenSSL PHP extension. */
1281          $openssl = $this->checkForOpenSSL();
1282          if (is_a($openssl, 'PEAR_Error')) {
1283              return $openssl;
1284          }
1285  
1286          if (!isset($params['sslpath'])) {
1287              return PEAR::raiseError(_("No path to the OpenSSL binary provided. The OpenSSL binary is necessary to work with PKCS 12 data."), 'horde.error');
1288          }
1289          $sslpath = escapeshellcmd($params['sslpath']);
1290  
1291          /* Create temp files for input/output. */
1292          $input = $this->_createTempFile('horde-smime');
1293          $output = $this->_createTempFile('horde-smime');
1294  
1295          $ob = &new stdClass;
1296  
1297          /* Write text to file */
1298          $fp = fopen($input, 'w+');
1299          fwrite($fp, $pkcs12);
1300          fclose($fp);
1301  
1302          /* Extract the private key from the file first. */
1303          $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nocerts';
1304          if (isset($params['password'])) {
1305              $cmdline .= ' -passin stdin';
1306              if (!empty($params['newpassword'])) {
1307                  $cmdline .= ' -passout stdin';
1308              } else {
1309                  $cmdline .= ' -nodes';
1310              }
1311              $fd = popen($cmdline, 'w');
1312              fwrite($fd, $params['password'] . "\n");
1313              if (!empty($params['newpassword'])) {
1314                  fwrite($fd, $params['newpassword'] . "\n");
1315              }
1316              pclose($fd);
1317          } else {
1318              $cmdline .= ' -nodes';
1319              exec($cmdline);
1320          }
1321          $ob->private = trim(file_get_contents($output));
1322          if (empty($ob->private)) {
1323              return PEAR::raiseError(_("Password incorrect"), 'horde.error');
1324          }
1325  
1326          /* Extract the public key next. */
1327          $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -clcerts';
1328          if (isset($params['password'])) {
1329              $cmdline .= ' -passin stdin';
1330              $fd = popen($cmdline, 'w');
1331              fwrite($fd, $params['password'] . "\n");
1332              pclose($fd);
1333          } else {
1334              exec($cmdline);
1335          }
1336          $ob->public = trim(file_get_contents($output));
1337  
1338          /* Extract the public key next. */
1339          $cmdline = $sslpath . ' pkcs12 -in ' . $input . ' -out ' . $output . ' -nokeys -cacerts';
1340          if (isset($params['password'])) {
1341              $cmdline .= ' -passin stdin';
1342              $fd = popen($cmdline, 'w');
1343              fwrite($fd, $params['password'] . "\n");
1344              pclose($fd);
1345          } else {
1346              exec($cmdline);
1347          }
1348          $ob->certs = trim(file_get_contents($output));
1349  
1350          return $ob;
1351      }
1352  
1353      /**
1354       * The Content-Type parameters PHP's openssl_pkcs7_* functions return are
1355       * deprecated.  Fix these headers to the correct ones (see RFC 2311).
1356       *
1357       * @access private
1358       *
1359       * @param string $text  The PKCS7 data.
1360       * @param string $type  Is this 'message' or 'signature' data?
1361       *
1362       * @return string  The PKCS7 data with the correct Content-Type parameter.
1363       */
1364      function _fixContentType($text, $type)
1365      {
1366          if ($type == 'message') {
1367              $from = 'application/x-pkcs7-mime';
1368              $to = 'application/pkcs7-mime';
1369          } else {
1370              $from = 'application/x-pkcs7-signature';
1371              $to = 'application/pkcs7-signature';
1372          }
1373          return str_replace('Content-Type: ' . $from, 'Content-Type: ' . $to, $text);
1374      }
1375  
1376  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7