diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Trust/Api/CertController.php b/src/opnsense/mvc/app/controllers/OPNsense/Trust/Api/CertController.php index 3ae6b811a..92c75058a 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Trust/Api/CertController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Trust/Api/CertController.php @@ -231,4 +231,43 @@ class CertController extends ApiMutableModelControllerBase } return $result; } + + /** + * generate file download content + * @param string $uuid certificate reference + * @param string $type one of crt/prv/pkcs12, + * $_POST['password'] my contain an optional password for the pkcs12 format + * @return array + */ + public function generateFileAction($uuid=null, $type='crt') + { + $result = ['status' => 'failed']; + if ($this->request->isPost() && !empty($uuid)) { + $node = $this->getModel()->getNodeByReference('cert.'. $uuid); + if ($node === null || empty((string)$node->crt_payload)) { + $result['error'] = gettext('Misssing certificate'); + } elseif ($type == 'crt') { + $result['status'] = 'ok'; + $result['payload'] = (string)$node->crt_payload; + } elseif ($type == 'prv') { + $result['status'] = 'ok'; + $result['payload'] = (string)$node->prv_payload; + } elseif ($type == 'pkcs12') { + $passphrase = $this->request->getPost('password', null, ''); + $tmp = CertStore::getPKCS12( + (string)$node->crt_payload, + (string)$node->prv_payload, + (string)$node->descr, + $passphrase + ); + if (!empty($tmp['payload'])) { + // binary data, we need to encode it to deliver it to the client + $result['payload_b64'] = base64_encode($tmp['payload']); + } else { + $result['error'] = $tmp['error'] ?? ''; + } + } + } + return $result; + } } diff --git a/src/opnsense/mvc/app/library/OPNsense/Trust/Store.php b/src/opnsense/mvc/app/library/OPNsense/Trust/Store.php index c7019418f..6c43d582f 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Trust/Store.php +++ b/src/opnsense/mvc/app/library/OPNsense/Trust/Store.php @@ -485,6 +485,28 @@ class Store return false; } + /** + * @param string $certificate + * @param string $private_key + * @param string $friendly_name + * @param string $passphrase + * @return string + */ + public static function getPKCS12($certificate, $private_key, $friendly_name=null, $passphrase=null) + { + $old_err_level = error_reporting(0); /* prevent openssl error from going to stderr/stdout */ + $options = []; + if (!empty($friendly_name)) { + $options['friendly_name'] = $friendly_name; + } + $result = []; + if (!openssl_pkcs12_export($certificate, $result['payload'], $private_key, $passphrase, $options)) { + self::_addSSLErrors($result); + } + error_reporting($old_err_level); + return $result; + } + /** * @param string $cert certificate * @return array [stdout|stderr] diff --git a/src/opnsense/mvc/app/views/OPNsense/Trust/cert.volt b/src/opnsense/mvc/app/views/OPNsense/Trust/cert.volt index b3b3f6cf9..5abebc3bf 100644 --- a/src/opnsense/mvc/app/views/OPNsense/Trust/cert.volt +++ b/src/opnsense/mvc/app/views/OPNsense/Trust/cert.volt @@ -74,6 +74,72 @@ classname: 'fa fa-fw fa-info-circle', title: "{{ lang._('show certificate info') }}", sequence: 10 + }, + download: { + method: function(event){ + let uuid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : ''; + let $container = $("
"); + let $type = $(""); + let $password = $(""); + $type.append($("").text('Certificate')); + $type.append($("").text('Private key')); + $type.append($("