From 64b51ade16c28cd3ac4ee742047d0006796c75f6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 22 Nov 2017 12:25:14 +0100 Subject: [PATCH] Add CRUD for client identities refs #3016 --- .../TlsclientidentityController.php | 134 ++++++++++++++++++ .../Config/Tls/ClientIdentity/CreateForm.php | 97 +++++++++++++ .../Config/Tls/ClientIdentity/EditForm.php | 85 +++++++++++ .../scripts/tlsclientidentity/edit.phtml | 78 ++++++++++ .../scripts/tlsclientidentity/form.phtml | 6 + 5 files changed, 400 insertions(+) create mode 100644 application/controllers/TlsclientidentityController.php create mode 100644 application/forms/Config/Tls/ClientIdentity/CreateForm.php create mode 100644 application/forms/Config/Tls/ClientIdentity/EditForm.php create mode 100644 application/views/scripts/tlsclientidentity/edit.phtml create mode 100644 application/views/scripts/tlsclientidentity/form.phtml diff --git a/application/controllers/TlsclientidentityController.php b/application/controllers/TlsclientidentityController.php new file mode 100644 index 000000000..01d9b6b84 --- /dev/null +++ b/application/controllers/TlsclientidentityController.php @@ -0,0 +1,134 @@ +assertPermission('config/application/tlscert'); + + parent::init(); + } + + public function createAction() + { + $this->view->form = $form = new CreateForm(); + $form->setRedirectUrl('tlsclientidentity/edit') + ->handleRequest(); + + $this->addTitleTab( + $this->translate('New Client Identity'), + $this->translate('Create A New TLS Client Identity') + ); + + $this->render('form'); + } + + public function editAction() + { + $this->view->editForm = $editForm = new EditForm(); + $name = $this->params->getRequired('name'); + $editForm->setOldName($name) + ->setRedirectUrl('tlsclientidentity/edit') + ->handleRequest(); + + $rawIdentity = LocalFileStorage::common('tls/clientidentities')->read(bin2hex($name) . '.pem'); + + preg_match( + '/-+BEGIN CERTIFICATE-+.+?-+END CERTIFICATE-+/s', + $rawIdentity, + $cert + ); + + $this->view->cert = array( + 'info' => openssl_x509_parse($cert[0]), + 'sha1' => openssl_x509_fingerprint($cert[0], 'sha1'), + 'sha256' => openssl_x509_fingerprint($cert[0], 'sha256'), + ); + + preg_match( + '/-+BEGIN PRIVATE KEY-+.+?-+END PRIVATE KEY-+/s', + $rawIdentity, + $key + ); + + $keyDetails = openssl_pkey_get_details(openssl_pkey_get_private($key[0])); + $pubKey = base64_decode(preg_replace('/-+(?:BEGIN|END) PUBLIC KEY-+/', '', $keyDetails['key'])); + $this->view->pubKey = array( + 'sha1' => hash('sha1', $pubKey), + 'sha256' => hash('sha256', $pubKey) + ); + + $this->addTitleTab( + $this->translate('Edit Client Identity'), + sprintf($this->translate('Edit TLS Client Identity "%s"'), $name) + ); + } + + public function removeAction() + { + $clientIdentities = LocalFileStorage::common('tls/clientidentities'); + + $name = $this->params->getRequired('name'); + $fileName = bin2hex($name) . '.pem'; + $clientIdentities->resolvePath($fileName, true); + + $this->view->form = $form = new ConfirmRemovalForm(); + $form->setOnSuccess(function (ConfirmRemovalForm $form) use ($name, $fileName, $clientIdentities) { + try { + $clientIdentities->delete($fileName); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + Notification::success( + sprintf(t('TLS client identity "%s" successfully removed'), $name) + ); + return true; + }) + ->setRedirectUrl('config/tls') + ->handleRequest(); + + $this->addTitleTab( + $this->translate('Remove Client Identity'), + sprintf($this->translate('Remove TLS Client Identity "%s"'), $name) + ); + + $this->render('form'); + } + + /** + * Add primary tab with the given label and title + * + * @param string $label + * @param string $title + */ + protected function addTitleTab($label, $title) + { + $url = clone $this->getRequest()->getUrl(); + + $this->getTabs()->add( + preg_replace('~\A.*/~', '', $url->getPath()), + array( + 'active' => true, + 'label' => $label, + 'title' => $title, + 'url' => $url + ) + ); + } +} diff --git a/application/forms/Config/Tls/ClientIdentity/CreateForm.php b/application/forms/Config/Tls/ClientIdentity/CreateForm.php new file mode 100644 index 000000000..220072e21 --- /dev/null +++ b/application/forms/Config/Tls/ClientIdentity/CreateForm.php @@ -0,0 +1,97 @@ +setName('form_config_tlsclientidentity'); + $this->setSubmitLabel($this->translate('Create')); + } + + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'name', + array( + 'label' => $this->translate('Name'), + 'description' => $this->translate('The new TLS client identity\'s name'), + 'required' => true + ) + ); + + $this->addElement( + 'file', + 'cert', + array( + 'label' => $this->translate('Certificate (PEM)'), + 'description' => $this->translate('The new TLS client certificate'), + 'required' => true, + 'validators' => array(new TlsCertFileValidator()) + ) + ); + + $this->addElement( + 'file', + 'key', + array( + 'label' => $this->translate('Private Key (PEM)'), + 'description' => $this->translate('The new TLS client private key'), + 'required' => true, + 'validators' => array(new TlsKeyFileValidator()) + ) + ); + } + + public function onSuccess() + { + $name = $this->getElement('name')->getValue(); + + try { + /** @var \Zend_Form_Element_File $cert */ + $cert = $this->getElement('cert'); + + if ($cert->isUploaded()) { + $cert->getValue(); + } + + openssl_x509_export('file://' . $cert->getFileName(), $newCert); + + /** @var \Zend_Form_Element_File $key */ + $key = $this->getElement('key'); + + if ($key->isUploaded()) { + $key->getValue(); + } + + openssl_pkey_export('file://' . $key->getFileName(), $newKey); + + $newCertDetails = openssl_pkey_get_details(openssl_pkey_get_public($newCert)); + $newKeyDetails = openssl_pkey_get_details(openssl_pkey_get_private($newKey)); + + if ($newCertDetails['key'] !== $newKeyDetails['key']) { + $this->error($this->translate('The public keys of the certificate and the private key don\'t match')); + return false; + } + + LocalFileStorage::common('tls/clientidentities')->create(bin2hex($name) . '.pem', $newCert . $newKey); + } catch (Exception $e) { + $this->error($e->getMessage()); + return false; + } + + $this->getRedirectUrl()->setParam('name', $name); + } +} diff --git a/application/forms/Config/Tls/ClientIdentity/EditForm.php b/application/forms/Config/Tls/ClientIdentity/EditForm.php new file mode 100644 index 000000000..b79425071 --- /dev/null +++ b/application/forms/Config/Tls/ClientIdentity/EditForm.php @@ -0,0 +1,85 @@ +setName('form_config_tlsclientidentity'); + $this->setSubmitLabel($this->translate('Save Changes')); + } + + public function createElements(array $formData) + { + $this->addElement( + 'text', + 'name', + array( + 'label' => $this->translate('Name'), + 'description' => $this->translate('The TLS client identity\'s name'), + 'required' => true, + 'value' => $this->oldName + ) + ); + + $this->addElement( + 'hidden', + 'old_name', + array( + 'required' => true, + 'disabled' => true, + 'value' => $this->oldName + ) + ); + } + + public function onSuccess() + { + $name = $this->getElement('name')->getValue(); + + if ($name !== $this->oldName) { + try { + $clientIdentities = LocalFileStorage::common('tls/clientidentities'); + $oldFileName = bin2hex($this->oldName) . '.pem'; + + $clientIdentities->create(bin2hex($name) . '.pem', $clientIdentities->read($oldFileName)); + $clientIdentities->delete($oldFileName); + } catch (Exception $e) { + $this->error($e->getMessage()); + return false; + } + } + + $this->getRedirectUrl()->setParam('name', $name); + } + + /** + * Set the TLS client identity's old name + * + * @param string $oldName + * + * @return $this + */ + public function setOldName($oldName) + { + $this->oldName = $oldName; + + return $this; + } +} diff --git a/application/views/scripts/tlsclientidentity/edit.phtml b/application/views/scripts/tlsclientidentity/edit.phtml new file mode 100644 index 000000000..22c7851df --- /dev/null +++ b/application/views/scripts/tlsclientidentity/edit.phtml @@ -0,0 +1,78 @@ +success() ? $timezoneDetect->getTimezoneName() : date_default_timezone_get() +); + +$subject = array(); +foreach ($cert['info']['subject'] as $key => $value) { + $subject[] = $this->escape("$key = " . var_export($value, true)); +} + +$issuer = array(); +foreach ($cert['info']['issuer'] as $key => $value) { + $issuer[] = $this->escape("$key = " . var_export($value, true)); +} +?> +
+ +
+
+ + +

