dkim signature for posts using mailDomainSigner class, plus some code to generate and store a key-pair

This commit is contained in:
Ralf Becker 2012-10-02 22:30:36 +00:00
parent 49f1d43ad1
commit 33510a2f4d
2 changed files with 89 additions and 10 deletions

View File

@ -55,6 +55,34 @@ class ischedule_client
{
$this->url = $url;
}
$this->dkim_private_key = $GLOBALS['egw_info']['server']['dkim_private_key'];
}
/**
* Generate private/public key pair
*
* Private and public key are stored in api config as dkim_private_key / dkim_public_key and loaded automatic by constructor.
*
* @return string public key
*/
public static function generateKeyPair()
{
// Create the keypair
$res = openssl_pkey_new();
// Get private key
openssl_pkey_export($res, $dkim_private_key);
// Get public key
$details = openssl_pkey_get_details($res);
$dkim_public_key = $details['key'];
// store both in config
config::save_value('dkim_private_key', $dkim_private_key, 'phpgwapi');
config::save_value('dkim_public_key', $dkim_public_key, 'phpgwapi');
return $dkim_public_key;
}
const EMAIL_PREG = '/^([a-z0-9][a-z0-9._-]*)?[a-z0-9]@([a-z0-9](|[a-z0-9_-]*[a-z0-9])\.)+[a-z]{2,6}$/i';
@ -131,11 +159,16 @@ class ischedule_client
*
* @param string $content
* @param string $content_type
* @param boolean $debug=false true echo request before posting
* @return string
* @throws Exception with http status code and message, if server responds other then 2xx
*/
public function post_msg($content, $content_type)
public function post_msg($content, $content_type, $debug=false)
{
if (empty($this->dkim_private_key))
{
throw new Exception('You need to generate a key pair first!');
}
$url_parts = parse_url($this->url);
$headers = array(
'Host' => $url_parts['host'].($url_parts['port'] ? ':'.$url_parts['port'] : ''),
@ -145,12 +178,13 @@ class ischedule_client
'Recipient' => $this->recipient,
'Content-Length' => bytes($content),
);
$headers['DKIM-Signature'] = $this->dkim_sign($headers, $content);
$header_string = '';
foreach($headers as $name => $value)
{
$header_string .= $name.': '.$value."\r\n";
}
$header_string .= $this->dkim_sign($headers, $content)."\r\n";
$opts = array('http' =>
array(
'method' => 'POST',
@ -160,6 +194,8 @@ class ischedule_client
)
);
if ($debug) echo "POST $this->url HTTP/1.1\n$header_string\n$content\n";
// need to suppress warning, if http-status not 2xx
if (($response = @file_get_contents($this->url, false, stream_context_create($opts))) === false)
{
@ -172,13 +208,29 @@ class ischedule_client
/**
* Calculate DKIM signature for headers and body using originators domains private key
*
* @param array $headers
* @param array $headers name => value pairs, names as in $sign_headers
* @param string $body
* @param string $type dkim-type
* @param string $selector='calendar'
* @param string $sign_headers='Content-Type:Host:Originator:Recipient'
* @return string DKIM-Signature: ...
*/
public function dkim_sign(array $headers, $body, $type='calendar')
public function dkim_sign(array $headers, $body, $selector='calendar', $sign_headers='Content-Type:Host:Originator:Recipient')
{
return 'dummy';
include_once EGW_API_INC.'/php-mail-domain-signer/lib/class.mailDomainSigner.php';
list(,$domain) = explode('@', $this->originator);
$mds = new mailDomainSigner($this->dkim_private_key, $domain, $selector);
$dkim_headers = array();
foreach(explode(':', $sign_headers) as $header)
{
$dkim_headers[] = $header.': '.$headers[$header];
}
$dkim = $mds->getDKIM(strtolower($sign_headers), $dkim_headers, $body);
// as we do http, no need to fold dkim, in fact recommendation is not to
$dkim = str_replace(array(";\r\n\t", "\r\n\t"), array('; ', ''), $dkim);
return $dkim;
}
/**

View File

@ -21,7 +21,15 @@ if (isset($_SERVER['HTTP_HOST'])) die("This is a commandline ONLY tool!\n");
*/
function usage($err=null)
{
echo basename(__FILE__).": [--url ischedule-url] [--component (VEVENT|VFREEBUSY|VTODO) (-|ical-filename)] [--method (REQUEST(default)|RESPONSE)] recipient-email [originator-email]\n\n";
echo "\nUsage: ".basename(__FILE__).": [options] recipient-email [originator-email]\n\n";
echo "available options:\n\n";
echo "\t--url ischedule-url\n";
echo "\t--component (VEVENT|VFREEBUSY|VTODO) (-|ical-filename)\n";
echo "\t--method (REQUEST(default)|REPLY|CANCEL|ADD)\n";
echo "\t--generate-key-pair : generates and stores a new key pair\n";
echo "\t--public-key : outputs public key\n";
echo "\t-v|--verbose output posted message too\n";
echo "\n";
if ($err) echo "$err\n\n";
exit;
}
@ -42,10 +50,10 @@ $method = 'REQUEST';
while($args[0][0] == '-')
{
$option = array_shift($args);
if (count($args) < 2) usage("Missing arguments for '$option'!".array2string($args));
switch($option)
{
case '--url':
if (count($args) < 2) usage("Missing arguments for '$option'!");
$url = array_shift($args);
break;
@ -64,6 +72,7 @@ while($args[0][0] == '-')
break;
case '--method':
if (count($args) < 2) usage("Missing arguments for '$option'!");
$method = strtoupper(array_shift($args));
if (!in_array($method, array('REQUEST','REPLY','CANCEL','ADD')))
{
@ -71,23 +80,41 @@ while($args[0][0] == '-')
}
break;
case '-v':
case '--verbose':
$verbose = true;
break;
case '--generate-key-pair':
$GLOBALS['egw_info']['server']['dkim_public_key'] = ischedule_client::generateKeyPair();
echo "\nKey pair generated\n";
// fall through
case '--public-key':
if (empty($GLOBALS['egw_info']['server']['dkim_public_key'])) die("\nYou need to generate a key pair first!\n\n");
echo "\nYou need following DNS record:\n";
$public_key = preg_replace('/([-]+(BEGIN|END) PUBLIC KEY[-]+|\s*)/m', '', $GLOBALS['egw_info']['server']['dkim_public_key']);
echo "\ncalendar._domainkey IN TXT \"v=DKIM1;k=rsa;h=sha1;s=calendar;t=s;p=$public_key\"\n\n";
exit;
default:
usage("Unknown option '$option'!");
}
}
if (!count($args)) usage();
if (!count($args) && !($public_key || $generate_key_pair)) usage();
$recipient = array_shift($args);
if ($args) $originator = array_shift($args);
try {
$client = new ischedule_client($recipient, $url);
echo "\nUsing iSchedule URL: $client->url\n\n";
if ($originator) $client->setOriginator($originator);
if ($component)
{
$content_type = 'text/calendar; component='.$component.'; method='.$method;
echo $client->post_msg($content, $content_type);
$response = $client->post_msg($content, $content_type, $verbose);
echo $response;
}
else
{