Welcome Guest, Not a member yet? Register   Sign In
HMAC Helper
#1

[eluser]stensi[/eluser]
What's HMAC (Hash Message Authentication Code)?

In short, it's basically a specific algorithm, such as MD5 or SHA1, used in combination with a hash function and a private key. It is used for verifying the data integrity and authenticity of a message or request.

How's it work?

A relevant example would be using HMAC in your requests to Amazon S3.

When you sign up to use Amazon S3, they provide you with a public key and a private key. Only you and Amazon know what your private key is. Basically you can think of the public key as a username and the private key as a password.

When making a request with Amazon S3, you include your public key within it and your HMAC. The HMAC is created by performing a hash on the data in your request using your private key.

Upon receiving your request, Amazon S3 will go through the same process to authenticate your request. They use your public key to lookup your private key in their database. They then perform a hash on the data in your request using your private key. They compere the resulting HMAC with the HMAC you supplied in your request. If they match, the request is deemed as authentic!

So, the private key is never sent across the internet giving no chance of it being intercepted. The longer the private key, the more secure the HMAC.

Why make an HMAC Helper?

The main reason I made it was because most current Amazon S3 libraries require that you use either the Crypt HMAC package from the PEAR library or PHP5's hash_hmac function. I prefer not to add the bloat of PEAR or rely on it so sticking with CodeIgniter's ideology of maintaining backwards compatibility, my HMAC helper will work in both PHP4 and PHP5.

HMAC Helper

Code:
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* HMAC helper
*
* $signature = hex2b64(hmac_sha1($key, $data));
*
* @package        stensi
* @category    Helpers
* @author        stensi
* @link        http://stensi.com
*/

// ------------------------------------------------------------------------

/**
* HMAC
*
* Calculate HMAC according to RFC 2104, for chosen algorithm.
* http://www.ietf.org/rfc/rfc2104.txt
*
* @access    public
* @param    string    hash algorithm
* @param    string    key to sign hash with
* @param    string    data to be hashed
* @return    string
*/
if (!function_exists('hmac'))
{
    function hmac($hashfunc, $key, $data)
    {
        $blocksize=64;

        if (strlen($key) > $blocksize)
        {
            $key = pack('H*', $hashfunc($key));
        }

        $key = str_pad($key, $blocksize, chr(0x00));
        $ipad = str_repeat(chr(0x36), $blocksize);
        $opad = str_repeat(chr(0x5c), $blocksize);
        $hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data))));

        return bin2hex($hmac);
    }
}

// ------------------------------------------------------------------------

/**
* HMAC-SHA1
*
* Calculate HMAC-SHA1 according to RFC 2104.
* http://www.ietf.org/rfc/rfc2104.txt
*
* @access    public
* @param    string    key to sign hash with
* @param    string    data to be hashed
* @return    string
*/
if (!function_exists('hmac_sha1'))
{
    function hmac_sha1($key, $data)
    {
        return hmac('sha1', $key, $data);
    }
}

// ------------------------------------------------------------------------

/**
* HMAC-MD5
*
* Calculate HMAC-MD5 according to RFC 2104.
* http://www.ietf.org/rfc/rfc2104.txt
*
* @access    public
* @param    string    key to sign hash with
* @param    string    data to be hashed
* @return    string
*/
if (!function_exists('hmac_md5'))
{
    function hmac_md5($key, $data)
    {
        return hmac('md5', $key, $data);
    }
}

// ------------------------------------------------------------------------

/**
* Hex to Base64
*
* Convert hex to base64.
*
* @access    public
* @param    string
* @return    string
*/
if (!function_exists('hex2b64'))
{
    function hex2b64($str)
    {
        $raw = '';

        for ($i = 0; $i < strlen($str); $i += 2)
        {
            $raw .= chr(hexdec(substr($str, $i, 2)));
        }

        return base64_encode($raw);
    }
}

/* End of file hmac_helper.php */
/* Location: ./application/helpers/hmac_helper.php */
#2

[eluser]stensi[/eluser]
Example

I've taken the XML-RPC client and server examples from the Code Igniter User Guide and modified them to include the use of HMAC signatures to authenticate requests with.

XMLRPC Client
Code:
&lt;?php

class Xmlrpc_client extends Controller {
    
