4 * This page is used to setup https access to OpenEMR with client certificate authentication.
5 * If enabled, the browser must connect to OpenEMR using a client SSL certificate that is
6 * generated by OpenEMR. This page is used to create the Certificate Authority and
7 * Apache SSL server certificate.
10 * @link http://www.open-emr.org
11 * @author Visolve <vicareplus_engg@visolve.com>
12 * @author Brady Miller <brady.g.miller@gmail.com>
13 * @author Jerry Padgett <sjpadgett@gmail.com>
14 * @copyright Copyright (c) Visolve <vicareplus_engg@visolve.com>
15 * @copyright Copyright (c) 2018-2020 Brady Miller <brady.g.miller@gmail.com>
16 * @copyright Copyright (c) 2020 Jerry Padgett <sjpadgett@gmail.com>
17 * @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3
20 require_once("../globals.php");
21 require_once("../../library/create_ssl_certificate.php");
23 use OpenEMR\Common\Acl\AclMain
;
24 use OpenEMR\Common\Csrf\CsrfUtils
;
25 use OpenEMR\Common\Session\SessionUtil
;
26 use OpenEMR\Common\Twig\TwigContainer
;
27 use OpenEMR\Core\Header
;
30 if (!CsrfUtils
::verifyCsrfToken($_POST["csrf_token_form"])) {
31 CsrfUtils
::csrfNotVerified();
35 if (!AclMain
::aclCheckCore('admin', 'users')) {
36 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("SSL Certificate Administration")]);
40 /* This string contains any error messages if generating
46 * Send an http reply so that the browser downloads the given file.
47 * Delete the file once the download is completed.
48 * @param $filename - The file to download.
49 * @param $filetype - The type of file.
51 function download_file($filename, $filetype)
54 header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
55 header("Cache-Control: private");
56 header("Content-Type: application/" . $filetype);
57 header("Content-Disposition: attachment; filename=" . basename($filename) . ";");
58 header("Content-Transfer-Encoding: binary");
59 header("Content-Length: " . filesize($filename));
66 /* This function is called when the "Create Client Certificate" button is clicked.
67 * Create and download a client certificate, given the following form inputs:
68 * client_cert_user - The username to store in the certificate
69 * client_cert_email - The email to store in the certificate
70 * A temporary certificate will be written to /tmp/openemr_client_cert.p12.
71 * If an error occurs, set the $error_msg (which is displayed later below).
73 function create_client_cert()
77 if (!$GLOBALS['is_client_ssl_enabled']) {
78 $error_msg .= xl('Error, User Certificate Authentication is not enabled in OpenEMR');
81 if (!file_exists($GLOBALS['certificate_authority_crt'])) {
82 $error_msg .= xl('Error, the CA Certificate File doesn\'t exist');
85 if (!file_exists($GLOBALS['certificate_authority_key'])) {
86 $error_msg .= xl('Error, the CA Key File doesn\'t exist');
90 if ($_POST["client_cert_user"]) {
91 $user = trim($_POST['client_cert_user']);
94 if ($_POST["client_cert_email"]) {
95 $email = trim($_POST['client_cert_email']);
98 if ($_POST["clientPassPhrase"]) {
99 $clientPassPhrase = trim($_POST['clientPassPhrase']);
103 $data = create_user_certificate(
107 $GLOBALS['certificate_authority_crt'],
108 $GLOBALS['certificate_authority_key'],
109 $GLOBALS['client_certificate_valid_in_days']
111 if ($data === false) {
112 $error_msg .= xl('Error, unable to create client certificate.');
116 $filename = $GLOBALS['temporary_files_dir'] . "/openemr_client_cert.p12";
117 $handle = fopen($filename, 'w');
118 fwrite($handle, $data);
121 download_file($filename, "p12");
124 /* Delete the following temporary certificate files, if they exist:
125 * /tmp/CertificateAuthority.key
126 * /tmp/CertificateAuthority.crt
132 function delete_certificates()
134 $tempDir = $GLOBALS['temporary_files_dir'];
135 $files = array("CertificateAuthority.key", "CertificateAuthority.crt",
136 "Server.key", "Server.crt", "admin.p12", "ssl.zip");
138 foreach ($files as $file) {
139 if (file_exists($file)) {
146 * Create and download the following certificates:
147 * - CertificateAuthority.key
148 * - CertificateAuthority.crt
152 * The following form inputs are used:
154 function create_and_download_certificates()
157 $tempDir = $GLOBALS['temporary_files_dir'];
159 $zipName = $tempDir . "/ssl.zip";
160 if (file_exists($zipName)) {
165 $emailAddress = false;
166 $countryName = false;
167 $stateOrProvinceName = false;
168 $localityName = false;
169 $organizationName = false;
170 $organizationalUnitName = false;
171 $clientCertValidity = false;
172 $clientPassPhrase = null;
174 /* Retrieve the certificate name settings from the form input */
175 if ($_POST["commonName"]) {
176 $commonName = trim($_POST['commonName']);
179 if ($_POST["emailAddress"]) {
180 $emailAddress = trim($_POST['emailAddress']);
183 if ($_POST["countryName"]) {
184 $countryName = trim($_POST['countryName']);
187 if ($_POST["stateOrProvinceName"]) {
188 $stateOrProvinceName = trim($_POST['stateOrProvinceName']);
191 if ($_POST["localityName"]) {
192 $localityName = trim($_POST['localityName']);
195 if ($_POST["organizationName"]) {
196 $organizationName = trim($_POST['organizationName']);
199 if ($_POST["organizationalUnitName"]) {
200 $organizationName = trim($_POST['organizationalUnitName']);
203 if ($_POST["clientCertValidity"]) {
204 $clientCertValidity = trim($_POST['clientCertValidity']);
207 if ($_POST["clientPassPhrase"]) {
208 $clientPassPhrase = trim($_POST['clientPassPhrase']);
211 /* Create the Certficate Authority (CA) */
213 "OpenEMR CA for " . $commonName,
216 $stateOrProvinceName,
219 $organizationalUnitName
222 if ($arr === false) {
223 $error_msg .= xl('Error, unable to create the Certificate Authority certificate.');
224 delete_certificates();
230 $ca_crt = create_crt($ca_csr, null, $ca_key);
231 if ($ca_crt === false) {
232 $error_msg .= xl('Error, unable to create the Certificate Authority certificate.');
233 delete_certificates();
237 openssl_pkey_export_to_file($ca_key, $tempDir . "/CertificateAuthority.key", null);
238 openssl_x509_export_to_file($ca_crt, $tempDir . "/CertificateAuthority.crt");
240 /* Create the Server certificate */
245 $stateOrProvinceName,
248 $organizationalUnitName
250 if ($arr === false) {
251 $error_msg .= xl('Error, unable to create the Server certificate.');
252 delete_certificates();
256 $server_csr = $arr[0];
257 $server_key = $arr[1];
258 $server_crt = create_crt($server_csr, $ca_crt, $ca_key);
260 if ($server_crt === false) {
261 $error_msg .= xl('Error, unable to create the Server certificate.');
262 delete_certificates();
266 openssl_pkey_export_to_file($server_key, $tempDir . "/Server.key", null);
267 openssl_x509_export_to_file($server_crt, $tempDir . "/Server.crt");
269 /* Create the client certificate for the 'admin' user */
271 $res = sqlStatement("select id from users where username='admin'");
272 if ($row = sqlFetchArray($res)) {
273 $serial = $row['id'];
276 $user_cert = create_user_certificate(
280 $tempDir . "/CertificateAuthority.crt",
281 $tempDir . "/CertificateAuthority.key",
284 if ($user_cert === false) {
285 $error_msg .= xl('Error, unable to create the admin.p12 certificate.');
286 delete_certificates();
290 $adminFile = $tempDir . "/admin.p12";
291 $handle = fopen($adminFile, 'w');
292 fwrite($handle, $user_cert);
295 /* Create a zip file containing the CertificateAuthority, Server, and admin files */
297 if (! (class_exists('ZipArchive'))) {
298 SessionUtil
::setSession('zip_error', xl("Error, Class ZipArchive does not exist"));
302 $zip = new ZipArchive();
304 SessionUtil
::setSession('zip_error', xl("Error, Could not create file archive"));
308 if ($zip->open($zipName, ZipArchive
::CREATE
)) {
309 $files = array("CertificateAuthority.key", "CertificateAuthority.crt",
310 "Server.key", "Server.crt", "admin.p12");
311 foreach ($files as $file) {
312 $zip->addFile($tempDir . "/" . $file, $file);
315 SessionUtil
::setSession('zip_error', xl("Error, unable to create zip file with all the certificates"));
321 if (ini_get('zlib.output_compression')) {
322 ini_set('zlib.output_compression', 'Off');
324 } catch (Exception
$e) {
325 SessionUtil
::setSession('zip_error', xl("Error, Could not create file archive"));
329 download_file($zipName, "zip");
332 /*if ($_POST["mode"] == "save_ssl_settings") {
333 save_certificate_settings();
336 if (!empty($_POST["mode"]) && ($_POST["mode"] == "create_client_certificate")) {
337 create_client_cert();
338 } elseif (!empty($_POST["mode"]) && ($_POST["mode"] == "download_certificates")) {
339 create_and_download_certificates();
342 if (!empty($_SESSION["zip_error"])) {
343 $zipErrorOutput = '<div><table align="center"><tr valign="top"><td rowspan="3"><font class="redtext">' . text($_SESSION["zip_error"]) . '</td></tr></table></div>';
344 SessionUtil
::unsetSession('zip_error');
353 //check whether email id is valid or not
354 function checkEmail(email
) {
358 var lat
=str
.indexOf(at
);
360 var ldot
=str
.indexOf(dot
);
361 if (str
.indexOf(at
)==-1){
365 if (str
.indexOf(at
)==-1 || str
.indexOf(at
)==0 || str
.indexOf(at
)==lstr
){
369 if (str
.indexOf(dot
)==-1 || str
.indexOf(dot
)==0 || str
.indexOf(dot
)==lstr
){
373 if (str
.indexOf(at
,(lat+
1))!=-1){
377 if (str
.substring(lat
-1,lat
)==dot || str
.substring(lat+
1,lat+
2)==dot
){
381 if (str
.indexOf(dot
,(lat+
2))==-1){
385 if (str
.indexOf(" ")!=-1){
391 function download_click(){
392 if (document
.ssl_certificate_frm
.commonName
.value
== "") {
393 alert (<?php
echo xlj('Host Name cannot be empty'); ?
>);
394 document
.ssl_certificate_frm
.commonName
.focus();
398 if (document
.ssl_certificate_frm
.emailAddress
.value
) {
399 //call checkEmail function
400 if(checkEmail(document
.ssl_certificate_frm
.emailAddress
.value
) == false){
401 alert (<?php
echo xlj('Provide valid Email Address'); ?
>);
406 if (document
.ssl_certificate_frm
.countryName
.value
.length
> 2) {
407 alert (<?php
echo xlj('Country Name should be represent in two letters. (Example: United States is US)'); ?
>);
408 document
.ssl_certificate_frm
.countryName
.focus();
411 if (document
.ssl_certificate_frm
.clientCertValidity
.value
< 1) {
412 alert (<?php
echo xlj('Client certificate validity should be a valid number.'); ?
>);
413 document
.ssl_certificate_frm
.clientCertValidity
.focus();
417 function create_client_certificate_click(){
419 /*if(document.ssl_frm.isClientAuthenticationEnabled[1].checked == true)
421 alert (<?php echo xlj('User Certificate Authentication is disabled'); ?>);
425 if (document
.client_cert_frm
.client_cert_user
.value
== "") {
426 alert (<?php
echo xlj('User name or Host name cannot be empty'); ?
>);
427 document
.ssl_certificate_frm
.commonName
.focus();
430 if (document
.client_cert_frm
.client_cert_email
.value
) {
431 //call checkEmail function
432 if(checkEmail(document
.client_cert_frm
.client_cert_email
.value
) == false){
433 alert (<?php
echo xlj('Provide valid Email Address'); ?
>);
439 function isNumberKey(evt
) {
440 var charCode
= (evt
.which
) ? evt
.which
: evt
.keyCode
441 if (charCode
> 31 && (charCode
< 48 || charCode
> 57))
449 <?php Header
::setupHeader(); ?
>
461 <body
class="body_top">
462 <span
class='title'><b
><?php
echo xlt('SSL Certificate Administration'); ?
></b
></span
>
464 <?php
if (!empty($zipErrorOutput)) {
465 echo $zipErrorOutput;
469 if ($error_msg != "") {
470 echo "<font class='redtext'>" . text($error_msg) . "</font><br /><br />";
473 <?php
echo xlt('To setup https access with client certificate authentication, do the following'); ?
>
475 <li
><?php
echo xlt('Create the SSL Certificate Authority and Server certificates.'); ?
>
476 <li
><?php
echo xlt('Configure Apache to use HTTPS.'); ?
>
477 <li
><?php
echo xlt('Configure Apache and OpenEMR to use Client side SSL certificates.'); ?
>
478 <li
><?php
echo xlt('Import certificate to the browser.'); ?
>
479 <li
><?php
echo xlt('Create a Client side SSL certificate for each user or client machine.'); ?
>
483 if ($GLOBALS['certificate_authority_crt'] != "" && $GLOBALS['is_client_ssl_enabled']) {
484 echo xlt('OpenEMR already has a Certificate Authority configured.');
487 <form method
='post' name
=ssl_certificate_frm action
='ssl_certificates_admin.php'>
488 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
489 <input type
='hidden' name
='mode' value
='download_certificates'>
490 <div
class='borderbox'>
491 <b
><?php
echo xlt('Create the SSL Certificate Authority and Server certificates.'); ?
></b
><br
/>
493 1. <?php
echo xlt('Fill in the values below'); ?
><br
/>
494 2. <?php
echo xlt('Click Download Certificate to download the certificates in the file ssl.zip'); ?
> <br
/>
495 3. <?php
echo xlt('Extract the zip file');
496 echo ": ssl.zip "; ?
><br
/><br
/>
497 <?php
echo xlt('The zip file will contain the following items'); ?
> <br
/>
499 <li
>Server
.crt
: <?php
echo xlt('The Apache SSL server certificate and public key'); ?
>
500 <li
>Server
.key
: <?php
echo xlt('The corresponding private key'); ?
>
501 <li
>CertificateAuthority
.crt
: <?php
echo xlt('The Certificate Authority certificate'); ?
>
502 <li
>CertificateAuthority
.key
: <?php
echo xlt('The corresponding private key'); ?
>
503 <li
>admin
.p12
: <?php
echo xlt('A client certificate for the admin user'); ?
>
507 <td
><?php
echo xlt('Host Name'); ?
> *:</td
>
508 <td
><input name
='commonName' type
='text' value
=''></td
>
509 <td
><?php
echo xlt('Example') ;
510 echo ': hostname.domain.com'; ?
></td
>
513 <td
><?php
echo xlt('Email Address'); ?
>:</td
>
514 <td
><input name
='emailAddress' type
='text' value
=''></td
>
515 <td
><?php
echo xlt('Example') ;
516 echo ': web_admin@domain.com'; ?
></td
>
519 <td
><?php
echo xlt('Organization Name'); ?
>:</td
>
520 <td
><input name
='organizationName' type
='text' value
=''></td
>
521 <td
><?php
echo xlt('Example');
522 echo ': My Company Ltd'; ?
></td
>
525 <td
><?php
echo xlt('Organizational Unit Name'); ?
>:</td
>
526 <td
><input name
='organizationalUnitName' type
='text' value
=''></td
>
527 <td
><?php
echo xlt('Example');
528 echo ': OpenEMR'; ?
></td
>
531 <td
><?php
echo xlt('Locality'); ?
>:</td
>
532 <td
><input name
='localityName' type
='text' value
=''></td
>
533 <td
><?php
echo xlt('Example') ;
534 echo ': City'; ?
></td
>
537 <td
><?php
echo xlt('State Or Province'); ?
>:</td
>
538 <td
><input name
='stateOrProvinceName' type
='text' value
=''></td
>
539 <td
><?php
echo xlt('Example') ;
540 echo ': California'; ?
></td
>
543 <td
><?php
echo xlt('Country'); ?
>:</td
>
544 <td
><input name
='countryName' type
='text' value
='' maxlength
='2'></td
>
545 <td
><?php
echo xlt('Example');
548 echo xlt('Should be two letters');
552 <td
><?php
echo xlt('Client certificate validation period'); ?
>:</td
>
553 <td
><input name
='clientCertValidity' type
='text' onkeypress
='return isNumberKey(event)' value
='365'></td
>
554 <td
><?php
echo xlt('days'); ?
></td
>
557 <td
><?php
echo xlt('Client certificate passphrase'); ?
>:</td
>
558 <td
><input name
='clientPassPhrase' type
='text' value
=''></td
>
559 <td
><?php
echo xlt('Not required. This password is for generated admin.p12'); ?
></td
>
562 <td colspan
=3 align
='center'>
563 <input name
='sslcrt' type
='submit' onclick
='return download_click();' value
='<?php echo xla('Download Certificates
'); ?>'>
571 <div
class="borderbox">
572 <b
><?php
echo xlt('Configure Apache to use HTTPS.'); ?
></b
><br
/>
574 <?php
echo xlt('Add new certificates to the Apache configuration file'); ?
>:<br
/>
577 SSLCertificateFile
/path
/to
/Server
.crt
<br
/>
578 SSLCertificateKeyFile
/path
/to
/Server
.key
<br
/>
579 SSLCACertificateFile
/path
/to
/CertificateAuthority
.crt
<br
/>
581 <?php
echo xlt('Note'); ?
>:
583 <li
><?php
echo xlt('To Enable only HTTPS, perform the above changes and restart Apache server. If you want to configure client side certificates also, please configure them in the next section.'); ?
><br
/>
584 <li
> <?php
echo xlt('To Disable HTTPS, comment the above lines in Apache configuration file and restart Apache server.'); ?
>
589 <div
class="borderbox">
590 <form name
='ssl_frm' method
='post'>
591 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
592 <b
><?php
echo xlt('Configure Apache to use Client side SSL certificates'); ?
> </b
>
594 <?php
echo xlt('Add following lines to the Apache configuration file'); ?
>:<br
/>
596 SSLVerifyClient
require<br
/>
597 SSLVerifyDepth
2<br
/>
598 SSLOptions +StdEnvVars
<br
/>
599 <br
/> <b
><?php
echo xlt('Configure Openemr to use Client side SSL certificates'); ?
> </b
><br
/>
600 <input type
='hidden' name
='clientCertValidity_hidden' value
=''>
603 <?php
echo xlt('Update the following settings in Administration->Globals->Security'); ?
>:<br
/>
604 <?php
echo xlt('Turn on Enable Client SSL'); ?
><br
/>
605 <?php
echo xlt('Provide absolute path of following file in Path to CA Certificate File'); ?
> CertificateAuthority
.crt
<br
/>
606 <?php
echo xlt('Provide absolute path of following file in Path to CA Key File'); ?
> CertificateAuthority
.key
<br
/>
608 <br
/><?php
echo xlt('Note'); ?
>:
610 <li
><?php
echo xlt('To Enable Client side SSL certificates authentication, HTTPS should be enabled.'); ?
>
611 <li
><?php
echo xlt('After performing above configurations, import the admin client certificate to the browser and restart Apache server (empty password).'); ?
>
612 <li
><?php
echo xlt('To Disable client side SSL certificates, comment above lines in Apache configuration file and turn off the \'Enable Client SSL\' global setting in OpenEMR and restart Apache server.'); ?
>
616 <div
class="borderbox">
617 <b
><?php
echo xlt('Create Client side SSL certificates'); ?
></b
><br
/>
619 <?php
echo xlt('Create a client side SSL certificate for either a user or a client hostname.'); ?
>
623 !$GLOBALS['is_client_ssl_enabled'] ||
624 $GLOBALS['certificate_authority_crt'] == ""
626 echo "<font class='redtext'>" . xlt('OpenEMR must be configured to use certificates before it can create client certificates.') . "</font><br />";
629 <form name
='client_cert_frm' method
='post' action
='ssl_certificates_admin.php'>
630 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
631 <input type
='hidden' name
='mode' value
='create_client_certificate'>
634 <td
><?php
echo xlt('User or Host name'); ?
>*:</td
>
635 <td
><input type
='text' name
='client_cert_user' size
=20 />
638 <td
><?php
echo xlt('Email'); ?
>:</td
>
639 <td
><input type
='text' name
='client_cert_email' size
=20 />
642 <td
><?php
echo xlt('Client certificate passphrase'); ?
>:</td
>
643 <td
><input name
='clientPassPhrase' type
='password' value
=''></td
>
644 <td
><?php
echo xlt('Not required.'); ?
></td
>
647 <br
/> <input type
='submit' onclick
='return create_client_certificate_click();' value
='<?php echo xla('Create Client Certificate
'); ?>'>