TYPO3  7.6
DomainKeySigner.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of SwiftMailer.
5  * (c) 2004-2009 Chris Corbyn
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10 
17 {
23  protected $_privateKey;
24 
30  protected $_domainName;
31 
37  protected $_selector;
38 
44  protected $_hashAlgorithm = 'rsa-sha1';
45 
51  protected $_canon = 'simple';
52 
58  protected $_ignoredHeaders = array();
59 
65  protected $_signerIdentity;
66 
72  protected $_debugHeaders = false;
73 
74  // work variables
80  private $_signedHeaders = array();
81 
87  protected $_domainKeyHeader;
88 
94  private $_hashHandler;
95 
96  private $_hash;
97 
98  private $_canonData = '';
99 
101 
103 
104  private $_bodyCanonSpace = false;
105 
106  private $_bodyCanonLastChar = null;
107 
108  private $_bodyCanonLine = '';
109 
110  private $_bound = array();
111 
119  public function __construct($privateKey, $domainName, $selector)
120  {
121  $this->_privateKey = $privateKey;
122  $this->_domainName = $domainName;
123  $this->_signerIdentity = '@'.$domainName;
124  $this->_selector = $selector;
125  }
126 
136  public static function newInstance($privateKey, $domainName, $selector)
137  {
138  return new static($privateKey, $domainName, $selector);
139  }
140 
146  public function reset()
147  {
148  $this->_hash = null;
149  $this->_hashHandler = null;
150  $this->_bodyCanonIgnoreStart = 2;
151  $this->_bodyCanonEmptyCounter = 0;
152  $this->_bodyCanonLastChar = null;
153  $this->_bodyCanonSpace = false;
154 
155  return $this;
156  }
157 
175  public function write($bytes)
176  {
177  $this->_canonicalizeBody($bytes);
178  foreach ($this->_bound as $is) {
179  $is->write($bytes);
180  }
181 
182  return $this;
183  }
184 
193  public function commit()
194  {
195  // Nothing to do
196  return $this;
197  }
198 
208  public function bind(Swift_InputByteStream $is)
209  {
210  // Don't have to mirror anything
211  $this->_bound[] = $is;
212 
213  return $this;
214  }
215 
226  public function unbind(Swift_InputByteStream $is)
227  {
228  // Don't have to mirror anything
229  foreach ($this->_bound as $k => $stream) {
230  if ($stream === $is) {
231  unset($this->_bound[$k]);
232 
233  return;
234  }
235  }
236 
237  return $this;
238  }
239 
248  public function flushBuffers()
249  {
250  $this->reset();
251 
252  return $this;
253  }
254 
262  public function setHashAlgorithm($hash)
263  {
264  $this->_hashAlgorithm = 'rsa-sha1';
265 
266  return $this;
267  }
268 
276  public function setCanon($canon)
277  {
278  if ($canon == 'nofws') {
279  $this->_canon = 'nofws';
280  } else {
281  $this->_canon = 'simple';
282  }
283 
284  return $this;
285  }
286 
294  public function setSignerIdentity($identity)
295  {
296  $this->_signerIdentity = $identity;
297 
298  return $this;
299  }
300 
308  public function setDebugHeaders($debug)
309  {
310  $this->_debugHeaders = (bool) $debug;
311 
312  return $this;
313  }
314 
318  public function startBody()
319  {
320  }
321 
325  public function endBody()
326  {
327  $this->_endOfBody();
328  }
329 
335  public function getAlteredHeaders()
336  {
337  if ($this->_debugHeaders) {
338  return array('DomainKey-Signature', 'X-DebugHash');
339  } else {
340  return array('DomainKey-Signature');
341  }
342  }
343 
351  public function ignoreHeader($header_name)
352  {
353  $this->_ignoredHeaders[strtolower($header_name)] = true;
354 
355  return $this;
356  }
357 
365  public function setHeaders(Swift_Mime_HeaderSet $headers)
366  {
367  $this->_startHash();
368  $this->_canonData = '';
369  // Loop through Headers
370  $listHeaders = $headers->listAll();
371  foreach ($listHeaders as $hName) {
372  // Check if we need to ignore Header
373  if (!isset($this->_ignoredHeaders[strtolower($hName)])) {
374  if ($headers->has($hName)) {
375  $tmp = $headers->getAll($hName);
376  foreach ($tmp as $header) {
377  if ($header->getFieldBody() != '') {
378  $this->_addHeader($header->toString());
379  $this->_signedHeaders[] = $header->getFieldName();
380  }
381  }
382  }
383  }
384  }
385  $this->_endOfHeaders();
386 
387  return $this;
388  }
389 
397  public function addSignature(Swift_Mime_HeaderSet $headers)
398  {
399  // Prepare the DomainKey-Signature Header
400  $params = array('a' => $this->_hashAlgorithm, 'b' => chunk_split(base64_encode($this->_getEncryptedHash()), 73, ' '), 'c' => $this->_canon, 'd' => $this->_domainName, 'h' => implode(': ', $this->_signedHeaders), 'q' => 'dns', 's' => $this->_selector);
401  $string = '';
402  foreach ($params as $k => $v) {
403  $string .= $k.'='.$v.'; ';
404  }
405  $string = trim($string);
406  $headers->addTextHeader('DomainKey-Signature', $string);
407 
408  return $this;
409  }
410 
411  /* Private helpers */
412 
413  protected function _addHeader($header)
414  {
415  switch ($this->_canon) {
416  case 'nofws' :
417  // Prepare Header and cascade
418  $exploded = explode(':', $header, 2);
419  $name = strtolower(trim($exploded[0]));
420  $value = str_replace("\r\n", '', $exploded[1]);
421  $value = preg_replace("/[ \t][ \t]+/", ' ', $value);
422  $header = $name.':'.trim($value)."\r\n";
423  case 'simple' :
424  // Nothing to do
425  }
426  $this->_addToHash($header);
427  }
428 
429  protected function _endOfHeaders()
430  {
431  $this->_bodyCanonEmptyCounter = 1;
432  }
433 
434  protected function _canonicalizeBody($string)
435  {
436  $len = strlen($string);
437  $canon = '';
438  $nofws = ($this->_canon == 'nofws');
439  for ($i = 0; $i < $len; ++$i) {
440  if ($this->_bodyCanonIgnoreStart > 0) {
442  continue;
443  }
444  switch ($string[$i]) {
445  case "\r" :
446  $this->_bodyCanonLastChar = "\r";
447  break;
448  case "\n" :
449  if ($this->_bodyCanonLastChar == "\r") {
450  if ($nofws) {
451  $this->_bodyCanonSpace = false;
452  }
453  if ($this->_bodyCanonLine == '') {
455  } else {
456  $this->_bodyCanonLine = '';
457  $canon .= "\r\n";
458  }
459  } else {
460  // Wooops Error
461  throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r');
462  }
463  break;
464  case ' ' :
465  case "\t" :
466  case "\x09": //HTAB
467  if ($nofws) {
468  $this->_bodyCanonSpace = true;
469  break;
470  }
471  default :
472  if ($this->_bodyCanonEmptyCounter > 0) {
473  $canon .= str_repeat("\r\n", $this->_bodyCanonEmptyCounter);
474  $this->_bodyCanonEmptyCounter = 0;
475  }
476  $this->_bodyCanonLine .= $string[$i];
477  $canon .= $string[$i];
478  }
479  }
480  $this->_addToHash($canon);
481  }
482 
483  protected function _endOfBody()
484  {
485  if (strlen($this->_bodyCanonLine) > 0) {
486  $this->_addToHash("\r\n");
487  }
488  $this->_hash = hash_final($this->_hashHandler, true);
489  }
490 
491  private function _addToHash($string)
492  {
493  $this->_canonData .= $string;
494  hash_update($this->_hashHandler, $string);
495  }
496 
497  private function _startHash()
498  {
499  // Init
500  switch ($this->_hashAlgorithm) {
501  case 'rsa-sha1' :
502  $this->_hashHandler = hash_init('sha1');
503  break;
504  }
505  $this->_canonLine = '';
506  }
507 
513  private function _getEncryptedHash()
514  {
515  $signature = '';
516  $pkeyId = openssl_get_privatekey($this->_privateKey);
517  if (!$pkeyId) {
518  throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']');
519  }
520  if (openssl_sign($this->_canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) {
521  return $signature;
522  }
523  throw new Swift_SwiftException('Unable to sign DomainKey Hash ['.openssl_error_string().']');
524  }
525 }