2017-01-18 19:03:17 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* EGroupware Api: generic base class for SMIME
|
|
|
|
*
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @package api
|
|
|
|
* @subpackage mail
|
|
|
|
* @author Hadi Nategh <hn@egrupware.org>
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License Version 2+
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace EGroupware\Api\Mail;
|
2017-07-10 11:12:35 +02:00
|
|
|
use Horde_Mime_Part;
|
2017-01-18 19:03:17 +01:00
|
|
|
use Horde_Crypt_Smime;
|
|
|
|
/**
|
|
|
|
* EMailAdmin generic base class for SMTP
|
|
|
|
*/
|
|
|
|
class Smime extends Horde_Crypt_Smime
|
|
|
|
{
|
2017-01-25 18:04:54 +01:00
|
|
|
/*
|
|
|
|
* SMIME types
|
|
|
|
*/
|
2017-01-18 19:03:17 +01:00
|
|
|
static $SMIME_TYPES = array (
|
|
|
|
'application/pkcs8',
|
|
|
|
'application/pkcs7',
|
|
|
|
'application/pkcs10',
|
|
|
|
'application/pkcs8',
|
|
|
|
'application/x-pkcs7-signature',
|
2017-01-19 18:52:28 +01:00
|
|
|
'application/x-pkcs7-mime',
|
|
|
|
'application/pkcs7-mime',
|
|
|
|
'application/pkcs7-signature',
|
2017-01-18 19:03:17 +01:00
|
|
|
);
|
2017-01-25 18:04:54 +01:00
|
|
|
|
2017-01-26 17:45:07 +01:00
|
|
|
/**
|
|
|
|
* SMIME signature only types
|
|
|
|
* @var type
|
|
|
|
*/
|
|
|
|
static $SMIME_SIGNATURE_ONLY_TYPES = array (
|
|
|
|
'application/x-pkcs7-signature',
|
|
|
|
'application/pkcs7-signature',
|
2017-07-25 17:13:49 +02:00
|
|
|
'multipart/signed'
|
2017-01-26 17:45:07 +01:00
|
|
|
);
|
|
|
|
|
2017-02-03 12:33:49 +01:00
|
|
|
/**
|
|
|
|
* SMIME public key regular expression
|
2017-01-25 18:04:54 +01:00
|
|
|
*/
|
2017-01-26 17:45:07 +01:00
|
|
|
static public $pubkey_regexp = '/-----BEGIN PUBLIC KEY-----.*-----END PUBLIC KEY-----\r?\n/s';
|
2017-01-25 18:04:54 +01:00
|
|
|
|
2017-02-03 12:33:49 +01:00
|
|
|
/**
|
2017-01-25 18:04:54 +01:00
|
|
|
* SMIME encrypted private key regular expresion
|
|
|
|
*/
|
2017-01-26 17:45:07 +01:00
|
|
|
static public $privkey_encrypted_regexp = '/-----BEGIN ENCRYPTED PRIVATE KEY-----.*-----END ENCRYPTED PRIVATE KEY-----\r?\n/s';
|
2017-01-25 18:04:54 +01:00
|
|
|
|
2017-02-03 12:33:49 +01:00
|
|
|
/**
|
|
|
|
* SMIME private key regular expression
|
2017-01-25 18:04:54 +01:00
|
|
|
*/
|
2017-01-26 17:45:07 +01:00
|
|
|
static public $privkey_regexp = '/-----BEGIN PRIVATE KEY-----.*-----END PRIVATE KEY-----\r?\n/s';
|
2017-01-25 18:04:54 +01:00
|
|
|
|
2017-02-03 12:33:49 +01:00
|
|
|
/**
|
|
|
|
* SMIME certificate regular expression
|
2017-01-25 18:04:54 +01:00
|
|
|
*/
|
2017-01-26 17:45:07 +01:00
|
|
|
static public $certificate_regexp = '/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----\r?\n/s';
|
2017-01-25 18:04:54 +01:00
|
|
|
|
2017-02-03 12:33:49 +01:00
|
|
|
/**
|
|
|
|
* Encryption type of sign
|
|
|
|
*
|
|
|
|
* @var String;
|
|
|
|
*/
|
|
|
|
const TYPE_SIGN = 'smime_sign';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encryption type of encrypt
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const TYPE_ENCRYPT = 'smime_encrypt';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encryption type of sign and encrypt
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
const TYPE_SIGN_ENCRYPT = 'smime_sign_encrypt';
|
|
|
|
|
2017-01-18 19:03:17 +01:00
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
2017-07-10 11:12:35 +02:00
|
|
|
* @param Horde_Crypt_Smime $params S/MIME object.
|
2017-01-18 19:03:17 +01:00
|
|
|
*/
|
|
|
|
public function __construct($params = array())
|
|
|
|
{
|
|
|
|
parent::__construct($params);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a given mime type is smime type
|
|
|
|
*
|
|
|
|
* @param string $_mime mime type
|
|
|
|
*
|
|
|
|
* @return boolean returns TRUE if the given mime is smime
|
|
|
|
*/
|
|
|
|
public static function isSmime ($_mime)
|
|
|
|
{
|
|
|
|
return in_array($_mime, self::$SMIME_TYPES);
|
|
|
|
}
|
|
|
|
|
2017-01-26 17:45:07 +01:00
|
|
|
/**
|
|
|
|
* Check if a given mime type is smime type of signature only
|
|
|
|
*
|
|
|
|
* @param string $_mime mimetype
|
|
|
|
*
|
|
|
|
* @return type
|
|
|
|
*/
|
|
|
|
public static function isSmimeSignatureOnly ($_mime)
|
|
|
|
{
|
|
|
|
return in_array($_mime, self::$SMIME_SIGNATURE_ONLY_TYPES);
|
|
|
|
}
|
|
|
|
|
2017-01-18 19:03:17 +01:00
|
|
|
/**
|
|
|
|
* Check if the openssl is supported
|
|
|
|
*
|
|
|
|
* @return boolean returns True if openssl is supported
|
|
|
|
*/
|
|
|
|
public function enabled ()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
$this->checkForOpenSSL();
|
|
|
|
} catch (Exception $ex) {
|
2017-07-10 11:12:35 +02:00
|
|
|
error_log(__METHOD__."() openssl extension is not enabled! $ex");
|
2017-01-18 19:03:17 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-23 16:19:19 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract public key from certificate
|
|
|
|
*
|
2017-01-26 17:45:07 +01:00
|
|
|
* @param string $cert content of certificate in PEM format
|
|
|
|
*
|
2017-01-23 16:19:19 +01:00
|
|
|
* @return string returns public key
|
|
|
|
*/
|
|
|
|
public function get_publickey ($cert)
|
|
|
|
{
|
|
|
|
$handle = openssl_get_publickey($cert);
|
|
|
|
$keyData = openssl_pkey_get_details($handle);
|
|
|
|
return $keyData['key'];
|
|
|
|
}
|
2017-01-25 18:04:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract certificates info from a p12 file
|
|
|
|
*
|
2017-01-26 17:45:07 +01:00
|
|
|
* @param string $pkcs12 content of p12 file in string
|
|
|
|
* @param string $passphrase = '', passphrase to unlock the p12 file
|
|
|
|
*
|
2017-01-25 18:04:54 +01:00
|
|
|
* @return boolean|array returns array of certs info or false if not successful
|
|
|
|
*/
|
2017-01-26 17:45:07 +01:00
|
|
|
public function extractCertPKCS12 ($pkcs12, $passphrase = '')
|
2017-01-25 18:04:54 +01:00
|
|
|
{
|
2017-08-24 18:15:40 +02:00
|
|
|
$certs = $out = array ();
|
2017-01-25 18:04:54 +01:00
|
|
|
if (openssl_pkcs12_read($pkcs12, $certs, $passphrase))
|
|
|
|
{
|
2017-08-24 18:15:40 +02:00
|
|
|
openssl_pkey_export($certs['pkey'], $out, $passphrase);
|
|
|
|
$certs['pkey'] = $out;
|
2017-01-25 18:04:54 +01:00
|
|
|
return $certs;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-04-11 14:24:22 +02:00
|
|
|
|
|
|
|
/**
|
2017-06-09 12:12:31 +02:00
|
|
|
* Extract the contents from signed S/MIME data.
|
2017-04-11 14:24:22 +02:00
|
|
|
*
|
2017-06-09 12:12:31 +02:00
|
|
|
* @param string $data The signed S/MIME data.
|
2017-04-11 14:24:22 +02:00
|
|
|
*
|
2017-07-10 11:05:15 +02:00
|
|
|
* @return Horde_Mime_Part returns content of signed message as mime part object
|
2017-04-11 14:24:22 +02:00
|
|
|
*/
|
2017-06-09 12:12:31 +02:00
|
|
|
public function extractSignedContents($data)
|
2017-04-11 14:24:22 +02:00
|
|
|
{
|
2017-07-13 15:37:02 +02:00
|
|
|
return Horde_Mime_Part::parseMessage(parent::extractSignedContents($data), array('forcemime' => true));
|
2017-04-11 14:24:22 +02:00
|
|
|
}
|
2017-06-09 12:12:31 +02:00
|
|
|
|
2017-07-25 17:13:49 +02:00
|
|
|
/**
|
|
|
|
* Verify a signature
|
|
|
|
*
|
|
|
|
* @param type $message
|
|
|
|
* @return type
|
|
|
|
*/
|
|
|
|
public function verifySignature($message)
|
|
|
|
{
|
|
|
|
$cert_locations = openssl_get_cert_locations();
|
|
|
|
$certs = array();
|
2017-08-28 16:24:56 +02:00
|
|
|
foreach (scandir($cert_locations['default_cert_dir']) as $file)
|
2017-07-25 17:13:49 +02:00
|
|
|
{
|
2017-08-28 16:24:56 +02:00
|
|
|
if ($file !== '..' && $file !=='.'
|
|
|
|
&& !is_dir($cert_locations['default_cert_dir'].'/'.$file)) $certs[]= $cert_locations['default_cert_dir'].'/'.$file;
|
2017-07-25 17:13:49 +02:00
|
|
|
}
|
|
|
|
return $this->verify($message, $certs);
|
|
|
|
}
|
2017-08-22 17:25:29 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate certificate, private and public key pair
|
|
|
|
*
|
|
|
|
* @param array $_dn distinguished name to be used in certificate
|
|
|
|
* @param mixed $_cacert certificate will be signed by cacert (CA). Null means
|
|
|
|
* self-signed certificate.
|
|
|
|
* @param string $passphrase = null, protect private key by passphrase
|
|
|
|
*
|
|
|
|
* @return mixed returns signed certificate, private key and pubkey or False on failure.
|
|
|
|
*/
|
|
|
|
public function generate_certificate ($_dn, $_cacert = null, $passphrase = null)
|
|
|
|
{
|
|
|
|
$config = array(
|
|
|
|
'digest_alg' => 'sha1',
|
|
|
|
'private_key_bits' => 2048,
|
|
|
|
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
|
|
|
);
|
|
|
|
$result = array();
|
|
|
|
$csrsout = '';
|
|
|
|
if (!!($pkey = openssl_pkey_new($config)))
|
|
|
|
{
|
|
|
|
if(openssl_pkey_export($pkey, $result['privkey'], $passphrase))
|
|
|
|
{
|
|
|
|
$pubkey = openssl_pkey_get_details($pkey);
|
|
|
|
$result['pubkey'] = $pubkey['key'];
|
|
|
|
}
|
|
|
|
$csr = openssl_csr_new($_dn, $pkey, $config);
|
|
|
|
$csrs = openssl_csr_sign($csr, $_cacert, $pkey, $_dn['validation']?$_dn['validation']:365);
|
|
|
|
if (openssl_x509_export($csrs, $csrsout)) $result['cert'] = $csrsout;
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
2017-01-18 19:03:17 +01:00
|
|
|
}
|