W.I.P S/MIME:

- Generate private key and certificate
- Add export buttons for private key and certificate
- Add certificate field
This commit is contained in:
Hadi Nategh 2017-08-22 17:25:29 +02:00
parent fbe3b33d40
commit bbea403298
7 changed files with 210 additions and 31 deletions

View File

@ -893,6 +893,13 @@ class admin_mail
{
if (!$content['acc_'.$type.'_ssl']) $content['acc_'.$type.'_ssl'] = 'no';
}
if (!empty($content['acc_smime_password']))
{
$AB_bo = new addressbook_bo();
$smime_cert = $AB_bo->get_smime_keys($content['ident_email']);
$content['smime_cert'] = $smime_cert[$content['ident_email']];
}
}
catch(Api\Exception\NotFound $e) {
if (self::$debug) _egw_log_exception($e);
@ -953,35 +960,6 @@ class admin_mail
$tpl->disableElement('notify_save_default', !$is_multiple || !$edit_access);
$tpl->disableElement('notify_use_default', !$is_multiple);
if (isset($content['smimeKeyUpload'])
&& ($pkcs12 = file_get_contents($content['smimeKeyUpload']['tmp_name'])))
{
$smime = new Mail\Smime;
switch($content['smimeKeyUpload']['type'])
{
case 'application/x-pkcs12':
$cert_info = $smime->extractCertPKCS12($pkcs12, $content['smime_pkcs12_password']);
if (is_array($cert_info))
{
$content['acc_smime_password'] = $cert_info['pkey'];
if ($cert_info['cert'])
{
$AB_bo = new addressbook_bo();
$AB_bo->set_smime_keys(array(
$content['ident_email'] => $cert_info['cert']
));
}
}
else
{
$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!'));
}
break;
case 'application/x-iwork-keynote-sffkey':
$content['acc_smime_password'] = $pkcs12;
break;
}
}
if (isset($content['button']))
{
list($button) = each($content['button']);
@ -1086,6 +1064,40 @@ class admin_mail
}
$content['accounts'][$content['acc_id']] = Mail\Account::identity_name($content, false);
}
if (isset($content['smimeKeyUpload'])
&& ($pkcs12 = file_get_contents($content['smimeKeyUpload']['tmp_name'])))
{
$smime = new Mail\Smime;
switch($content['smimeKeyUpload']['type'])
{
case 'application/x-pkcs12':
$cert_info = $smime->extractCertPKCS12($pkcs12, $content['smime_pkcs12_password']);
if (is_array($cert_info))
{
$content['acc_smime_password'] = $cert_info['pkey'];
if ($cert_info['cert'])
{
$AB_bo = new addressbook_bo();
$AB_bo->set_smime_keys(array(
$content['ident_email'] => $cert_info['cert']
));
}
}
else
{
$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!'));
}
break;
case 'application/x-iwork-keynote-sffkey':
$content['acc_smime_password'] = $pkcs12;
break;
}
}
elseif ($content['smime_cert'] && $content['acc_smime_password'])
{
$AB_bo = new addressbook_bo();
$AB_bo->set_smime_keys(array($content['ident_email'] => $content['smime_cert']));
}
}
else
{

View File

@ -1159,5 +1159,108 @@ app.classes.admin = AppJS.extend(
{
this.egw.json('admin.admin_hooks.ajax_clear_cache&errored=1').sendRequest(true);
}, this));
},
/**
* Export content of given field into relevant file
* @param string _field
*/
smime_exportKey: function (_field)
{
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;
}
$a[0].click();
$a.remove();
},
/**
* Create certificate generator dialog
*/
smime_genCertificate: function ()
{
var self = this;
et2_createWidget("dialog",
{
callback: function(_button_id, _value)
{
if (_button_id == 'create' && _value)
{
var isValid = true;
var required = ['countryName', 'emailAddress'];
var widget = {};
// check the required fields
for (var i=0;i<required.length;i++)
{
if (_value[required[i]]) continue;
widget = this.template.widgetContainer.getWidgetById(required[i]);
widget.set_validation_error('This field is required!');
isValid = false;
}
// check mismatch passphrase
if (_value.passphrase && _value.passphrase !== _value.passphraseConf)
{
var passphraseConf = this.template.widgetContainer.getWidgetById('passphrase');
passphraseConf.set_validation_error('Confirm passphrase is not match!');
isValid = false;
}
if (isValid)
{
egw.json('mail.mail_ui.ajax_smimeGenCertificate', _value, function(_cert){
if (_cert)
{
for (var key in _cert)
{
if (!_cert[key]) continue;
switch (key)
{
case 'cert':
self.et2.getWidgetById('smime_cert').set_value(_cert[key]);
break;
case 'privkey':
self.et2.getWidgetById('acc_smime_password').set_value(_cert[key]);
break;
}
}
self.egw.message('New certificate information has been generated, please save your account if you want to store it.');
}
}).sendRequest(true);
}
else
{
return false;
}
}
},
title: egw.lang('Generate Certificate'),
buttons: [
{text: this.egw.lang("Create"), id: "create", "class": "ui-priority-primary", "default": true},
{text: this.egw.lang("Cancel"), id:"cancel"}
],
value:{
content:{
value: ''
}},
template: egw.webserverUrl+'/mail/templates/default/smimeCertGen.xet',
resizable: false,
position: 'left top'
}, et2_dialog._create_parent('mail'));
}
});

