diff --git a/.gitignore b/.gitignore index 989164d65..d3c6c62fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ docs/phpdoc/ test/message.txt test/testbootstrap.php +test/*.pem .idea build/ diff --git a/class.phpmailer.php b/class.phpmailer.php index 1faaabd43..e45b3070a 100644 --- a/class.phpmailer.php +++ b/class.phpmailer.php @@ -536,6 +536,13 @@ class PHPMailer */ protected $sign_key_file = ''; + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * @type string + * @access protected + */ + protected $sign_extracerts_file = ''; + /** * The S/MIME password for the key. * Used only if the key is encrypted. @@ -1971,7 +1978,9 @@ public function createBody() $signed, 'file://' . realpath($this->sign_cert_file), array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), - null + null, + PKCS7_DETACHED, + $this->sign_extracerts_file ) ) { @unlink($file); @@ -3244,12 +3253,14 @@ public static function normalizeBreaks($text, $breaktype = "\r\n") * @param string $cert_filename * @param string $key_filename * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate */ - public function sign($cert_filename, $key_filename, $key_pass) + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') { $this->sign_cert_file = $cert_filename; $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; } /** diff --git a/test/phpmailerTest.php b/test/phpmailerTest.php index 2fd66e6eb..b59a89497 100644 --- a/test/phpmailerTest.php +++ b/test/phpmailerTest.php @@ -1260,7 +1260,7 @@ public function testBase64() $this->assertTrue($this->Mail->send(), 'Base64 encoding failed'); } /** - * S/MIME Signing tests. + * S/MIME Signing tests (self-signed). */ public function testSigning() { @@ -1277,12 +1277,17 @@ public function testSigning() 'commonName' => 'PHPMailer Test', 'emailAddress' => 'phpmailer@example.com' ); + $keyconfig = array( + "digest_alg" => "sha256", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + ); $password = 'password'; $certfile = 'certfile.txt'; $keyfile = 'keyfile.txt'; //Make a new key pair - $pk = openssl_pkey_new(); + $pk = openssl_pkey_new($keyconfig); //Create a certificate signing request $csr = openssl_csr_new($dn, $pk); //Create a self-signed cert @@ -1304,6 +1309,88 @@ public function testSigning() unlink($keyfile); } + /** + * S/MIME Signing tests using a CA chain cert. + * To test that a generated message is signed correctly, save the message in a file + * and use openssl along with the certs generated by this script: + * `openssl smime -verify -in signed.eml -signer certfile.pem -CAfile cacertfile.pem` + */ + public function testSigningWithCA() + { + $this->Mail->Subject .= ': S/MIME signing with CA'; + $this->Mail->Body = 'This message is S/MIME signed with an extra CA cert.'; + $this->buildBody(); + + $certprops = array( + 'countryName' => 'UK', + 'stateOrProvinceName' => 'Here', + 'localityName' => 'There', + 'organizationName' => 'PHP', + 'organizationalUnitName' => 'PHPMailer', + 'commonName' => 'PHPMailer Test', + 'emailAddress' => 'phpmailer@example.com' + ); + $cacertprops = array( + 'countryName' => 'UK', + 'stateOrProvinceName' => 'Here', + 'localityName' => 'There', + 'organizationName' => 'PHP', + 'organizationalUnitName' => 'PHPMailer CA', + 'commonName' => 'PHPMailer Test CA', + 'emailAddress' => 'phpmailer@example.com' + ); + $keyconfig = array( + "digest_alg" => "sha256", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + ); + $password = 'password'; + $cacertfile = 'cacertfile.pem'; + $cakeyfile = 'cakeyfile.pem'; + $certfile = 'certfile.pem'; + $keyfile = 'keyfile.pem'; + + //Create a CA cert + //Make a new key pair + $capk = openssl_pkey_new($keyconfig); + //Create a certificate signing request + $csr = openssl_csr_new($cacertprops, $capk); + //Create a self-signed cert + $cert = openssl_csr_sign($csr, null, $capk, 1); + //Save the CA cert + openssl_x509_export($cert, $certout); + file_put_contents($cacertfile, $certout); + //Save the CA key + openssl_pkey_export($capk, $pkeyout, $password); + file_put_contents($cakeyfile, $pkeyout); + + //Create a cert signed by our CA + //Make a new key pair + $pk = openssl_pkey_new($keyconfig); + //Create a certificate signing request + $csr = openssl_csr_new($certprops, $pk); + //Create a self-signed cert + $cert = openssl_csr_sign($csr, 'file://' . $cacertfile, $capk, 1); + //Save the cert + openssl_x509_export($cert, $certout); + file_put_contents($certfile, $certout); + //Save the key + openssl_pkey_export($pk, $pkeyout, $password); + file_put_contents($keyfile, $pkeyout); + + $this->Mail->sign( + $certfile, + $keyfile, + $password, + $cacertfile + ); + $this->assertTrue($this->Mail->send(), 'S/MIME signing with CA failed'); + unlink($cacertfile); + unlink($cakeyfile); + unlink($certfile); + unlink($keyfile); + } + /** * DKIM Signing tests. */