WIP of SMIME support: First attempt to decrypt a smime encrypted message

This commit is contained in:
Hadi Nategh 2017-01-26 17:45:07 +01:00
parent dc4a825c54
commit 2d8b8fc5dc
2 changed files with 118 additions and 27 deletions

View File

@ -35,25 +35,35 @@ class Smime extends Horde_Crypt_Smime
'application/pkcs7-signature', 'application/pkcs7-signature',
); );
/**
* SMIME signature only types
* @var type
*/
static $SMIME_SIGNATURE_ONLY_TYPES = array (
'application/x-pkcs7-signature',
'application/pkcs7-signature',
'multipart/signed'
);
/* /*
* SMIME public key regular expresion * SMIME public key regular expresion
*/ */
static public $pubkey_regexp = '/-----BEGIN PUBLIC KEY-----.*-----END PUBLIC KEY-----\r?\n/s/'; static public $pubkey_regexp = '/-----BEGIN PUBLIC KEY-----.*-----END PUBLIC KEY-----\r?\n/s';
/* /*
* SMIME encrypted private key regular expresion * SMIME encrypted private key regular expresion
*/ */
static public $privkey_encrypted_regexp = '/-----BEGIN ENCRYPTED PRIVATE KEY-----.*-----END ENCRYPTED PRIVATE KEY-----\r?\n/s/'; static public $privkey_encrypted_regexp = '/-----BEGIN ENCRYPTED PRIVATE KEY-----.*-----END ENCRYPTED PRIVATE KEY-----\r?\n/s';
/* /*
* SMIME private key regular expresion * SMIME private key regular expresion
*/ */
static public $privkey_regexp = '/-----BEGIN PRIVATE KEY-----.*-----END PRIVATE KEY-----\r?\n/s/'; static public $privkey_regexp = '/-----BEGIN PRIVATE KEY-----.*-----END PRIVATE KEY-----\r?\n/s';
/* /*
* SMIME certificate regular expresion * SMIME certificate regular expresion
*/ */
static public $certificate_regexp = '/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----\r?\n/s/'; static public $certificate_regexp = '/-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----\r?\n/s';
/** /**
* Constructor. * Constructor.
@ -77,6 +87,18 @@ class Smime extends Horde_Crypt_Smime
return in_array($_mime, self::$SMIME_TYPES); return in_array($_mime, self::$SMIME_TYPES);
} }
/**
* 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);
}
/** /**
* Check if the openssl is supported * Check if the openssl is supported
* *
@ -96,7 +118,8 @@ class Smime extends Horde_Crypt_Smime
/** /**
* Extract public key from certificate * Extract public key from certificate
* *
* @param type $cert * @param string $cert content of certificate in PEM format
*
* @return string returns public key * @return string returns public key
*/ */
public function get_publickey ($cert) public function get_publickey ($cert)
@ -109,11 +132,12 @@ class Smime extends Horde_Crypt_Smime
/** /**
* Extract certificates info from a p12 file * Extract certificates info from a p12 file
* *
* @param string $pkcs12 * @param string $pkcs12 content of p12 file in string
* @param string $passphrase * @param string $passphrase = '', passphrase to unlock the p12 file
*
* @return boolean|array returns array of certs info or false if not successful * @return boolean|array returns array of certs info or false if not successful
*/ */
public function extractCertPKCS12 ($pkcs12, $passphrase) public function extractCertPKCS12 ($pkcs12, $passphrase = '')
{ {
$certs = array (); $certs = array ();
if (openssl_pkcs12_read($pkcs12, $certs, $passphrase)) if (openssl_pkcs12_read($pkcs12, $certs, $passphrase))

View File

@ -2011,15 +2011,23 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
if (is_array($smimeData)) if (is_array($smimeData))
{ {
$error_msg[] = $smimeData['msg']; $error_msg[] = $smimeData['msg'];
$linkData = array if ($smimeData['signed'])
( {
'menuaction' => 'mail.mail_ui.getAttachment', $linkData = array
'id' => $rowID, (
'part' => $smimeData['partID'], 'menuaction' => 'mail.mail_ui.getAttachment',
'is_winmail' => false, 'id' => $rowID,
'mailbox' => base64_encode($mailbox) 'part' => $smimeData['partID'],
); 'is_winmail' => false,
$content['smimeSigUrl'] = Egw::link('/index.php',$linkData); 'mailbox' => base64_encode($mailbox)
);
$content['smimeSigUrl'] = Egw::link('/index.php',$linkData);
}
if ($smimeData['required_password'])
{
$response = Api\Json\Response::get();
$response->call('app.mail.smimeRequestPassphrase');
}
} }
//error_log(__METHOD__.__LINE__.array2string($attachments)); //error_log(__METHOD__.__LINE__.array2string($attachments));
@ -2091,6 +2099,35 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
$etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2); $etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2);
} }
/**
* decrypt given smime encrypted message
*
* @param string $_message
* @param string $_passphrase
* @return array|string return
*/
function decryptSmimeBody ($_message, $_passphrase = '')
{
$AB_bo = new addressbook_bo();
$credents = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
$certkey = $AB_bo->get_smime_keys($GLOBALS['egw_info']['user']['account_email']);
if (!$this->smime->verifyPassphrase($credents['acc_smime_password'], $_passphrase))
{
return array (
'password_required' => true,
'msg' => 'Authentication failure!'
);
}
$params = array (
'type' => 'message',
'pubkey' => $certkey[$GLOBALS['egw_info']['user']['account_email']],
'privkey' => $credents['acc_smime_password'],
'passphrase'=> $_passphrase
);
return $this->smime->decrypt($_message, $params);
}
/** /**
* Resolve certificate from smime attachment * Resolve certificate from smime attachment
* *
@ -2102,21 +2139,40 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
*/ */
function resolveSmimeAttachment (&$attachments, $_uid, $_partID, $_mailbox) function resolveSmimeAttachment (&$attachments, $_uid, $_partID, $_mailbox)
{ {
$smime = new Mail\Smime; $this->smime = new Mail\Smime;
foreach ($attachments as $key => $attachment) foreach ($attachments as $key => $attachment)
{ {
if (Mail\Smime::isSmime($attachment['mimeType'])) if (Mail\Smime::isSmime($attachment['mimeType']))
{ {
$message = $this->mail_bo->getMessageRawBody($_uid,$_partID,$_mailbox); $message = $this->mail_bo->getMessageRawBody($_uid, $_partID, $_mailbox);
$cert = $smime->verify($message);
$data = array ( if (!Mail\Smime::isSmimeSignatureOnly($attachment['mimeType']))
'verify' => $cert->verify, {
'cert' => $cert->cert, try {
'msg' => $cert->msg, $message = $this->decryptSmimeBody($message);
'certHtml' => $smime->certToHTML($cert->cert), } catch (Exception $ex) {
'partID' => $attachment['partID'] return array('msg', $ex->getMessage());
); }
}
try {
$cert = $this->smime->verify($message);
$data = array (
'verify' => $cert->verify,
'cert' => $cert->cert,
'msg' => $cert->msg,
'certHtml' => $this->smime->certToHTML($cert->cert),
'partID' => $attachment['partID'],
'signed' => true,
'message' => $message
);
} catch (Exception $ex) {
$data = array (
'signed' => false,
'message' => $message
);
}
unset ($attachments[$key]); unset ($attachments[$key]);
} }
} }
@ -2930,7 +2986,18 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
$structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false); $structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false);
$calendar_part = null; $calendar_part = null;
$bodyParts = $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part); $bodyParts = $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
$mimeType = $structure->getType();
if (Mail\Smime::isSmime( $mimeType) && !Mail\Smime::isSmimeSignatureOnly($mimeType))
{
$smimeAttachments = array ( array('mimeType' => $mimeType));
$smime = $this->resolveSmimeAttachment($smimeAttachments, $uid, 0, $mailbox);
if ($smime['message'])
{
$bodyParts[0]['body'] = $smime['message'];
}
}
// for meeting requests (multipart alternative with text/calendar part) let calendar render it // for meeting requests (multipart alternative with text/calendar part) let calendar render it
if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar'])) if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
{ {