root/trunk/securemail.class.php

Revision 18, 10.4 kB (checked in by bobe, 2 years ago)

oups, ajout mots-clés sur securemail.class.php

  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php
2 /**
3  * Copyright (c) 2006 Aurélien Maille
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * @package Wamailer
20  * @author  Bobe <wascripts@phpcodeur.net>
21  * @link    http://phpcodeur.net/wascripts/wamailer/
22  * @license http://www.gnu.org/copyleft/lesser.html
23  * @version $Id$
24  *
25  * @see RFC 1847 - Security Multiparts for MIME: Multipart/Signed and Multipart/Encrypted
26  * @see RFC 3156 - MIME Security with OpenPGP
27  *
28  * Les sources qui m’ont bien aidées :
29  *
30  * @link http://www.gnupg.org/(en)/documentation/index.html
31  * @link http://lea-linux.org/cached/index/Reseau-secu-gpg-intro.html#
32  * @link http://www.kfwebs.net/articles/article/15/PHP-Sendmail-classes
33  */
34
35 class SecureMail extends Email {
36     
37     const A_ENCRYPTION = 1;// chiffrement asymétrique
38     const S_ENCRYPTION = 2;// chiffrement symétrique
39     
40     /**
41      * Si non défini, GnuPG utilisera la variable d’environnement $GNUPGHOME
42      * ou le répertoire ~/.gnupg
43      *
44      * @var string
45      * @access public
46      */
47     public $homedir     = null;
48     
49     public $tmpdir      = '/tmp';
50     public $logfile     = '/dev/null';
51     public $gpg_bin     = '/usr/bin/gpg';
52     public $digest_algo = 'sha1';
53     public $cipher_algo = null;// si non spécifié, GPG utilisera un algorithme par défaut
54     
55     private $_sign      = false;
56     private $_encrypt   = false;
57     private $secretkey  = null;
58     private $passphrase = null;
59     private $recipients = array();
60     private $encryption = null;
61     private $password   = null;
62     
63     /**
64      * Ajouter une signature numérique à cet email.
65      * Si le passphrase n’est pas fourni, le script supposera que la clé
66      * utilisée n’en nécessite aucun.
67      * Si $secretkey n’est pas spécifié, GnuPG utilisera la clé par défaut
68      * spécifiée dans le fichier gpg.conf.
69      *
70      * @param string $secretkey   Identifiant de la clé ou le nom ou adresse
71      *                            email correspondant à la clé voulue
72      * @param string $passphrase  passphrase pour dévérouiller la clé
73      *
74      * @access public
75      * @return void
76      */
77     public function sign($secretkey = null, $passphrase = null)
78     {
79         $this->_sign = true;
80         $this->secretkey  = $secretkey;
81         $this->passphrase = $passphrase;
82     }
83     
84     /**
85      * Chiffrement asymétrique ou symétrique de l’email.
86      * Si le type de chiffrement ($encryption) n’est pas spécifié,
87      * le script utilisera le chiffrement asymétrique.
88      *
89      * @param mixed   $data        Dépend du type de chiffrement choisi.
90      *   - Si le chiffrement asymétrique est utilisé, correspondant à
91      *     l’identifiant du destinataire.
92      *   - Si le chiffrement symétrique est utilisé, correspondant au
93      *     passphrase permettant le déchiffrement du message.
94      * @param integer $encryption  Type de chiffrement. Peut valoir
95      *                             self::A_ENCRYPTION ou self::S_ENCRYPTION
96      *
97      * @access public
98      * @return void
99      */
100     public function encrypt($data, $encryption = null)
101     {
102         if( !in_array($encryption, array(self::A_ENCRYPTION, self::S_ENCRYPTION)) ) {
103             $encryption = self::A_ENCRYPTION;
104         }
105         else {
106             $this->encryption = $encryption;
107         }
108         
109         if( $encryption == self::A_ENCRYPTION ) {
110             if( !is_array($data) ) {
111                 $data = array($data);
112             }
113             
114             $this->_encrypt   = true;
115             $this->recipients = $data;
116         }
117         else {
118             $this->_encrypt = true;
119             $this->password = $data;
120         }
121     }
122     
123     /**
124      * @access public
125      * @return void
126      */
127     public function initialize()
128     {
129         $this->_sign      = false;
130         $this->_encrypt   = false;
131         $this->secretkey  = null;
132         $this->passphrase = null;
133         $this->recipients = array();
134         $this->encryption = null;
135         $this->password   = null;
136     }
137     
138     /**
139      * Surcharge la méthode __toString() de la classe parente.
140      * Signe et/ou chiffre l’email résultant à l’aide du programme GnuPG.
141      *
142      * @access public
143      * @return string
144      */
145     public function __toString()
146     {
147         if( !is_executable($this->gpg_bin) ) {
148             throw new Exception(sprintf("[%s] is not a valid executable.", $this->gpg_bin));
149         }
150         
151         if( !((file_exists($this->logfile) && is_writable($this->logfile))
152             || is_writable(dirname($this->logfile))) )
153         {
154             throw new Exception(sprintf("Unable to write log file [%s].", $this->logfile));
155         }
156         
157         $tmpdir    = escapeshellarg($this->tmpdir);
158         $logfile   = escapeshellcmd($this->logfile);
159         $digest_algo = escapeshellarg($this->digest_algo);
160         $gpg_cmd   = escapeshellcmd($this->gpg_bin);
161         $gpg_cmd  .= " --no-verbose --no-tty --batch --textmode --armor"
162             . " --always-trust --comment \"Using GnuPG with Wamailer\"";
163         
164         if( !is_null($this->homedir) ) {
165             $gpg_cmd .= sprintf(" --homedir %s", escapeshellarg($this->homedir));
166         }
167         
168         /**
169          * Les emails signés et/ou chiffrés doivent être restreints
170          * aux caractères 7bit uniquement (us-ascii)
171          *
172          * @see RFC 3156#3 - Content-Transfer-Encoding restrictions
173          */
174         if( $this->_sign || $this->_encrypt ) {
175             if( !is_null($this->_textPart) ) {
176                 $this->_textPart->encoding = 'quoted-printable';
177             }
178             
179             if( !is_null($this->_htmlPart) ) {
180                 $this->_htmlPart->encoding = 'quoted-printable';
181             }
182         }
183         
184         parent::__toString();
185         
186         $headers = $this->headers_txt;
187         $message = $this->message_txt;
188         $tmpfile = tempnam($this->tmpdir, 'wa');
189         file_put_contents($tmpfile, $message);
190         
191         //
192         // Le message doit être chiffré (+ éventuellement signé)
193         //
194         if( $this->_encrypt ) {
195             if( !is_null($this->cipher_algo) ) {
196                 $gpc_cmd .= sprintf(' --cipher-algo %s', escapeshellarg($this->cipher_algo));
197             }
198             
199             if( $this->encryption == self::A_ENCRYPTION ) {
200                 if( count($this->recipients) == 0 ) {
201                     throw new Exception("No recipient specified!");
202                 }
203                 
204                 foreach( $this->recipients as $recipient ) {
205                     $gpg_cmd .= sprintf(' --recipient %s', escapeshellarg($recipient));
206                 }
207                 
208                 if( $this->_sign ) {
209                     if( !is_null($this->secretkey) ) {
210                         $gpg_cmd .= sprintf(" --local-user %s", escapeshellarg($this->secretkey));
211                     }
212                     
213                     if( !is_null($this->passphrase) ) {
214                         $gpg_cmd = sprintf("echo %s | %s --passphrase-fd 0",
215                             escapeshellarg($this->passphrase), $gpg_cmd);
216                     }
217                     
218                     $gpg_cmd = sprintf("%s --output - --digest-algo %s --encrypt --sign %s",
219                         $gpg_cmd, $digest_algo, escapeshellarg($tmpfile));
220                 }
221                 else {
222                     $gpg_cmd = sprintf("%s --output - --encrypt %s",
223                         $gpg_cmd, escapeshellarg($tmpfile));
224                 }
225             }
226             else {
227                 $password = escapeshellarg($this->password);
228                 if( $this->_sign ) {
229                     if( !is_null($this->secretkey) ) {
230                         $gpg_cmd .= sprintf(" --local-user %s", escapeshellarg($this->secretkey));
231                     }
232                     
233                     $gpg_cmd = sprintf("echo %s | %s --passphrase-fd 0"
234                         . " --output - --digest-algo %s --symmetric --sign %s",
235                         $password, $gpg_cmd, $digest_algo, escapeshellarg($tmpfile));
236                 }
237                 else {
238                     $gpg_cmd = sprintf("echo %s | %s --passphrase-fd 0"
239                         . " --output - --symmetric %s",
240                         $password, $gpg_cmd, escapeshellarg($tmpfile));
241                 }
242             }
243             
244             exec("$gpg_cmd 2>$logfile", $output, $result);
245             
246             if( $result !== 0 ) {
247                 unlink($tmpfile);
248                 throw new Exception("Cannot encrypt email (GPG error)");
249             }
250             
251             $encrypted_msg = implode("\r\n", $output);
252             
253             $gpg_sub1 = new Mime_Part();
254             $gpg_sub1->headers->set('Content-Type', 'application/pgp-encrypted');
255             $gpg_sub1->headers->set('Content-Description', 'PGP/MIME version identification');
256             $gpg_sub1->body = 'Version: 1';
257             
258             $gpg_sub2 = new Mime_Part();
259             $gpg_sub2->headers->set('Content-Type', 'application/octet-stream');
260             $gpg_sub2->headers->get('Content-Type')->param('name', 'encrypted.asc');
261             $gpg_sub2->headers->set('Content-Description', 'OpenPGP encrypted message');
262             $gpg_sub2->headers->set('Content-Disposition', 'inline');
263             $gpg_sub2->headers->get('Content-Disposition')->param('filename', 'encrypted.asc');
264             $gpg_sub2->body = $encrypted_msg;
265             $gpg_sub2->wraptext = false;
266             
267             //
268             // Bloc MIME global
269             //
270             $gpg = new Mime_Part();
271             $gpg->headers->set('Content-Type', 'multipart/encrypted');
272             $gpg->headers->get('Content-Type')->param('protocol', 'application/pgp-encrypted');
273             $gpg->body = 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)';
274             $gpg->addSubPart($gpg_sub1);
275             $gpg->addSubPart($gpg_sub2);
276             
277             $message = $gpg->__toString();
278         }
279         
280         //
281         // Le message doit être signé
282         //
283         else if( $this->_sign ) {
284             if( !is_null($this->secretkey) ) {
285                 $gpg_cmd .= sprintf(" --local-user %s", escapeshellarg($this->secretkey));
286             }
287             
288             if( !is_null($this->passphrase) ) {
289                 $gpg_cmd = sprintf("echo %s | %s --passphrase-fd 0",
290                     escapeshellarg($this->passphrase), $gpg_cmd);
291             }
292             
293             $gpg_cmd = sprintf("%s --output - --digest-algo %s --detach-sign %s",
294                 $gpg_cmd, $digest_algo, escapeshellarg($tmpfile));
295             exec("$gpg_cmd 2>$logfile", $output, $result);
296             
297             if( $result !== 0 ) {
298                 unlink($tmpfile);
299                 throw new Exception("Cannot sign email (GPG error)");
300             }
301             
302             $signature = implode("\r\n", $output);
303             
304             $gpg_sub = new Mime_Part();
305             $gpg_sub->headers->set('Content-Type', 'application/pgp-signature');
306             $gpg_sub->headers->get('Content-Type')->param('name', 'signature.asc');
307             $gpg_sub->headers->set('Content-Description', 'OpenPGP digital signature');
308             $gpg_sub->headers->set('Content-Disposition', 'attachment');
309             $gpg_sub->headers->get('Content-Disposition')->param('filename', 'signature.asc');
310             $gpg_sub->body = $signature;
311             $gpg_sub->wraptext = false;
312             
313             //
314             // Bloc MIME global
315             //
316             $gpg = new Mime_Part();
317             $gpg->headers->set('Content-Type', 'multipart/signed');
318             $gpg->headers->get('Content-Type')->param('micalg', "pgp-$this->digest_algo");
319             $gpg->headers->get('Content-Type')->param('protocol', 'application/pgp-signature');
320             $gpg->body = "This is an OpenPGP/MIME signed message (RFC 2440 and 3156).";
321             $gpg->addSubPart($message);
322             $gpg->addSubPart($gpg_sub);
323             
324             $message = $gpg->__toString();
325         }
326         
327         unlink($tmpfile);
328         
329         return $headers . $message;
330     }
331 }
332
333 ?>
334
Note: See TracBrowser for help on using the browser.