mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 16:48:49 +01:00
import of orginal version of PHP mail domain signer, required in enhanced form for iSchedule
This commit is contained in:
parent
f40a292865
commit
c8f1733634
452
phpgwapi/inc/class.mailDomainSigner.inc.php
Normal file
452
phpgwapi/inc/class.mailDomainSigner.inc.php
Normal file
@ -0,0 +1,452 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2011 Ahmad Amarullah
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* mailDomainSigner - PHP class for Add DKIM-Signature and DomainKey-Signature on your mail
|
||||
*
|
||||
* Requirement: >= PHP 5.3.0
|
||||
* For lower version, find "#^--for ver < PHP5.3" comment
|
||||
*
|
||||
* @link http://code.google.com/p/php-mail-domain-signer/
|
||||
*
|
||||
* @package mailDomainSigner
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
class mailDomainSigner{
|
||||
|
||||
///////////////////////
|
||||
// PRIVATE VARIABLES //
|
||||
///////////////////////
|
||||
private $pkid=null;
|
||||
private $s;
|
||||
private $d;
|
||||
|
||||
//////////////////////
|
||||
// AGENT PROPERTIES //
|
||||
//////////////////////
|
||||
private $__app_name = "PHP mailDomainSigner";
|
||||
private $__app_ver = "0.1-20110129";
|
||||
private $__app_url = "http://code.google.com/p/php-mail-domain-signer/";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $private_key Raw Private Key to Sign the mail
|
||||
* @param string $d The domain name of the signing domain
|
||||
* @param string $s The selector used to form the query for the public key
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function __construct($private_key,$d,$s){
|
||||
// Get a private key
|
||||
$this->pkid = openssl_pkey_get_private($private_key);
|
||||
|
||||
// Save Domain and Selector
|
||||
$this->d = $d;
|
||||
$this->s = $s;
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// PRIVATE FUNCTIONS //
|
||||
///////////////////////
|
||||
|
||||
/**
|
||||
* The nofws ("No Folding Whitespace") Canonicalization Algorithm
|
||||
* Function implementation according to RFC4870
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc4870#page-19
|
||||
* @param array $raw_headers Array of Mail Headers
|
||||
* @param string $raw_body Raw Mail body
|
||||
* @return string nofws Canonicalizated data
|
||||
* @access public
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function nofws($raw_headers,$raw_body){
|
||||
// nofws-ed headers
|
||||
$headers = array();
|
||||
|
||||
// Loop the raw_headers
|
||||
foreach ($raw_headers as $header){
|
||||
// Replace all Folding Whitespace
|
||||
$headers[] = preg_replace('/[\r\t\n ]++/','',$header);
|
||||
}
|
||||
|
||||
// Join headers with LF then Add it into data
|
||||
$data = implode("\n",$headers)."\n";
|
||||
|
||||
// Loop Body Lines
|
||||
foreach(explode("\n","\n".str_replace("\r","",$raw_body)) as $line)
|
||||
{
|
||||
// Replace all Folding Whitespace from current line
|
||||
// then Add it into data
|
||||
$data .= preg_replace('/[\t\n ]++/','',$line)."\n";
|
||||
}
|
||||
|
||||
// Remove Trailing empty lines then split it with LF
|
||||
$data = explode("\n",rtrim($data,"\n"));
|
||||
|
||||
// Join array of data with CRLF and Append CRLF
|
||||
// to the resulting line
|
||||
$data = implode("\r\n",$data)."\r\n";
|
||||
|
||||
// Return Canonicalizated Data
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "relaxed" Header Canonicalization Algorithm
|
||||
* Function implementation according to RFC4871
|
||||
*
|
||||
* Originally taken from RelaxedHeaderCanonicalization
|
||||
* function in PHP-DKIM by Eric Vyncke
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc4871#page-14
|
||||
* @link http://php-dkim.sourceforge.net/
|
||||
*
|
||||
* @param string $s Header String to Canonicalization
|
||||
* @return string Relaxed Header Canonicalizated data
|
||||
* @access public
|
||||
* @author Eric Vyncke
|
||||
*/
|
||||
public function headRelaxCanon($s) {
|
||||
// Replace CR,LF and spaces into single SP
|
||||
$s=preg_replace("/\r\n\s+/"," ",$s) ;
|
||||
|
||||
// Explode Header Line
|
||||
$lines=explode("\r\n",$s) ;
|
||||
|
||||
// Loop the lines
|
||||
foreach ($lines as $key=>$line) {
|
||||
// Split the key and value
|
||||
list($heading,$value)=explode(":",$line,2) ;
|
||||
|
||||
// Lowercase heading key
|
||||
$heading=strtolower($heading);
|
||||
|
||||
// Compress useless spaces
|
||||
$value=preg_replace("/\s+/"," ",$value);
|
||||
|
||||
// Don't forget to remove WSP around the value
|
||||
$lines[$key]=$heading.":".trim($value);
|
||||
}
|
||||
|
||||
// Implode it again
|
||||
$s=implode("\r\n",$lines);
|
||||
|
||||
// Return Canonicalizated Headers
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "relaxed" Body Canonicalization Algorithm
|
||||
* Function implementation according to RFC4871
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc4871#page-15
|
||||
*
|
||||
* @param string $body Body String to Canonicalization
|
||||
* @return string Relaxed Body Canonicalizated data
|
||||
* @access public
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function bodyRelaxCanon($body) {
|
||||
// Return CRLF for empty body
|
||||
if ($body == ''){
|
||||
return "\r\n";
|
||||
}
|
||||
|
||||
// Replace all CRLF to LF
|
||||
$body = str_replace("\r\n","\n",$body);
|
||||
|
||||
// Replace LF to CRLF
|
||||
$body = str_replace("\n","\r\n",$body);
|
||||
|
||||
// Ignores all whitespace at the end of lines
|
||||
$body=rtrim($body,"\r\n");
|
||||
|
||||
// Canonicalizated String Variable
|
||||
$canon_body = '';
|
||||
|
||||
// Split the body into lines
|
||||
foreach(explode("\r\n",$body) as $line){
|
||||
// Reduces all sequences of White Space within a line
|
||||
// to a single SP character
|
||||
$canon_body.= rtrim(preg_replace('/[\t\n ]++/',' ',$line))."\r\n";
|
||||
}
|
||||
|
||||
// Return the Canonicalizated Body
|
||||
return $canon_body;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PUBLIC FUNCTIONS //
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
* DKIM-Signature Header Creator Function
|
||||
* implementation according to RFC4871
|
||||
*
|
||||
* Originally code inspired by AddDKIM
|
||||
* function in PHP-DKIM by Eric Vyncke
|
||||
* And rewrite it for better result
|
||||
*
|
||||
* The function use relaxed/relaxed canonicalization alghoritm
|
||||
* for better verifing validation
|
||||
*
|
||||
* different from original PHP-DKIM that used relaxed/simple
|
||||
* canonicalization alghoritm
|
||||
*
|
||||
* Doesn't include z, i and q tag for smaller data because
|
||||
* it doesn't really needed
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc4871
|
||||
* @link http://php-dkim.sourceforge.net/
|
||||
*
|
||||
* @param string $h Signed header fields, A colon-separated list of header field names that identify the header fields presented to the signing algorithm
|
||||
* @param array $_h Array of headers in same order with $h (Signed header fields)
|
||||
* @param string $body Raw Email Body String
|
||||
* @return string DKIM-Signature Header String
|
||||
* @access public
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function getDKIM($h,$_h,$body) {
|
||||
|
||||
// Relax Canonicalization for Body
|
||||
$_b = $this->bodyRelaxCanon($body);
|
||||
|
||||
// Canonicalizated Body Length [tag:l]
|
||||
$_l = strlen($_b);
|
||||
|
||||
// Signature Timestamp [tag:t]
|
||||
$_t = time();
|
||||
|
||||
// Hash of the canonicalized body [tag:bh]
|
||||
$_bh= base64_encode(sha1($_b,true));
|
||||
#^--for ver < PHP5.3 # $_bh= base64_encode(pack("H*",sha1($_b)));
|
||||
|
||||
// Creating DKIM-Signature
|
||||
$_dkim = "DKIM-Signature: ".
|
||||
"v=1; ". // DKIM Version
|
||||
"a=rsa-sha1; ". // The algorithm used to generate the signature "rsa-sha1"
|
||||
"s={$this->s}; ". // The selector subdividing the namespace for the "d=" (domain) tag
|
||||
"d={$this->d}; ". // The domain of the signing entity
|
||||
"l={$_l}; ". // Canonicalizated Body length count
|
||||
"t={$_t}; ". // Signature Timestamp
|
||||
"c=relaxed/relaxed; ". // Message (Headers/Body) Canonicalization "relaxed/relaxed"
|
||||
"h={$h}; ". // Signed header fields
|
||||
"bh={$_bh};\r\n\t". // The hash of the canonicalized body part of the message
|
||||
"b="; // The signature data (Empty because we will calculate it later)
|
||||
|
||||
// Wrap DKIM Header
|
||||
$_dkim = wordwrap($_dkim,76,"\r\n\t");
|
||||
|
||||
// Canonicalization Header Data
|
||||
$_unsigned = $this->headRelaxCanon(implode("\r\n",$_h)."\r\n{$_dkim}");
|
||||
|
||||
// Sign Canonicalization Header Data with Private Key
|
||||
openssl_sign($_unsigned, $_signed, $this->pkid, OPENSSL_ALGO_SHA1);
|
||||
|
||||
// Base64 encoded signed data
|
||||
// Chunk Split it
|
||||
// Then Append it $_dkim
|
||||
$_dkim .= chunk_split(base64_encode($_signed),76,"\r\n\t");
|
||||
|
||||
// Return trimmed $_dkim
|
||||
return trim($_dkim);
|
||||
}
|
||||
|
||||
/**
|
||||
* DomainKey-Signature Header Creator Function
|
||||
* implementation according to RFC4870
|
||||
*
|
||||
* The function use nofws canonicalization alghoritm
|
||||
* for better verifing validation
|
||||
*
|
||||
* NOTE: the $h and $_h arguments must be in right order
|
||||
* if to header location upper the from header
|
||||
* it should ordered like "to:from", don't randomize
|
||||
* the order for better validating result.
|
||||
*
|
||||
* NOTE: if your DNS TXT contained g=*, remove it
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc4870
|
||||
*
|
||||
* @param string $h Signed header fields, A colon-separated list of header field names that identify the header fields presented to the signing algorithm
|
||||
* @param array $_h Array of headers in same order with $h (Signed header fields)
|
||||
* @param string $body Raw Email Body String
|
||||
* @return string DomainKey-Signature Header String
|
||||
* @access public
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function getDomainKey($h,$_h,$body){
|
||||
// If $h = empty, dont add h tag into DomainKey-Signature
|
||||
$hval = '';
|
||||
if ($h)
|
||||
$hval= "h={$h}; ";
|
||||
|
||||
// Creating DomainKey-Signature
|
||||
$_dk = "DomainKey-Signature: ".
|
||||
"a=rsa-sha1; ". // The algorithm used to generate the signature "rsa-sha1"
|
||||
"c=nofws; ". // Canonicalization Alghoritm "nofws"
|
||||
"d={$this->d}; ". // The domain of the signing entity
|
||||
"s={$this->s}; ". // The selector subdividing the namespace for the "d=" (domain) tag
|
||||
"{$hval}"; // If Exists - Signed header fields
|
||||
|
||||
// nofws Canonicalization for headers and body data
|
||||
$_unsigned = $this->nofws($_h,$body);
|
||||
|
||||
// Sign nofws Canonicalizated Data with Private Key
|
||||
openssl_sign($_unsigned, $_signed, $this->pkid, OPENSSL_ALGO_SHA1);
|
||||
|
||||
// Base64 encoded signed data
|
||||
// Chunk Split it
|
||||
$b = chunk_split(base64_encode($_signed),76,"\r\n\t");
|
||||
|
||||
// Append sign data into b tag in $_dk
|
||||
$_dk.="b={$b}";
|
||||
|
||||
// Return Wrapped and trimmed $_dk
|
||||
return trim(wordwrap($_dk,76,"\r\n\t"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto Sign RAW Mail Data with DKIM-Signature
|
||||
* and DomailKey-Signature
|
||||
*
|
||||
* It Support auto positioning Signed header fields
|
||||
*
|
||||
* @param string $mail_data Raw Mail Data to be signed
|
||||
* @param string $suggested_h Suggested Signed Header Fields, separated by colon ":"
|
||||
* Default: string("from:to:subject")
|
||||
* @param bool $create_dkim If true, it will generate DKIM-Signature for $mail_data
|
||||
* Default: boolean(true)
|
||||
* @param bool $create_domainkey If true, it will generate DomailKey-Signature for $mail_data
|
||||
* Default: boolean(true)
|
||||
* @param integer $out_sign_header_only If true or 1, it will only return signature headers as String
|
||||
* If 2, it will only return signature headers as Array
|
||||
* If false or 0, it will return signature headers with original mail data as String
|
||||
* Default: boolean(false)
|
||||
* @return mixed Signature Headers with/without original data as String/Array depended on $out_sign_header_only parameter
|
||||
* @access public
|
||||
* @author Ahmad Amarullah
|
||||
*/
|
||||
public function sign(
|
||||
$mail_data, // Raw Mail Data
|
||||
$suggested_h = "from:to:subject", // Suggested Signed Header Fields
|
||||
$create_dkim = true, // Create DKIM-Signature Header
|
||||
$create_domainkey = true, // Create DomainKey-Signature Header
|
||||
$out_sign_header_only = false // Return Signature Header Only without original data
|
||||
){
|
||||
|
||||
if (!$suggested_h) $suggested_h = "from:to:subject"; // Default Suggested Signed Header Fields
|
||||
|
||||
// Remove all space and Lowercase Suggested Signed header fields then split it into array
|
||||
$_h = explode(":",strtolower(preg_replace('/[\r\t\n ]++/','',$suggested_h)));
|
||||
|
||||
// Split Raw Mail data into $raw_headers and $body
|
||||
list($raw_headers, $body) = explode("\r\n\r\n",$mail_data,2);
|
||||
|
||||
// Explode $raw_header into $header_list
|
||||
$header_list = preg_split("/\r\n(?![\t ])/", $raw_headers);
|
||||
|
||||
// Empty Header Array
|
||||
$headers = array();
|
||||
|
||||
// Loop $header_list
|
||||
foreach($header_list as $header){
|
||||
// Find Header Key for Array Key
|
||||
list($key) = explode(':',$header, 2);
|
||||
|
||||
// Trim and Lowercase It
|
||||
$key = strtolower(trim($key));
|
||||
|
||||
// If header with current key was exists
|
||||
// Change it into array
|
||||
if (isset($headers[$key])){
|
||||
// If header not yet array set as Array
|
||||
if (!is_array($headers[$key]))
|
||||
$headers[$key] = array($headers[$key]);
|
||||
|
||||
// Add Current Header as next element
|
||||
$headers[$key][] = $header;
|
||||
}
|
||||
|
||||
// If header with current key not exists
|
||||
// Insert header as string
|
||||
else{
|
||||
$headers[$key] = $header;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, lets find accepted Suggested Signed header fields
|
||||
// and reorder it to match headers position
|
||||
|
||||
$accepted_h = array(); // For Accepted Signed header fields
|
||||
$accepted_headers = array(); // For Accepted Header
|
||||
|
||||
// Loop the Headers Array
|
||||
foreach ($headers as $key=>$val){
|
||||
// Check if $val wasn't array
|
||||
// We don't want to include multiple headers as Signed header fields
|
||||
if (!is_array($val)){
|
||||
// Check if this header exists in Suggested Signed header fields
|
||||
if (in_array($key,$_h)){
|
||||
// If Exists, add it into accepted headers and accepted header fields
|
||||
$accepted_h[] = $key;
|
||||
$accepted_headers[] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it doesn't contain any $accepted_h
|
||||
// return false, because we don't have enough data
|
||||
// for signing email
|
||||
if (count($accepted_h)==0)
|
||||
return false;
|
||||
|
||||
// Create $_hdata for Signed header fields
|
||||
// by imploding it with colon
|
||||
$_hdata = implode(":",$accepted_h);
|
||||
|
||||
// New Headers Variable
|
||||
$_nh = array("x-domain-signer"=>"X-Domain-Signer: {$this->__app_name} {$this->__app_ver} <$this->__app_url>");
|
||||
|
||||
// Create DKIM First
|
||||
if ($create_dkim)
|
||||
$_nh['dkim-signature'] = $this->getDKIM($_hdata,$accepted_headers,$body);
|
||||
|
||||
// Now Create Domain-Signature
|
||||
if ($create_domainkey)
|
||||
$_nh['domainKey-signature'] = $this->getDomainKey($_hdata,$accepted_headers,$body);
|
||||
|
||||
// Implode $_nh with \r\n
|
||||
$to_be_appended_headers = implode("\r\n",$_nh);
|
||||
|
||||
// Return Immediately if
|
||||
// * $out_sign_header_only=true (as headers string)
|
||||
// * $out_sign_header_only=2 (as headers array)
|
||||
if ($out_sign_header_only===2)
|
||||
return $_nh;
|
||||
elseif ($out_sign_header_only)
|
||||
return "{$to_be_appended_headers}\r\n";
|
||||
|
||||
// Return signed headers with original data
|
||||
return "{$to_be_appended_headers}\r\n{$mail_data}";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Loading…
Reference in New Issue
Block a user