forked from extern/egroupware
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:
parent
fbe3b33d40
commit
bbea403298
@ -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
|
||||
{
|
||||
|
103
admin/js/app.js
103
admin/js/app.js
@ -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'));
|
||||
}
|
||||
});
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user