    function index()
    {    
        $this->load->helper('url');
        $server_url = site_url('xmlrpc_server');

        $this->load->library('xmlrpc');
        
        $this->xmlrpc->server($server_url, 80);
        $this->xmlrpc->method('Greetings');
        

        // Load the HMAC helper
        $this->load->helper('hmac');

        // Your Public and Private Keys
        $public_key = "my public key";
        $private_key = "my private key";

        // Mock data to put in the request
        $name = "stensi";
        $foo = "bar";
        $test = TRUE;

        // Data that we will use for creating the HMAC signature
        // (must be the same data that the server will use)
        $stringToSign = $public_key . $name . $foo . $test;

        // Create the HMAC signature and convert the resulting hex hash into base64
        $signature = hex2b64(hmac_sha1($private_key, $stringToSign));

        // Create the request object, including your public_key and HMAC signature
        $request = array(
            array(
                // param 0
                array(
                    'public_key' => array($public_key, 'string'),
                    'signature'=> array($signature, 'string'),
                    'name' => array($name, 'string'),
                    'foo' => array($foo, 'string'),
                    'test' => array($test, 'boolean')
                ),
            'struct'
            ),
        'struct'
        );

        $this->xmlrpc->request($request);    
        
        if ( ! $this->xmlrpc->send_request())
        {
            echo $this->xmlrpc->display_error();
        }
        else
        {
            echo '<h1>XML-RPC with HMAC signature</h1>';
            echo '<h2>Client request</h2>';
            echo '<pre>';
            print_r($request);
            echo '</pre>';

            echo '<h2>Server response</h2>';
            echo '<pre>';
            print_r($this->xmlrpc->display_response());
            echo '</pre>';
        }
    }
}

/* End of file xmlrpc_client.php */
/* Location: ./application/controllers/xmlrpc_client.php */

XMLRPC Server
Code:
&lt;?php

class Xmlrpc_server extends Controller {

    function index()
    {
        $this->load->library('xmlrpc');
        $this->load->library('xmlrpcs');
        
        $config['functions']['Greetings'] = array('function' => 'Xmlrpc_server.process');
        
        $this->xmlrpcs->initialize($config);
        $this->xmlrpcs->serve();
    }
    
    
    function process($request)
    {
        // Get the request parameters
        $parameters = $request->output_parameters();

        $public_key = $parameters['0']['public_key'];
        $signature = $parameters['0']['signature'];
        $name = $parameters['0']['name'];
        $foo = $parameters['0']['foo'];
        $test = $parameters['0']['test'];

        // Load the HMAC helper
        $this->load->helper('hmac');

        // Get the corresponding private key from the database, for the given public key.
        // For now, we'll just put it straight in.
        $private_key = "my private key";
        
        // Data that we will use for creating the HMAC signature
        $stringToSign = $public_key . $name . $foo . $test;

        // Create the HMAC signature and convert the resulting hex hash into base64
        $server_signature = hex2b64(hmac_sha1($private_key, $stringToSign));

        // Compare the server side HMAC signature with the request HMAC signature
        if ($server_signature === $signature)
        {
            // Valid request
            $response = array (
                array(
                    'message' => array('Valid signature', 'string'),
                ),
            'struct'
            );
        }
        else
        {
            // Invalid request
            // Return an error response
            $response = array (
                array(
                    'message' => array('Invalid signature', 'string'),
                ),
            'struct'
            );
        }
                        
        return $this->xmlrpc->send_response($response);
    }
}

/* End of file xmlrpc_server.php */
/* Location: ./application/controllers/xmlrpc_server.php */

In the actual XML-RPC client requests I use for my sites, I put the public key and HMAC signature in the Basic HTTP Authorization header. This gives better separation of the authentication from the content of the request. I made a drop in replacement of CodeIgniters XMLRPC controller to allow this. If anyone wants it let me know and I'll post it. For now I'll leave it out since this post is long enough already.

I pass the public key and HMAC signature like so:

Code:
$server_url = 'http://' . $public_key . ':' . $signature . '@domain.com/xmlrpc_server');
$this->xmlrpc->server($server_url, 80);

Which is then accessible in the server in this way:

Code:
$public_key = $_SERVER['PHP_AUTH_USER'];
$signature = $_SERVER['PHP_AUTH_PW'];

Anyway, I hope someone finds this useful. I know I would have a day ago! Wink
#3

[eluser]carnalito[/eluser]
Hi Stansi,

you saved my day. As far as i searched the forum, your solution to authenticate via xmlrpc-lib is the best of all (maybe the only one?!)

I will try it and would be happy to get some help from your side (as i need it).

Thanks again for posting your code.

Regards

Carnalito
#4

[eluser]carnalito[/eluser]
Hi Stansi,

done testing and i like it. One question is how to do the "xml-rpc-communication" from point of successful validation?

Is providing a session-id the way to go?

or

validating the keys on every request?

Thanks.

Regards

Carnalito




Theme © iAndrew 2016 - Forum software by © MyBB