View File

@ -298,11 +298,20 @@
<rows>
<row>
<description value="SMIME"/>
<hbox>
<buttononly label="Generate Certificate" onclick="app.admin.smime_genCertificate"/>
<buttononly label="export private key" onclick="app.admin.smime_exportKey('privkey')" disabled="!@acc_smime_password"/>
<buttononly label="export certificate" onclick="app.admin.smime_exportKey('cert')" disabled="!@smime_cert"/>
</hbox>
</row>
<row>
<description value="Paste your private key as text here or upload your .key/.p12 file"/>
<textbox multiline="true" id="acc_smime_password" height="150" width="70%"/>
</row>
<row>
<description value="Certificate"/>
<textbox multiline="true" id="smime_cert" height="150" width="70%"/>
</row>
<row>
<description/>
<file id="smimeKeyUpload" accept=".key,.p12"/>

View File

@ -197,4 +197,37 @@ class Smime extends Horde_Crypt_Smime
}
return $this->verify($message, $certs);
}
/**
* 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;
}
}

View File

@ -2243,6 +2243,27 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
$ab = new addressbook_bo();
$response->data($ab->set_smime_keys(array($_metadata['email'] => $_metadata['cert'])));
}
/**
* Generates certificate base on given data and send
* private key, pubkey and certificate back to client callback.
*
* @param array $_data
*/
function ajax_smimeGenCertificate ($_data)
{
$smime = new Mail\Smime();
$response = Api\Json\Response::get();
// fields need to be excluded from data
$discards = array ('passphrase', 'passphraseConf', 'ca');
$ca = $_data['ca'];
$passphrase = $_data['passphrase'];
foreach ($_data as $key => $val)
{
if (empty($_data[$key]) || in_array($key, $discards)) unset($_data[$key]);
}
$response->data($smime->generate_certificate($_data, $ca, $passphrase));
}
/**
* Build actions for display toolbar

View File

@ -5858,7 +5858,7 @@ app.classes.mail = AppJS.extend(
/**
* Inform user about sender's certificate and offers to add it into
* relevant contact in addressbook.
*
*
* @param {type} _metadata
*/
smime_certAddToContact: function (_metadata)
@ -5869,7 +5869,7 @@ app.classes.mail = AppJS.extend(
if (_button == 2)
{
self.egw.json('mail.mail_ui.ajax_smimeAddCertToContact',
_metadata,null,function(_message){egw.message(_message);}).sendRequest(true);
_metadata,function(_message){egw.message(_message);}).sendRequest(true);
}
},
this.egw.lang("There's a new certificate information for email %1. Would you like to update/add this certificate?\n %2",_metadata.email,_metadata.certHtml),

View File

@ -19,6 +19,7 @@
<textbox id="organizationalUnitName" class="et2_fullWidth" blur="Organizational unit name"/>
<textbox id="commonName" class="et2_fullWidth" blur="Common name"/>
<textbox id="emailAddress" class="et2_fullWidth" blur="Email address"/>
<textbox id="validation" type="integer" label="Certificate validation in days" blur="365"/>
</vbox>
</groupbox>
</row>