- Store full binary (pkcs12) certificate into credentials
- Extract cacert from pkcs12 and feed it into signing process
- Implement p12 export
This commit is contained in:
Hadi Nategh 2017-08-30 17:00:28 +02:00
parent b785766331
commit 3f06a89db5
8 changed files with 82 additions and 60 deletions

View File

@ -896,9 +896,7 @@ class admin_mail
if (!empty($content['acc_smime_password']))
{
$AB_bo = new addressbook_bo();
$smime_cert = $AB_bo->get_smime_keys($content['acc_smime_username']);
$content['smime_cert'] = $smime_cert[$content['acc_smime_username']];
$readonlys['smime_export_p12'] = false;
}
}
catch(Api\Exception\NotFound $e) {
@ -1036,7 +1034,7 @@ class admin_mail
$content['called_for'] : $GLOBALS['egw_info']['user']['account_id'];
}
// SMIME SAVE
if (isset($content['smimeKeyUpload']) || $content['smime_cert'] && $content['acc_smime_password'])
if (isset($content['smimeKeyUpload']))
{
$smime = new Mail\Smime;
$content['acc_smime_username'] = $smime->getEmailFromKey($content['smime_cert']);
@ -1044,10 +1042,10 @@ class admin_mail
if (($pkcs12 = file_get_contents($content['smimeKeyUpload']['tmp_name'])) &&
$content['smimeKeyUpload']['type'] == 'application/x-pkcs12')
{
$cert_info = $smime->extractCertPKCS12($pkcs12, $content['smime_pkcs12_password']);
$cert_info = Mail\Smime::extractCertPKCS12($pkcs12, $content['smime_pkcs12_password']);
if (is_array($cert_info))
{
$content['acc_smime_password'] = $cert_info['pkey'];
$content['acc_smime_password'] = $pkcs12;
$content['smime_cert'] = $cert_info['cert'];
if ($content['smime_cert'])
{
@ -1063,10 +1061,6 @@ class admin_mail
$tpl->set_validation_error('smimeKeyUpload', lang('Could not extract private key from given p12 file. Either the p12 file is broken or password is wrong!'));
}
}
elseif ($content['smime_cert'] && $content['acc_smime_password'])
{
$AB_bo->set_smime_keys(array($content['acc_smime_username'] => $content['smime_cert']));
}
}
self::fix_account_id_0($content['account_id'], true);
$content = Mail\Account::write($content, $content['called_for'] || !$this->is_admin ?

View File

@ -1163,29 +1163,16 @@ app.classes.admin = AppJS.extend(
/**
* Export content of given field into relevant file
* @param string _field
*/
smime_exportKey: function (_field)
smime_exportCert: function ()
{
var $a = jQuery(document.createElement('a')).appendTo('body').hide();
var widget = {};
switch (_field)
{
case 'privkey':
widget = this.et2.getWidgetById('acc_smime_password');
$a.attr({
download: 'private_key.key',
href: 'data:application/x-iwork-keynote-sffkey;charset=utf-8,' + encodeURI(widget.getValue())
});
break;
case 'cert':
widget = this.et2.getWidgetById('smime_cert');
$a.attr({
download: 'cert.crt',
href: 'data:application/pkix-cert;charset=utf-8,' + encodeURI(widget.getValue())
});
break;
}
var acc_id = this.et2.getArrayMgr("content").getEntry('acc_id');
var url = window.egw_webserverUrl+'/index.php?';
url += 'menuaction=mail.mail_ui.smimeExportCert';
url += '&acc_id='+acc_id;
$a.prop('href',url);
$a.prop('download',"");
$a[0].click();
$a.remove();
},

View File

@ -303,24 +303,18 @@
<description value="Certificate"/>
<hbox>
<buttononly label="Generate Certificate" onclick="app.admin.smime_genCertificate" image="add" background_image="1"/>
<buttononly label="export private key" onclick="app.admin.smime_exportKey('privkey')" disabled="!@acc_smime_password" image="export" background_image="1"/>
<buttononly label="export certificate" onclick="app.admin.smime_exportKey('cert')" disabled="!@smime_cert" image="export" background_image="1"/>
</hbox>
</row>
<row>
<description value="Certificate file in p12 format"/>
<description value="Upload your certificate .p12/.pfx file"/>
<vbox>
<file id="smimeKeyUpload" accept=".p12,.pfx"/>
<passwd id="smime_pkcs12_password" size="32" maxlength="128" blur="Password to unlock encrypted p12" autocomplete="off"/>
</vbox>
</row>
<row>
<description value="Paste your private key as text here or upload your .p12 file"/>
<textbox multiline="true" id="acc_smime_password" height="130" resize_ratio="0" class="et2_fullWidth"/>
</row>
<row>
<description value="Certificate"/>
<textbox multiline="true" id="smime_cert" height="130" resize_ratio="0" class="et2_fullWidth"/>
<description value="Current certificate"/>
<buttononly id="smime_export_p12" label="export certificate as p12" onclick="app.admin.smime_exportCert" image="export" background_image="1" readonly="true"/>
</row>
</rows>
</grid>

View File

@ -7424,9 +7424,9 @@ class Mail
private function _decryptSmimeBody ($_message, $_passphrase = '')
{
$AB_bo = new \addressbook_bo();
$credents = Mail\Credentials::read($this->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
$certkey = $AB_bo->get_smime_keys($credents['acc_smime_username']);
if (!$this->smime->verifyPassphrase($credents['acc_smime_password'], $_passphrase))
$acc_smime = Mail\Smime::get_acc_smime($this->profileID, $_passphrase);
$certkey = $AB_bo->get_smime_keys($acc_smime['acc_smime_username']);
if (!$this->smime->verifyPassphrase($acc_smime['pkey'], $_passphrase))
{
return array (
'password_required' => true,
@ -7436,8 +7436,8 @@ class Mail
$params = array (
'type' => 'message',
'pubkey' => $certkey[$credents['acc_smime_username']],
'privkey' => $credents['acc_smime_password'],
'pubkey' => $certkey[$acc_smime['acc_smime_username']],
'privkey' => $acc_smime['pkey'],
'passphrase'=> $_passphrase
);
return $this->smime->decrypt($_message, $params);

View File

@ -13,6 +13,7 @@
namespace EGroupware\Api\Mail;
use Horde_Mime_Part;
use Horde_Crypt_Smime;
use EGroupware\Api;
/**
* EMailAdmin generic base class for SMTP
*/
@ -156,7 +157,7 @@ class Smime extends Horde_Crypt_Smime
*
* @return boolean|array returns array of certs info or false if not successful
*/
public function extractCertPKCS12 ($pkcs12, $passphrase = '')
public static function extractCertPKCS12 ($pkcs12, $passphrase = '')
{
$certs = $out = array ();
if (openssl_pkcs12_read($pkcs12, $certs, $passphrase))
@ -233,4 +234,38 @@ class Smime extends Horde_Crypt_Smime
}
return $result;
}
/**
* Method to extract smime related info from credential table
*
* @param type $acc_id acc id of mail account
* @param type $passphrase = '' protect private key by passphrase
* @return mixed return array of smime info or false if fails
*/
public static function get_acc_smime($acc_id, $passphrase = '')
{
if (Api\Cache::getSession('mail', 'smime_passphrase'))
{
$passphrase = Api\Cache::getSession('mail', 'smime_passphrase');
}
$acc_smime = Credentials::read(
$acc_id,
Credentials::SMIME,
$GLOBALS['egw_info']['user']['account_id']
);
foreach ($acc_smime as $key => $val)
{
// remove other imap stuffs but smime
if (!preg_match("/acc_smime/", $key)) unset($acc_smime[$key]);
}
if ($acc_smime['acc_smime_password'])
{
$extracted = self::extractCertPKCS12(
$acc_smime['acc_smime_password'],
$passphrase
);
return array_merge($acc_smime, is_array($extracted) ? $extracted : array());
}
return false;
}
}

View File

@ -1164,7 +1164,7 @@ class Mailer extends Horde_Mime_Mail
'privkey' => $params['senderPrivKey'],
'passphrase'=> $params['passphrase'],
'sigtype' => 'detach',
'certs' => ''
'certs' => $params['extracerts']
);
// parameters to pass on for encrypt mime part
$encrypt_params = array(

View File

@ -214,8 +214,8 @@ class mail_compose
),
);
$credentials = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
if ($credentials['acc_smime_password'])
$acc_smime = Mail\Smime::get_acc_smime($this->mail_bo->profileID);
if ($acc_smime['acc_smime_password'])
{
$actions = array_merge($actions, array(
'smime_sign' => array (
@ -3656,12 +3656,8 @@ class mail_compose
protected function _encrypt($mail, $type, $recipients, $sender, $passphrase='')
{
$AB = new addressbook_bo();
$params = array (
'senderPubKey' => '', // Sender Public key
'passphrase' => $passphrase, // passphrase of sender private key
'senderPrivKey' => '', // sender private key
'recipientsCerts' => array() // Recipients Certificates
);
// passphrase of sender private key
$params['passphrase'] = $passphrase;
try
{
@ -3670,9 +3666,9 @@ class mail_compose
$sender_cert = $AB->get_smime_keys($sender);
if (!$sender_cert) throw new Exception("Encryption failed because no certificate has been found for sender address: " . $sender);
$params['senderPubKey'] = $sender_cert[$sender];
$credents = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
$params['senderPrivKey'] = $credents['acc_smime_password'];
$acc_smime = Mail\Smime::get_acc_smime($this->mail_bo->profileID, $params['passphrase']);
$params['senderPrivKey'] = $acc_smime['pkey'];
$params['extracerts'] = $acc_smime['extracerts'];
}
if (isset($recipients) && ($type == Mail\Smime::TYPE_ENCRYPT || $type == Mail\Smime::TYPE_SIGN_ENCRYPT))

View File

@ -56,6 +56,7 @@ class mail_ui
'importMessageFromVFS2DraftAndDisplay'=>True,
'subscription' => True,
'folderManagement' => true,
'smimeExportCert' => true
);
/**
@ -2267,6 +2268,21 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
$response->data($smime->generate_certificate($_data, $ca, null, $passphrase));
}
/**
* Export stored smime certificate in database
* @return boolean return false if not successful
*/
function smimeExportCert()
{
if (empty($_GET['acc_id'])) return false;
$acc_smime = Mail\Credentials::read($_GET['acc_id'], Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
$length = 0;
$mime = 'application/x-pkcs12';
Api\Header\Content::safe($acc_smime['acc_smime_password'], "certificate.p12", $mime, $length, true, true);
echo $acc_smime['acc_smime_password'];
exit();
}
/**
* Build actions for display toolbar
*/
@ -3111,10 +3127,10 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
}
catch(Mail\Smime\PassphraseMissing $e)
{
$credentials = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
if (empty($credentials['acc_smime_password']))
$acc_smime = Mail\Smime::get_acc_smime($this->mail_bo->profileID);
if (empty($acc_smime))
{
self::callWizard($e->getMessage().' '.lang('Please configure your S/MIME private key in Encryption tab located at Edit Account dialog.'));
self::callWizard($e->getMessage().' '.lang('Please configure your S/MIME certificate in Encryption tab located at Edit Account dialog.'));
}
Framework::message($e->getMessage());
// do NOT include any default CSS