Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add s/mime encryption #127

Open
pavarnos opened this issue Oct 30, 2013 · 14 comments
Open

add s/mime encryption #127

pavarnos opened this issue Oct 30, 2013 · 14 comments

Comments

@pavarnos
Copy link

delighted stumble across your library today and to find your support for s/mime signatures. thanks for making an awesome tool!

is it possible to add s/mime encryption that works alongside s/mime signatures?

i found http://lamp-dev.com/smime-email-encryptionsigning-using-zend_mail/100 which has an example for zend framework 1 (which I'm reluctant to use because it patches the zend framework library). Sample code can be downloaded by clicking the "attachments" link near the bottom of the page.

The gymnastics that are listed in the comments of http://php.net/manual/en/function.openssl-pkcs7-encrypt.php show that it probably needs someone with a fair bit of expertise / experience to get it working...

@Synchro
Copy link
Member

Synchro commented Oct 30, 2013

Yep, that would be nice. The difficulty in PHPMailer is in doing it at the right point since header and body generation is messy, and it would need to interact with S/MIME and DKIM signing too. Good example of sign and encrypt here.

@pavarnos
Copy link
Author

yes. Have you considered splitting out some functionality from class.phpmailer.php into smaller subclasses? It might help reduce the complexity if something like a decorator pattern http://en.wikipedia.org/wiki/Decorator_pattern was used to do the encryption.

So you might have a

class Message {
   public function createHeader()
   public function createBody()
   public function preSend()
   public function postSend()
   ... etc
}

and then decorate it with

class DKIMMessage extends Message {
    private $parent;
    public function __construct(Message $parent) { $this->parent = $parent; }
    public function preSend() { $this->parent->preSend(); $this->dkimSign(); }
    ... etc
}

class SMIMESignedMessage extends Message {}

class SMIMEEncryptedMessage extends Message {}

as needed, assembling the classes based on configuration parameters eg $message = new SMIMESignedMessage( new SMIMEEncryptedMessage( new DKIMMessage( new Message( $header, $body ))));

@Synchro
Copy link
Member

Synchro commented Oct 30, 2013

It would be lovely, but unfortunately since PHPMailer's popularity stems largely from legacy support, we can't really do BC breaks. There's a discussion in #88.

@pavarnos
Copy link
Author

hmmmm.... yes i see your point in #88. In theory it should be possible to completely rewrite phpmailer internally and create facade class to preserve the old interface using magic setters/getters for the properties, your existing PSR-2 methods etc. In reality, edge cases often get in the way.
The attraction for me of phpmailer over swiftmailer is its size: it does a lot in only a few lines of code.

@AZimmerhofer
Copy link

Does PHPmail allow to use end-to-end encryption now? Cant find any news for it?

@Synchro
Copy link
Member

Synchro commented Jun 23, 2015

No, not yet. PRs very welcome though!

@ravisorg
Copy link

@AZimmerhofer I just committed a subclass that does PGP signing / encryption and am looking for a few people to test / break it. If you're still interested, issue #505 might be for you.

@ratatine
Copy link

I might be a monkey wrench in the way of progress but here's a patch for the current master that does it native in 5.2.13.

--- PHPMailer-master/class.phpmailer.php        2015-10-14 16:33:44.000000000 -0500
+++ phpmailer/class.phpmailer.php       2016-01-25 23:09:17.853169700 -0600
@@ -591,6 +591,20 @@
     protected $sign_key_pass = '';

     /**
+     * An array of public PEM encoded certificates for each recipient
+     * @var array
+     * @access protected
+     */
+    protected $encrypt_recipcerts = array();
+
+    /**
+     * Used if body should be S/MIME encrypted
+     * @var bool
+     * @access protected
+     */
+    protected $encrypt_body = false;
+
+    /**
      * Whether to throw exceptions for errors.
      * @var boolean
      * @access protected
@@ -2271,6 +2285,32 @@
                     @unlink($signed);
                     throw new phpmailerException($this->lang('signing') . openssl_error_string());
                 }
+
+                if($this->encrypt_body) {
+
+                  // Write out the encrypted message
+                  $file = tempnam(sys_get_temp_dir(), "mail");
+                  if (false === file_put_contents($file, $this->MIMEHeader . $this->LE . $this->LE . $body)) {
+                    throw new phpmailerException($this->lang('encrypting') . ' Could not write temp file');
+                  }
+                  $encrypted = tempnam(sys_get_temp_dir(), 'encrypted');
+
+                  $encrypt = openssl_pkcs7_encrypt($file, $encrypted, $this->encrypt_recipcerts, array());
+                  if ($encrypt) {
+                    @unlink($file);
+                    $body = file_get_contents($encrypted);
+                    // As with signing, the headers get rewriting after encrypting
+                    $parts = explode("\n\n", $body, 2);
+                    $this->MIMEHeader = $parts[0] . $this->LE . $this->LE;
+                    $body = $parts[1];
+                    @unlink($encrypted);
+                  } else {
+                    @unlink($file);
+                    @unlink($encrypted);
+                    throw new phpmailerException($this->lang('encrypting') . openssl_error_string());
+                  }
+
+                }
             } catch (phpmailerException $exc) {
                 $body = '';
                 if ($this->exceptions) {
@@ -3623,6 +3663,18 @@
     }

     /**
+     * Set the certificates, keys and passwords to encrypt via S/MIME
+     * @access public
+     * @param array $recipcerts Array of certificates used for recipients in PEM format
+     */
+    public function add_encryption($recipcert_file)
+    {
+        $this->encrypt_body = true;
+       $cert = file_get_contents($recipcert_file);
+        array_push($this->encrypt_recipcerts, $cert);
+    }
+
+    /**
      * Quoted-Printable-encode a DKIM header.
      * @access public
      * @param string $txt

Everything is PEM encoded and you have to sign it and encrypt it.

ex.:
$mailer->sign("sendercert.pem", "senderkey.pem", "password", "sendercert_chain.pem");
$mailer->add_encryption("recipient1.pem");
$mailer->add_encryption("recipient2.pem");

@fischel
Copy link

fischel commented Nov 20, 2018

Ups, now it 2018 and nothing really happend in SMIME-Encryption? This point will be more and more important for mail-frameworks ...

@Synchro
Copy link
Member

Synchro commented Nov 20, 2018

Yes it will. Do you have a PR for it then?

@ratatine
Copy link

When I first wrote my piece above I had no clue about how to do a PR. Now I can barely do one. Let me know if that will help and I'll try not to cock it up too bad but if someone else can do it more easily feel free.

@fischel
Copy link

fischel commented Nov 23, 2018

@ratatine I put your patch in the 5.2.27 and it works well. Now I'll try it with 6.0.6. I'll give feedback next week. So you can make a PR ? :)

@fischel
Copy link

fischel commented Nov 30, 2018

@ratatine I would say it works perfectly even with 6.0.6. In 6 there was just a small modifiations for static-LE.
I would be nice to throw an exception if you want to send an encrypted message and maybe one of the recipients is without a certificate. He will not be able to read the message ... :)
PR?

@ratatine
Copy link

That always takes me longer than I think it will.
#1611

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants