diff --git a/dev/Model/Identity.js b/dev/Model/Identity.js index 098a11ba42..4a93b2d819 100644 --- a/dev/Model/Identity.js +++ b/dev/Model/Identity.js @@ -24,6 +24,7 @@ export class IdentityModel extends EmailModel /*AbstractModel*/ { smimeKey: '', smimeCertificate: '', + smimeCertificateChain: '', askDelete: false, @@ -33,7 +34,9 @@ export class IdentityModel extends EmailModel /*AbstractModel*/ { addComputablesTo(this, { smimeKeyEncrypted: () => this.smimeKey().includes('-----BEGIN ENCRYPTED PRIVATE KEY-----'), smimeKeyValid: () => /^-----BEGIN (ENCRYPTED |RSA )?PRIVATE KEY-----/.test(this.smimeKey()), - smimeCertificateValid: () => /^-----BEGIN CERTIFICATE-----/.test(this.smimeCertificate()) + smimeCertificateValid: () => /^-----BEGIN CERTIFICATE-----/.test(this.smimeCertificate()), + smimeCertificateChainValid: () => !this.smimeCertificateChain() + || /^-----BEGIN CERTIFICATE-----/.test(this.smimeCertificateChain()) }); } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index d768ef88f8..0af68883f2 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -1413,7 +1413,8 @@ export class ComposePopupView extends AbstractViewPopup { key && options.push(['OpenPGP', key]); key = GnuPGUserStore.getPrivateKeyFor(email, 1); key && options.push(['GnuPG', key]); - identity.smimeKeyValid() && identity.smimeCertificateValid() && identity.email === email + identity.smimeKeyValid() && identity.smimeCertificateValid() + && identity.smimeCertificateChainValid() && identity.email === email && options.push(['S/MIME']); console.dir({signOptions: options}); this.signOptions(options); @@ -1610,6 +1611,7 @@ export class ComposePopupView extends AbstractViewPopup { // TODO: sign in PHP fails params.sign = 'S/MIME'; // params.signCertificate = identity.smimeCertificate(); +// params.signCertificateChain = identity.smimeCertificateChain(); // params.signPrivateKey = identity.smimeKey(); // params.attachCertificate = false; if (identity.smimeKeyEncrypted()) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index 8b04bbdad2..1153e9bee3 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -1255,6 +1255,7 @@ private function buildMessage(Account $oAccount, bool $bWithDraftInfo = true) : $oPart->SubParts->append($oSignaturePart); } else { $sCertificate = $this->GetActionParam('signCertificate', ''); + $sCertificateChain = $this->GetActionParam('signCertificateChain', ''); $sPrivateKey = $this->GetActionParam('signPrivateKey', ''); if ('S/MIME' === $this->GetActionParam('sign', '')) { $sID = $this->GetActionParam('identityID', ''); @@ -1263,6 +1264,7 @@ private function buildMessage(Account $oAccount, bool $bWithDraftInfo = true) : && ($oIdentity->Id() === $sID || $oIdentity->Email() === $oFrom->GetEmail()) ) { $sCertificate = $oIdentity->smimeCertificate; + $sCertificateChain = $oIdentity->smimeCertificateChain; $sPrivateKey = $oIdentity->smimeKey; break; } @@ -1303,6 +1305,7 @@ private function buildMessage(Account $oAccount, bool $bWithDraftInfo = true) : $SMIME = $this->SMIME(); $SMIME->setCertificate($sCertificate); + $SMIME->setCertificateChain($sCertificateChain); $SMIME->setPrivateKey($sPrivateKey, $oPassphrase); $sSignature = $SMIME->sign($tmp, $detached); 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 f31e77a9c1..414efae6b9 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 @@ -31,6 +31,7 @@ class Identity implements \JsonSerializable private ?SensitiveString $smimeKey = null; private string $smimeCertificate = ''; + private string $smimeCertificateChain = ''; function __construct(string $sId = '', string $sEmail = '') { @@ -114,6 +115,7 @@ public function FromJSON(array $aData, bool $bJson = false): bool $this->pgpSign = !empty($aData['pgpSign']); $this->smimeKey = new SensitiveString(isset($aData['smimeKey']) ? $aData['smimeKey'] : ''); $this->smimeCertificate = isset($aData['smimeCertificate']) ? $aData['smimeCertificate'] : ''; + $this->smimeCertificateChain = isset($aData['smimeCertificateChain']) ? $aData['smimeCertificateChain'] : ''; return true; } @@ -136,7 +138,8 @@ public function ToSimpleJSON(): array 'pgpEncrypt' => $this->pgpEncrypt, 'pgpSign' => $this->pgpSign, 'smimeKey' => (string) $this->smimeKey, - 'smimeCertificate' => $this->smimeCertificate + 'smimeCertificate' => $this->smimeCertificate, + 'smimeCertificateChain' => $this->smimeCertificateChain ); } @@ -158,6 +161,7 @@ public function jsonSerialize() 'pgpSign' => $this->pgpSign, 'smimeKey' => (string) $this->smimeKey, 'smimeCertificate' => $this->smimeCertificate, + 'smimeCertificateChain' => $this->smimeCertificateChain, 'exists' => $this->exists ); } diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/smime/openssl.php b/snappymail/v/0.0.0/app/libraries/snappymail/smime/openssl.php index 1bb35c2d96..3af3b7f25a 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/smime/openssl.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/smime/openssl.php @@ -19,6 +19,7 @@ class OpenSSL // Used for sign and decrypt private $certificate; // OpenSSLCertificate|array|string private $privateKey; // OpenSSLAsymmetricKey|OpenSSLCertificate|array|string + private ?string $certificateChain = null; function __construct(string $homedir) { @@ -131,6 +132,15 @@ public function setCertificate(/*OpenSSLCertificate|string*/$certificate) } } + public function setCertificateChain(/*string*/$certificateChain) + { + if ($certificateChain === "") { + $this->certificateChain = null; + } else { + $this->certificateChain = $certificateChain; + } + } + public function setPrivateKey(/*OpenSSLAsymmetricKey|string*/$privateKey, ?\SnappyMail\SensitiveString $passphrase = null ) : void @@ -244,6 +254,13 @@ public function sign(/*string|Temporary*/$input, bool $detached = true) } $input = $tmp; } + if (\is_string($this->certificateChain)) { + $tmp = new Temporary('smimechain-'); + if (!$tmp->putContents($this->certificateChain)) { + return null; + } + $certificateChain = $tmp; + } $output = new Temporary('smimeout-'); if (!\openssl_pkcs7_sign( $input->filename(), @@ -252,7 +269,7 @@ public function sign(/*string|Temporary*/$input, bool $detached = true) $this->privateKey, $this->headers, $detached ? \PKCS7_DETACHED | \PKCS7_BINARY : 0, // | PKCS7_NOCERTS | PKCS7_NOATTR - $this->untrusted_certificates_filename + $certificateChain ?? null )) { throw new \RuntimeException('OpenSSL sign: ' . \openssl_error_string()); } diff --git a/snappymail/v/0.0.0/app/localization/de/user.json b/snappymail/v/0.0.0/app/localization/de/user.json index fcc16a6872..94328fe83d 100644 --- a/snappymail/v/0.0.0/app/localization/de/user.json +++ b/snappymail/v/0.0.0/app/localization/de/user.json @@ -331,6 +331,7 @@ "SMIME": { "POPUP_IMPORT_TITLE": "S\/MIME-Zertifikat importieren", "CERTIFICATE": "Zertifikat", + "CERTIFICATECHAIN": "Zwischenzertifikat(e)", "CERTIFICATES": "S\/MIME-Zertifikate", "SIGNED_MESSAGE": "S\/MIME-signierte Nachricht", "ENCRYPTED_MESSAGE": "S\/MIME-verschlüsselte Nachricht", diff --git a/snappymail/v/0.0.0/app/localization/en/user.json b/snappymail/v/0.0.0/app/localization/en/user.json index ef8da14527..bf82c0ad9c 100644 --- a/snappymail/v/0.0.0/app/localization/en/user.json +++ b/snappymail/v/0.0.0/app/localization/en/user.json @@ -331,6 +331,7 @@ "SMIME": { "POPUP_IMPORT_TITLE": "Import S\/MIME certificate", "CERTIFICATE": "Certificate", + "CERTIFICATECHAIN": "Intermediate Certificate(s)", "CERTIFICATES": "S\/MIME Certificates", "SIGNED_MESSAGE": "S\/MIME signed message", "ENCRYPTED_MESSAGE": "S\/MIME encrypted message", 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 0f067c9426..6d0855ff39 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 @@ -101,6 +101,10 @@

+
+ + +