From bbea403298521c24090bfdc8ba34a9860816af97 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Tue, 22 Aug 2017 17:25:29 +0200 Subject: [PATCH] W.I.P S/MIME: - Generate private key and certificate - Add export buttons for private key and certificate - Add certificate field --- admin/inc/class.admin_mail.inc.php | 70 +++++++++------- admin/js/app.js | 103 ++++++++++++++++++++++++ admin/templates/default/mailaccount.xet | 9 +++ api/src/Mail/Smime.php | 33 ++++++++ mail/inc/class.mail_ui.inc.php | 21 +++++ mail/js/app.js | 4 +- mail/templates/default/smimeCertGen.xet | 1 + 7 files changed, 210 insertions(+), 31 deletions(-) diff --git a/admin/inc/class.admin_mail.inc.php b/admin/inc/class.admin_mail.inc.php index 6642e51081..ba910b6f17 100644 --- a/admin/inc/class.admin_mail.inc.php +++ b/admin/inc/class.admin_mail.inc.php @@ -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 { diff --git a/admin/js/app.js b/admin/js/app.js index b037d9eb5c..76dd871c99 100644 --- a/admin/js/app.js +++ b/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 + + + + + + + + + diff --git a/api/src/Mail/Smime.php b/api/src/Mail/Smime.php index 781c7e1c28..ceed21b08b 100644 --- a/api/src/Mail/Smime.php +++ b/api/src/Mail/Smime.php @@ -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; + } } diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 3133ff38b8..883921a238 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -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 diff --git a/mail/js/app.js b/mail/js/app.js index babcd7d1e9..f1a1ffdf3f 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -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), diff --git a/mail/templates/default/smimeCertGen.xet b/mail/templates/default/smimeCertGen.xet index 0c8132c066..741a528a4a 100644 --- a/mail/templates/default/smimeCertGen.xet +++ b/mail/templates/default/smimeCertGen.xet @@ -19,6 +19,7 @@ +