mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-25 18:19:22 +03:00
Add intermediate certificates for S/MIME signing
This commit is contained in:
parent
8bf4727555
commit
dddf360db6
8 changed files with 39 additions and 4 deletions
|
|
@ -24,6 +24,7 @@ export class IdentityModel extends EmailModel /*AbstractModel*/ {
|
||||||
|
|
||||||
smimeKey: '',
|
smimeKey: '',
|
||||||
smimeCertificate: '',
|
smimeCertificate: '',
|
||||||
|
smimeCertificateChain: '',
|
||||||
|
|
||||||
askDelete: false,
|
askDelete: false,
|
||||||
|
|
||||||
|
|
@ -33,7 +34,9 @@ export class IdentityModel extends EmailModel /*AbstractModel*/ {
|
||||||
addComputablesTo(this, {
|
addComputablesTo(this, {
|
||||||
smimeKeyEncrypted: () => this.smimeKey().includes('-----BEGIN ENCRYPTED PRIVATE KEY-----'),
|
smimeKeyEncrypted: () => this.smimeKey().includes('-----BEGIN ENCRYPTED PRIVATE KEY-----'),
|
||||||
smimeKeyValid: () => /^-----BEGIN (ENCRYPTED |RSA )?PRIVATE KEY-----/.test(this.smimeKey()),
|
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())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1413,7 +1413,8 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
key && options.push(['OpenPGP', key]);
|
key && options.push(['OpenPGP', key]);
|
||||||
key = GnuPGUserStore.getPrivateKeyFor(email, 1);
|
key = GnuPGUserStore.getPrivateKeyFor(email, 1);
|
||||||
key && options.push(['GnuPG', key]);
|
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']);
|
&& options.push(['S/MIME']);
|
||||||
console.dir({signOptions: options});
|
console.dir({signOptions: options});
|
||||||
this.signOptions(options);
|
this.signOptions(options);
|
||||||
|
|
@ -1610,6 +1611,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
// TODO: sign in PHP fails
|
// TODO: sign in PHP fails
|
||||||
params.sign = 'S/MIME';
|
params.sign = 'S/MIME';
|
||||||
// params.signCertificate = identity.smimeCertificate();
|
// params.signCertificate = identity.smimeCertificate();
|
||||||
|
// params.signCertificateChain = identity.smimeCertificateChain();
|
||||||
// params.signPrivateKey = identity.smimeKey();
|
// params.signPrivateKey = identity.smimeKey();
|
||||||
// params.attachCertificate = false;
|
// params.attachCertificate = false;
|
||||||
if (identity.smimeKeyEncrypted()) {
|
if (identity.smimeKeyEncrypted()) {
|
||||||
|
|
|
||||||
|
|
@ -1255,6 +1255,7 @@ trait Messages
|
||||||
$oPart->SubParts->append($oSignaturePart);
|
$oPart->SubParts->append($oSignaturePart);
|
||||||
} else {
|
} else {
|
||||||
$sCertificate = $this->GetActionParam('signCertificate', '');
|
$sCertificate = $this->GetActionParam('signCertificate', '');
|
||||||
|
$sCertificateChain = $this->GetActionParam('signCertificateChain', '');
|
||||||
$sPrivateKey = $this->GetActionParam('signPrivateKey', '');
|
$sPrivateKey = $this->GetActionParam('signPrivateKey', '');
|
||||||
if ('S/MIME' === $this->GetActionParam('sign', '')) {
|
if ('S/MIME' === $this->GetActionParam('sign', '')) {
|
||||||
$sID = $this->GetActionParam('identityID', '');
|
$sID = $this->GetActionParam('identityID', '');
|
||||||
|
|
@ -1263,6 +1264,7 @@ trait Messages
|
||||||
&& ($oIdentity->Id() === $sID || $oIdentity->Email() === $oFrom->GetEmail())
|
&& ($oIdentity->Id() === $sID || $oIdentity->Email() === $oFrom->GetEmail())
|
||||||
) {
|
) {
|
||||||
$sCertificate = $oIdentity->smimeCertificate;
|
$sCertificate = $oIdentity->smimeCertificate;
|
||||||
|
$sCertificateChain = $oIdentity->smimeCertificateChain;
|
||||||
$sPrivateKey = $oIdentity->smimeKey;
|
$sPrivateKey = $oIdentity->smimeKey;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1303,6 +1305,7 @@ trait Messages
|
||||||
|
|
||||||
$SMIME = $this->SMIME();
|
$SMIME = $this->SMIME();
|
||||||
$SMIME->setCertificate($sCertificate);
|
$SMIME->setCertificate($sCertificate);
|
||||||
|
$SMIME->setCertificateChain($sCertificateChain);
|
||||||
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
$SMIME->setPrivateKey($sPrivateKey, $oPassphrase);
|
||||||
$sSignature = $SMIME->sign($tmp, $detached);
|
$sSignature = $SMIME->sign($tmp, $detached);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class Identity implements \JsonSerializable
|
||||||
|
|
||||||
private ?SensitiveString $smimeKey = null;
|
private ?SensitiveString $smimeKey = null;
|
||||||
private string $smimeCertificate = '';
|
private string $smimeCertificate = '';
|
||||||
|
private string $smimeCertificateChain = '';
|
||||||
|
|
||||||
function __construct(string $sId = '', string $sEmail = '')
|
function __construct(string $sId = '', string $sEmail = '')
|
||||||
{
|
{
|
||||||
|
|
@ -114,6 +115,7 @@ class Identity implements \JsonSerializable
|
||||||
$this->pgpSign = !empty($aData['pgpSign']);
|
$this->pgpSign = !empty($aData['pgpSign']);
|
||||||
$this->smimeKey = new SensitiveString(isset($aData['smimeKey']) ? $aData['smimeKey'] : '');
|
$this->smimeKey = new SensitiveString(isset($aData['smimeKey']) ? $aData['smimeKey'] : '');
|
||||||
$this->smimeCertificate = isset($aData['smimeCertificate']) ? $aData['smimeCertificate'] : '';
|
$this->smimeCertificate = isset($aData['smimeCertificate']) ? $aData['smimeCertificate'] : '';
|
||||||
|
$this->smimeCertificateChain = isset($aData['smimeCertificateChain']) ? $aData['smimeCertificateChain'] : '';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +138,8 @@ class Identity implements \JsonSerializable
|
||||||
'pgpEncrypt' => $this->pgpEncrypt,
|
'pgpEncrypt' => $this->pgpEncrypt,
|
||||||
'pgpSign' => $this->pgpSign,
|
'pgpSign' => $this->pgpSign,
|
||||||
'smimeKey' => (string) $this->smimeKey,
|
'smimeKey' => (string) $this->smimeKey,
|
||||||
'smimeCertificate' => $this->smimeCertificate
|
'smimeCertificate' => $this->smimeCertificate,
|
||||||
|
'smimeCertificateChain' => $this->smimeCertificateChain
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +161,7 @@ class Identity implements \JsonSerializable
|
||||||
'pgpSign' => $this->pgpSign,
|
'pgpSign' => $this->pgpSign,
|
||||||
'smimeKey' => (string) $this->smimeKey,
|
'smimeKey' => (string) $this->smimeKey,
|
||||||
'smimeCertificate' => $this->smimeCertificate,
|
'smimeCertificate' => $this->smimeCertificate,
|
||||||
|
'smimeCertificateChain' => $this->smimeCertificateChain,
|
||||||
'exists' => $this->exists
|
'exists' => $this->exists
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class OpenSSL
|
||||||
// Used for sign and decrypt
|
// Used for sign and decrypt
|
||||||
private $certificate; // OpenSSLCertificate|array|string
|
private $certificate; // OpenSSLCertificate|array|string
|
||||||
private $privateKey; // OpenSSLAsymmetricKey|OpenSSLCertificate|array|string
|
private $privateKey; // OpenSSLAsymmetricKey|OpenSSLCertificate|array|string
|
||||||
|
private ?string $certificateChain = null;
|
||||||
|
|
||||||
function __construct(string $homedir)
|
function __construct(string $homedir)
|
||||||
{
|
{
|
||||||
|
|
@ -131,6 +132,15 @@ class OpenSSL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setCertificateChain(/*string*/$certificateChain)
|
||||||
|
{
|
||||||
|
if ($certificateChain === "") {
|
||||||
|
$this->certificateChain = null;
|
||||||
|
} else {
|
||||||
|
$this->certificateChain = $certificateChain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function setPrivateKey(/*OpenSSLAsymmetricKey|string*/$privateKey,
|
public function setPrivateKey(/*OpenSSLAsymmetricKey|string*/$privateKey,
|
||||||
?\SnappyMail\SensitiveString $passphrase = null
|
?\SnappyMail\SensitiveString $passphrase = null
|
||||||
) : void
|
) : void
|
||||||
|
|
@ -244,6 +254,13 @@ class OpenSSL
|
||||||
}
|
}
|
||||||
$input = $tmp;
|
$input = $tmp;
|
||||||
}
|
}
|
||||||
|
if (\is_string($this->certificateChain)) {
|
||||||
|
$tmp = new Temporary('smimechain-');
|
||||||
|
if (!$tmp->putContents($this->certificateChain)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$certificateChain = $tmp;
|
||||||
|
}
|
||||||
$output = new Temporary('smimeout-');
|
$output = new Temporary('smimeout-');
|
||||||
if (!\openssl_pkcs7_sign(
|
if (!\openssl_pkcs7_sign(
|
||||||
$input->filename(),
|
$input->filename(),
|
||||||
|
|
@ -252,7 +269,7 @@ class OpenSSL
|
||||||
$this->privateKey,
|
$this->privateKey,
|
||||||
$this->headers,
|
$this->headers,
|
||||||
$detached ? \PKCS7_DETACHED | \PKCS7_BINARY : 0, // | PKCS7_NOCERTS | PKCS7_NOATTR
|
$detached ? \PKCS7_DETACHED | \PKCS7_BINARY : 0, // | PKCS7_NOCERTS | PKCS7_NOATTR
|
||||||
$this->untrusted_certificates_filename
|
$certificateChain ?? null
|
||||||
)) {
|
)) {
|
||||||
throw new \RuntimeException('OpenSSL sign: ' . \openssl_error_string());
|
throw new \RuntimeException('OpenSSL sign: ' . \openssl_error_string());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,7 @@
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "S\/MIME-Zertifikat importieren",
|
"POPUP_IMPORT_TITLE": "S\/MIME-Zertifikat importieren",
|
||||||
"CERTIFICATE": "Zertifikat",
|
"CERTIFICATE": "Zertifikat",
|
||||||
|
"CERTIFICATECHAIN": "Zwischenzertifikat(e)",
|
||||||
"CERTIFICATES": "S\/MIME-Zertifikate",
|
"CERTIFICATES": "S\/MIME-Zertifikate",
|
||||||
"SIGNED_MESSAGE": "S\/MIME-signierte Nachricht",
|
"SIGNED_MESSAGE": "S\/MIME-signierte Nachricht",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME-verschlüsselte Nachricht",
|
"ENCRYPTED_MESSAGE": "S\/MIME-verschlüsselte Nachricht",
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,7 @@
|
||||||
"SMIME": {
|
"SMIME": {
|
||||||
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
"POPUP_IMPORT_TITLE": "Import S\/MIME certificate",
|
||||||
"CERTIFICATE": "Certificate",
|
"CERTIFICATE": "Certificate",
|
||||||
|
"CERTIFICATECHAIN": "Intermediate Certificate(s)",
|
||||||
"CERTIFICATES": "S\/MIME Certificates",
|
"CERTIFICATES": "S\/MIME Certificates",
|
||||||
"SIGNED_MESSAGE": "S\/MIME signed message",
|
"SIGNED_MESSAGE": "S\/MIME signed message",
|
||||||
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
"ENCRYPTED_MESSAGE": "S\/MIME encrypted message",
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,10 @@
|
||||||
<label data-i18n="SMIME/CERTIFICATE"></label>
|
<label data-i18n="SMIME/CERTIFICATE"></label>
|
||||||
<textarea name="smimeCertificate" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeCertificate"></textarea>
|
<textarea name="smimeCertificate" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeCertificate"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group" data-bind="css: {'error': smimeCertificateChain() && !smimeCertificateChainValid()}">
|
||||||
|
<label data-i18n="SMIME/CERTIFICATECHAIN"></label>
|
||||||
|
<textarea name="smimeCertificateChain" class="input-xxlarge" rows="14" autofocus="" autocomplete="off" data-bind="value: smimeCertificateChain"></textarea>
|
||||||
|
</div>
|
||||||
<div class="control-group" data-bind="hidden:smimeCertificate">
|
<div class="control-group" data-bind="hidden:smimeCertificate">
|
||||||
<label></label>
|
<label></label>
|
||||||
<button type="button" data-bind="click: $root.createSelfSigned" data-i18n="CRYPTO/CREATE_SELF_SIGNED"></button>
|
<button type="button" data-bind="click: $root.createSelfSigned" data-i18n="CRYPTO/CREATE_SELF_SIGNED"></button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue