ellite/Wallos · error · Exception
$this->lang('signing') . openssl_error_string()
Error message
$this->lang('signing') . openssl_error_string() What it means
PHPMailer throws lang('signing') plus the openssl error string when openssl_pkcs7_sign() fails during S/MIME message signing in createBody(). The message body is discarded and the openssl diagnostic is appended to identify the cause.
Solutions
- Verify the private key and certificate match: compare openssl x509 -noout -modulus and openssl rsa -noout -modulus outputs
- Check key/cert file paths are absolute and readable by the PHP process user
- Confirm the PEM files are valid: openssl rsa -in key.pem -check and openssl x509 -in cert.pem -noout -text
- Read the appended openssl_error_string() message in the exception for the exact OpenSSL failure reason
Example fix
// before
$mail->sign('key.pem', 'cert.pem'); // relative paths, unreadable by www-data
// after
$mail->sign('/etc/ssl/private/mail.key', '/etc/ssl/certs/mail.crt');
chmod 600 /etc/ssl/private/mail.key; chown www-data /etc/ssl/private/mail.key Defensive patterns
Strategy: validation
Validate before calling
$key = openssl_pkey_get_private('file:///etc/ssl/private/mail.key', $pass);
if (!$key) throw new RuntimeException('Signing key invalid: ' . openssl_error_string());
openssl_x509_parse('file:///etc/ssl/certs/mail.crt') ?: throw new RuntimeException('Signing cert invalid'); Type guard
function signingFilesValid(string $key, string $cert): bool { return is_readable($key) && is_readable($cert) && (bool) openssl_x509_parse('file://' . $cert) && (bool) openssl_pkey_get_private('file://' . $key); } Try / catch
try { $mail->send(); } catch (PHPMailer\PHPMailer\Exception $e) { if (str_contains($e->getMessage(), 'Signing failed') || str_contains($e->getMessage(), 'signing')) { error_log('S/MIME signing failed: ' . $e->getMessage()); } throw $e; } Prevention
- Validate key/cert pair match (modulus compare) before configuring signing
- Use absolute, permission-checked paths readable by the PHP user
- Log openssl_error_string() whenever signing setup changes
- Renew certificates before expiry and re-verify after rotation
When it happens
Trigger: sign_key_file and sign_key_cert are set, PKCS7_TEXT is defined, but openssl_pkcs7_sign() returns false — typically due to unreadable/corrupt key or certificate files, a key/cert mismatch, or a wrong passphrase.
Common situations: Expired or malformed PEM certificates, certificate and private key not matching, relative paths that don't resolve under the web server user, or an unencrypted key file passed where a passphrase-protected one is required (or vice versa).
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- $this->lang('extension_missing') . 'openssl'
- ( )
- %s: %s
- Invalid address (to/cc/bcc)
- Invalid address (From)
AI-assisted analysis of ellite/Wallos@52820e87ca (2026-09-13).
Data as JSON: /api/errors/bb678eb258df6f91.
Report an issue: GitHub.
Appendix: source
Thrown at libs/PHPMailer/PHPMailer.php:3083
'file://' . realpath($this->sign_cert_file),
['file://' . realpath($this->sign_key_file), $this->sign_key_pass],
[],
PKCS7_DETACHED,
$this->sign_extracerts_file
);
}
@unlink($file);
if ($sign) {
$body = file_get_contents($signed);
@unlink($signed);
//The message returned by openssl contains both headers and body, so need to split them up
$parts = explode("\n\n", $body, 2);
$this->MIMEHeader .= $parts[0] . static::$LE . static::$LE;
$body = $parts[1];
} else {
@unlink($signed);
throw new Exception($this->lang('signing') . openssl_error_string());
}
} catch (Exception $exc) {
$body = '';
if ($this->exceptions) {
throw $exc;
}
}
}
return $body;
}
/**
* Get the boundaries that this message will use
* @return array
*/
public function getBoundaries()
{View on GitHub (pinned to 52820e87ca)