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

  1. Verify the private key and certificate match: compare openssl x509 -noout -modulus and openssl rsa -noout -modulus outputs
  2. Check key/cert file paths are absolute and readable by the PHP process user
  3. Confirm the PEM files are valid: openssl rsa -in key.pem -check and openssl x509 -in cert.pem -noout -text
  4. 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

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


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)