translate('TLS Client Certificate') ?>

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
escape($this->translate('Subject', 'x509.certificate')) ?>', $subject) ?>
escape($this->translate('Issuer', 'x509.certificate')) ?>', $issuer) ?>
escape($this->translate('Valid from', 'x509.certificate')) ?>escape( + DateTime::createFromFormat('U', $cert['info']['validFrom_time_t']) + ->setTimezone($timeZone) + ->format(DateTime::ISO8601) + ) ?>
escape($this->translate('Valid until', 'x509.certificate')) ?>escape( + DateTime::createFromFormat('U', $cert['info']['validTo_time_t']) + ->setTimezone($timeZone) + ->format(DateTime::ISO8601) + ) ?>
escape($this->translate('SHA256 fingerprint', 'x509.certificate')) ?>escape(implode(' ', str_split(strtoupper($cert['sha256']), 2))) ?>
escape($this->translate('SHA1 fingerprint', 'x509.certificate')) ?>escape(implode(' ', str_split(strtoupper($cert['sha1']), 2))) ?>
+ +

translate('TLS Client Public Key') ?>

+ + + + + + + + + + +
escape($this->translate('SHA256 fingerprint', 'x509.certificate')) ?>escape(implode(' ', str_split(strtoupper($pubKey['sha256']), 2))) ?>
escape($this->translate('SHA1 fingerprint', 'x509.certificate')) ?>escape(implode(' ', str_split(strtoupper($pubKey['sha1']), 2))) ?>
+
\ No newline at end of file diff --git a/application/views/scripts/tlsclientidentity/form.phtml b/application/views/scripts/tlsclientidentity/form.phtml new file mode 100644 index 000000000..67aa7ee70 --- /dev/null +++ b/application/views/scripts/tlsclientidentity/form.phtml @@ -0,0 +1,6 @@ +
+ +
+
+ +
\ No newline at end of file