From 182f88437435ccf5bdac58725c95d85d88435103 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 27 Feb 2024 02:19:12 +0100 Subject: [PATCH] Optionally use existing private key to generate S/MIME certificate --- dev/View/Popup/Identity.js | 3 +++ .../libraries/RainLoop/Actions/Accounts.php | 6 ++++- .../app/libraries/RainLoop/Actions/SMime.php | 8 +++--- .../app/libraries/RainLoop/Model/Identity.php | 10 +++++++ .../snappymail/smime/certificate.php | 26 ++++++++++++++----- .../templates/Views/User/PopupsIdentity.html | 2 +- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/dev/View/Popup/Identity.js b/dev/View/Popup/Identity.js index 0a15cd6de..97b8de9d9 100644 --- a/dev/View/Popup/Identity.js +++ b/dev/View/Popup/Identity.js @@ -52,10 +52,13 @@ export class IdentityPopupView extends AbstractViewPopup { if (oData.Result.x509) { identity.smimeKey(oData.Result.pkey); identity.smimeCertificate(oData.Result.x509); + } else { + this.submitError(oData.ErrorMessage); } }, { name: identity.name(), email: identity.email(), + privateKey: identity.smimeKey(), passphrase: pass.password }); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index 5a583f6e9..9de1db9f4 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -261,7 +261,11 @@ trait Accounts if (!$oIdentity->FromJSON($this->GetActionParams(), true)) { throw new ClientException(Notifications::InvalidInputArgument); } - +/* // TODO: verify private key for certificate? + if ($oIdentity->smimeCertificate && $oIdentity->smimeKey) { + new \SnappyMail\SMime\Certificate($oIdentity->smimeCertificate, $oIdentity->smimeKey); + } +*/ $this->IdentitiesProvider()->UpdateIdentity($oAccount, $oIdentity); return $this->TrueResponse(); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php index 6b6638f19..6c1f47b13 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/SMime.php @@ -59,15 +59,13 @@ trait SMime { $oAccount = $this->getAccountFromToken(); - $sName = $this->GetActionParam('name', '') ?: $oAccount->Name(); - $sEmail = $this->GetActionParam('email', '') ?: $oAccount->Email(); $sPassphrase = $this->GetActionParam('passphrase', ''); $this->logMask($sPassphrase); $cert = new Certificate(); - $cert->distinguishedName['commonName'] = $sName; - $cert->distinguishedName['emailAddress'] = $sEmail; - $result = $cert->createSelfSigned($sPassphrase); + $cert->distinguishedName['commonName'] = $this->GetActionParam('name', '') ?: $oAccount->Name(); + $cert->distinguishedName['emailAddress'] = $this->GetActionParam('email', '') ?: $oAccount->Email(); + $result = $cert->createSelfSigned($sPassphrase, $this->GetActionParam('privateKey', '')); return $this->DefaultResponse($result ?: false); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php index 34f3d2398..3cbd725ee 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php @@ -37,6 +37,16 @@ class Identity implements \JsonSerializable $this->sEmail = $sEmail; } + function __get(string $name) + { + if (!\property_exists($this, $name)) { + $name = \substr($name, 1); + } + if (\property_exists($this, $name)) { + return $this->$name; + } + } + function toMime() : \MailSo\Mime\Email { return new \MailSo\Mime\Email($this->sEmail, $this->sName); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/smime/certificate.php b/snappymail/v/0.0.0/app/libraries/snappymail/smime/certificate.php index 801cebfaa..b897fbd85 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/smime/certificate.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/smime/certificate.php @@ -112,7 +112,11 @@ class Certificate return \openssl_get_cipher_methods($aliases); } - public function createSelfSigned(string $passphrase = '') : array + public function createSelfSigned( + #[\SensitiveParameter] + string $passphrase = '', + ?string $privateKey = null + ) : array { $options = array( 'config' => __DIR__ . '/openssl.cnf', @@ -134,7 +138,13 @@ class Certificate } $pkey = null; // openssl_pkey_new($options); - $csr = \openssl_csr_new($dn, $pkey, $options); + if ($privateKey) { + $pkey = \openssl_pkey_get_private($privateKey, $passphrase); + if (!$pkey) { + throw new \RuntimeException('OpenSSL pkey: ' . \openssl_error_string()); + } + } + $csr = \openssl_csr_new($dn, $pkey, $options); if ($csr) { $this->x509 = \openssl_csr_sign( $csr, @@ -145,13 +155,11 @@ class Certificate ); if ($this->x509/* && $this->canSign() && $this->canEncrypt()*/) { $this->pkey = $pkey; - $privatekey = ''; + $privateKey || \openssl_pkey_export($pkey, $privateKey, $passphrase); $certificate = ''; - $csrStr = ''; - \openssl_pkey_export($pkey, $privatekey, $passphrase); \openssl_x509_export($this->x509, $certificate); return array( - 'pkey' => $privatekey, + 'pkey' => $privateKey, 'x509' => $certificate, // 'pkcs12' => $this->asPKCS12($pkey, $passphrase/*, array $args = array()*/) // 'canSign' => $this->canSign(), @@ -167,7 +175,11 @@ class Certificate } // returns binary data - public function asPKCS12(string $pass = '', array $args = array()) : string + public function asPKCS12( + #[\SensitiveParameter] + string $pass = '', + array $args = array() + ) : string { $out = ''; \openssl_pkcs12_export($this->x509, $out, $this->pkey, $pass, $args); diff --git a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html index f49013933..7dddd40cc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/PopupsIdentity.html @@ -96,7 +96,7 @@ -