XT Knowledge Base
Hauptseite | Über | Hilfe | FAQ | Spezialseiten | Anmelden

Druckversion | Impressum | Datenschutz | Aktuelle Version

Amzn PHP API

(Unterschied zwischen Versionen)

(Die Seite wurde neu angelegt: „<pre> <? define( 'SHA256_DIGEST_LENGTH', 32 ); class nanoSha2 { // php 4 - 5 compatible class properties var $toUpper; var $platform; …“)
 
(Der Versionsvergleich bezieht 1 dazwischenliegende Version mit ein.)
Zeile 1: Zeile 1:
 +
Den nachfolgenden Code habe ich aus verschiedenen Quellen im Internet zusammengetragen, angepasst und integriert.
 +
 +
Das Hauptproblem besteht darin, dass auf den 1&1-Servern PEAR, PECL und das PECL-Module 'crack' nicht installiert ist und es mir nicht gelungen ist, das selbst zu installieren. Das Modul 'crack' erzeugt bei der Installation einen Fehler.
 +
 +
Mit PEAR/Pecl und crack hätte ich einfach das [http://de2.php.net/manual/en/book.hash.php HASH Message Digest Framework] von PHP verwenden und mir einige Stunden Arbeit sparen können.
 +
 +
Deshalb habe ich den von Amazon benötigten HMAC-SHA256-Algorithmus selbst implementiert.
 +
 +
Den SHA256-Algorithmus habe ich von [http://www.nanolink.ca/pub/sha256/sha256.inc.txt hier]
 +
Er stammt von Perry McGee. Das Script kann [http://www.nanolink.ca/pub/sha256 auf dieser Seite] getestet werden.
 +
 +
Der HMAC-Algorithmus stammt von KC Cloyd [http://php.net/manual/en/function.hash-hmac.php]
 +
 +
Wie man sich überhaupt erfolgreich bei Amazon für das API registriert, erfährt man auf dieser sehr nützlichen Seite: [http://www.zerbit.de/artikel/amazon_e_commerce_service_nutzen.aspx Amazon E-Commerce-Service nutzen - Lieblingslisten per Amazon-API auf die eigene Website bringen]
 +
 +
Für die Implementierung des API-Aufrufs war diese Seite nützlich, die den generellen Vorgang beschreibt: [http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/rest-signature.html Example REST Requests]
 +
 +
Am meisten Freude bereitet hat mir die Anweisung '''Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm'''. Für den
 +
Aufwand, den man da betreiben muss, um überhaupt einen Request abschicken zu können, sind die Ergebnisse sehr dürftig. Viele Bücher
 +
werden aufgrund ihrer ISBN nicht gefunden, selbst wenn man diese direkt von der Amazon-Webseite kopiert. Wenn etwas gefunden wird, sind die gelieferten Informationen oft sehr unvollständig und verwirrend. Die Qualität der Product Advertising API ist sehr enttäuschend und für ernsthafte Anwendungen erstmal nicht zu gebrauchen.
<pre>
<pre>
&lt;?
&lt;?
Zeile 5: Zeile 25:
class nanoSha2
class nanoSha2
{
{
-
    // php 4 - 5 compatible class properties
 
     var    $toUpper;
     var    $toUpper;
     var    $platform;
     var    $platform;

Aktuelle Version vom 16:33, 12. Aug. 2010

Den nachfolgenden Code habe ich aus verschiedenen Quellen im Internet zusammengetragen, angepasst und integriert.

Das Hauptproblem besteht darin, dass auf den 1&1-Servern PEAR, PECL und das PECL-Module 'crack' nicht installiert ist und es mir nicht gelungen ist, das selbst zu installieren. Das Modul 'crack' erzeugt bei der Installation einen Fehler.

Mit PEAR/Pecl und crack hätte ich einfach das HASH Message Digest Framework von PHP verwenden und mir einige Stunden Arbeit sparen können.

Deshalb habe ich den von Amazon benötigten HMAC-SHA256-Algorithmus selbst implementiert.

Den SHA256-Algorithmus habe ich von hier Er stammt von Perry McGee. Das Script kann auf dieser Seite getestet werden.

Der HMAC-Algorithmus stammt von KC Cloyd [1]

Wie man sich überhaupt erfolgreich bei Amazon für das API registriert, erfährt man auf dieser sehr nützlichen Seite: Amazon E-Commerce-Service nutzen - Lieblingslisten per Amazon-API auf die eigene Website bringen

Für die Implementierung des API-Aufrufs war diese Seite nützlich, die den generellen Vorgang beschreibt: Example REST Requests

Am meisten Freude bereitet hat mir die Anweisung Calculate an RFC 2104-compliant HMAC with the SHA256 hash algorithm. Für den Aufwand, den man da betreiben muss, um überhaupt einen Request abschicken zu können, sind die Ergebnisse sehr dürftig. Viele Bücher werden aufgrund ihrer ISBN nicht gefunden, selbst wenn man diese direkt von der Amazon-Webseite kopiert. Wenn etwas gefunden wird, sind die gelieferten Informationen oft sehr unvollständig und verwirrend. Die Qualität der Product Advertising API ist sehr enttäuschend und für ernsthafte Anwendungen erstmal nicht zu gebrauchen.

<?
define( 'SHA256_DIGEST_LENGTH', 32 );

class nanoSha2
{
    var     $toUpper;
    var     $platform;

    // Php 4 - 6 compatable constructor
    function __construct( ) 
    {
        // Determine if the caller wants upper case or not.
        $this->toUpper = false;

        // Deteremine if the system is 32 or 64 bit.
        $tmpInt = (int)4294967295;
        $this->platform = ($tmpInt > 0) ? 64 : 32;
    }

    // Do the SHA-256 Padding routine (make input a multiple of 512 bits)
    function char_pad($str)
    {
        $tmpStr = $str;

        $l = strlen($tmpStr)*8;     // # of bits from input string

        $tmpStr .= "\x80";          // append the "1" bit followed by 7 0's

        $k = (512 - (($l + 8 + 64) % 512)) / 8;   // # of 0 bytes to append
        $k += 4;    // PHP Strings will never exceed (2^31)-1, 1st 32bits of
                    // the 64-bit value representing $l can be all 0's

        for ($x = 0; $x < $k; $x++) {
            $tmpStr .= "\0";
        }

        // append the 32-bits representing # of bits from input string ($l)
        $tmpStr .= chr((($l>>24) & 0xFF));
        $tmpStr .= chr((($l>>16) & 0xFF));
        $tmpStr .= chr((($l>>8) & 0xFF));
        $tmpStr .= chr(($l & 0xFF));

        return $tmpStr;
    }

    // Here are the bitwise and functions as defined in FIPS180-2 Standard
    function addmod2n($x, $y, $n = 4294967296)      // Z = (X + Y) mod 2^32
    {
        $mask = 0x80000000;

        if ($x < 0) {
            $x &= 0x7FFFFFFF;
            $x = (float)$x + $mask;
        }

        if ($y < 0) {
            $y &= 0x7FFFFFFF;
            $y = (float)$y + $mask;
        }

        $r = $x + $y;

        if ($r >= $n) {
            while ($r >= $n) {
                $r -= $n;
            }
        }

        return (int)$r;
    }

    // Logical bitwise right shift (PHP default is arithmetic shift)
    function SHR($x, $n)        // x >> n
    {
        if ($n >= 32) {      // impose some limits to keep it 32-bit
            return (int)0;
        }

        if ($n <= 0) {
            return (int)$x;
        }

        $mask = 0x40000000;

        if ($x < 0) {
            $x &= 0x7FFFFFFF;
            $mask = $mask >> ($n-1);
            return ($x >> $n) | $mask;
        }

        return (int)$x >> (int)$n;
    }

    function ROTR($x, $n) { return (int)(($this->SHR($x, $n) | ($x << (32-$n)) & 0xFFFFFFFF)); }
    function Ch($x, $y, $z) { return ($x & $y) ^ ((~$x) & $z); }
    function Maj($x, $y, $z) { return ($x & $y) ^ ($x & $z) ^ ($y & $z); }
    function Sigma0($x) { return (int) ($this->ROTR($x, 2)^$this->ROTR($x, 13)^$this->ROTR($x, 22)); }
    function Sigma1($x) { return (int) ($this->ROTR($x, 6)^$this->ROTR($x, 11)^$this->ROTR($x, 25)); }
    function sigma_0($x) { return (int) ($this->ROTR($x, 7)^$this->ROTR($x, 18)^$this->SHR($x, 3)); }
    function sigma_1($x) { return (int) ($this->ROTR($x, 17)^$this->ROTR($x, 19)^$this->SHR($x, 10)); }

    /*
     * Custom functions to provide PHP support
     */
    // split a byte-string into integer array values
    function int_split($input)
    {
        $l = strlen($input);

        if ($l <= 0) {
            return (int)0;
        }

        if (($l % 4) != 0) { // invalid input
            return false;
        }

        for ($i = 0; $i < $l; $i += 4)
        {
            $int_build  = (ord($input[$i]) << 24);
            $int_build += (ord($input[$i+1]) << 16);
            $int_build += (ord($input[$i+2]) << 8);
            $int_build += (ord($input[$i+3]));

            $result[] = $int_build;
        }

        return $result;
    }

    /**
     * Process and return the hash.
     *
     * @param $str Input string to hash
     * @return string Hexadecimal representation of the message digest
     */
    function sha256( $str )
    {
        unset($binStr);     // binary representation of input string
        unset($hexStr);     // 256-bit message digest in readable hex format

        /*
         * SHA-256 Constants
         *  Sequence of sixty-four constant 32-bit words representing the
         *  first thirty-two bits of the fractional parts of the cube roots
         *  of the first sixtyfour prime numbers.
         */
        $K = array((int)0x428a2f98, (int)0x71374491, (int)0xb5c0fbcf,
                   (int)0xe9b5dba5, (int)0x3956c25b, (int)0x59f111f1,
                   (int)0x923f82a4, (int)0xab1c5ed5, (int)0xd807aa98,
                   (int)0x12835b01, (int)0x243185be, (int)0x550c7dc3,
                   (int)0x72be5d74, (int)0x80deb1fe, (int)0x9bdc06a7,
                   (int)0xc19bf174, (int)0xe49b69c1, (int)0xefbe4786,
                   (int)0x0fc19dc6, (int)0x240ca1cc, (int)0x2de92c6f,
                   (int)0x4a7484aa, (int)0x5cb0a9dc, (int)0x76f988da,
                   (int)0x983e5152, (int)0xa831c66d, (int)0xb00327c8,
                   (int)0xbf597fc7, (int)0xc6e00bf3, (int)0xd5a79147,
                   (int)0x06ca6351, (int)0x14292967, (int)0x27b70a85,
                   (int)0x2e1b2138, (int)0x4d2c6dfc, (int)0x53380d13,
                   (int)0x650a7354, (int)0x766a0abb, (int)0x81c2c92e,
                   (int)0x92722c85, (int)0xa2bfe8a1, (int)0xa81a664b,
                   (int)0xc24b8b70, (int)0xc76c51a3, (int)0xd192e819,
                   (int)0xd6990624, (int)0xf40e3585, (int)0x106aa070,
                   (int)0x19a4c116, (int)0x1e376c08, (int)0x2748774c,
                   (int)0x34b0bcb5, (int)0x391c0cb3, (int)0x4ed8aa4a,
                   (int)0x5b9cca4f, (int)0x682e6ff3, (int)0x748f82ee,
                   (int)0x78a5636f, (int)0x84c87814, (int)0x8cc70208,
                   (int)0x90befffa, (int)0xa4506ceb, (int)0xbef9a3f7,
                   (int)0xc67178f2);

        // Pre-processing: Padding the string
        $binStr = $this->char_pad($str);

        // Parsing the Padded Message (Break into N 512-bit blocks)
        $M = str_split($binStr, 64);

        // Set the initial hash values
        $h[0] = (int)0x6a09e667;
        $h[1] = (int)0xbb67ae85;
        $h[2] = (int)0x3c6ef372;
        $h[3] = (int)0xa54ff53a;
        $h[4] = (int)0x510e527f;
        $h[5] = (int)0x9b05688c;
        $h[6] = (int)0x1f83d9ab;
        $h[7] = (int)0x5be0cd19;

        // loop through message blocks and compute hash. ( For i=1 to N : )
        $N = count($M);
        for ($i = 0; $i < $N; $i++)
        {
            // Break input block into 16 32bit words (message schedule prep)
            $MI = $this->int_split($M[$i]);

            // Initialize working variables
            $_a = (int)$h[0];
            $_b = (int)$h[1];
            $_c = (int)$h[2];
            $_d = (int)$h[3];
            $_e = (int)$h[4];
            $_f = (int)$h[5];
            $_g = (int)$h[6];
            $_h = (int)$h[7];
            unset($_s0);
            unset($_s1);
            unset($_T1);
            unset($_T2);
            $W = array();

            // Compute the hash and update
            for ($t = 0; $t < 16; $t++)
            {
                // Prepare the first 16 message schedule values as we loop
                $W[$t] = $MI[$t];

                // Compute hash
                $_T1 = $this->addmod2n($this->addmod2n($this->addmod2n($this->addmod2n($_h, $this->Sigma1($_e)), $this->Ch($_e, $_f, $_g)), $K[$t]), $W[$t]);
                $_T2 = $this->addmod2n($this->Sigma0($_a), $this->Maj($_a, $_b, $_c));

                // Update working variables
                $_h = $_g; $_g = $_f; $_f = $_e; $_e = $this->addmod2n($_d, $_T1);
                $_d = $_c; $_c = $_b; $_b = $_a; $_a = $this->addmod2n($_T1, $_T2);
            }

            for (; $t < 64; $t++)
            {
                // Continue building the message schedule as we loop
                $_s0 = $W[($t+1)&0x0F];
                $_s0 = $this->sigma_0($_s0);
                $_s1 = $W[($t+14)&0x0F];
                $_s1 = $this->sigma_1($_s1);

                $W[$t&0xF] = $this->addmod2n($this->addmod2n($this->addmod2n($W[$t&0xF], $_s0), $_s1), $W[($t+9)&0x0F]);

                // Compute hash
                $_T1 = $this->addmod2n($this->addmod2n($this->addmod2n($this->addmod2n($_h, $this->Sigma1($_e)), $this->Ch($_e, $_f, $_g)), $K[$t]), $W[$t&0xF]);
                $_T2 = $this->addmod2n($this->Sigma0($_a), $this->Maj($_a, $_b, $_c));

                // Update working variables
                $_h = $_g; $_g = $_f; $_f = $_e; $_e = $this->addmod2n($_d, $_T1);
                $_d = $_c; $_c = $_b; $_b = $_a; $_a = $this->addmod2n($_T1, $_T2);
            }

            $h[0] = $this->addmod2n($h[0], $_a);
            $h[1] = $this->addmod2n($h[1], $_b);
            $h[2] = $this->addmod2n($h[2], $_c);
            $h[3] = $this->addmod2n($h[3], $_d);
            $h[4] = $this->addmod2n($h[4], $_e);
            $h[5] = $this->addmod2n($h[5], $_f);
            $h[6] = $this->addmod2n($h[6], $_g);
            $h[7] = $this->addmod2n($h[7], $_h);
        }

        // Convert the 32-bit words into human readable hexadecimal format.
        $hexStr = sprintf("%08x%08x%08x%08x%08x%08x%08x%08x", $h[0], $h[1], $h[2], $h[3], $h[4], $h[5], $h[6], $h[7]);

        return ($this->toUpper) ? strtoupper($hexStr) : $hexStr;
    }
    
    function hmac_sha256( $data, $key )
    {
        $pack = 'H64'; //'H'.strlen($this->hash('test'));
        $size = 64;
        $opad = str_repeat(chr(0x5C), $size);
        $ipad = str_repeat(chr(0x36), $size);
    
        if (strlen($key) > $size) {
            $key = str_pad(pack($pack, $this->sha256($key)), $size, chr(0x00));
        } else {
            $key = str_pad($key, $size, chr(0x00));
        }
    
        for ($i = 0; $i < strlen($key) - 1; $i++) {
            $opad[$i] = $opad[$i] ^ $key[$i];
            $ipad[$i] = $ipad[$i] ^ $key[$i];
        }
    
        $output = $this->sha256($opad.pack($pack, $this->sha256($ipad.$data)));
        
        return base64_encode( pack($pack, $output) );
    }
}

class AmazonItemInfo
{
    public $title;
    public $link;
    
    function __construct( $xml )
    {
        if( $xml->Items->Request->IsValid == "False" )
        {
            $this->title = "Invalid Request";
            $this->link = "";
            return;
        }
        
        if( isset( $xml->Items->Request->Errors ) )
        {
            $this->title = "Amazon API failure";
            $this->link = "";
            return;
        }

        $this->title = $xml->Items->Item->ItemAttributes->Title;
        $this->link = $xml->Items->Item->DetailPageURL;
    }
}

class AmazonRequest
{
    private $_appid;
    private $_seckey;
    private $_url;
    
    function __construct( $amzn_appid, $amzn_secret_key, $amzn_url )
    {
        $this->_appid = $amzn_appid;
        $this->_seckey = $amzn_secret_key;
        $this->_url = $amzn_url;
    }
    
    function createParamList( $data )
    {
        ksort( $data );
        
        $req = "";
        foreach( $data as $key => $value )
            $req .= $key . '=' . urlencode($value) . '&';
    
        return substr( $req, 0, strlen($req)-1 );
    }
    
    function createSignature( $data, $key )
    {
        $params = $this->createParamList( $data );
        
        $request = "GET\n".
                   "webservices.amazon.com\n".
                   "/onca/xml\n".
                   $params;

        $obj = new nanoSha2;
        return $obj->hmac_sha256( $request, $key );
    }
    
    function sendRequest( $data )
    {
        $signature = $this->createSignature( $data, $this->_seckey );
        $params = $this->createParamList( $data );
        $sigparam = $this->createParamList( array( 'Signature' => $signature ) );
        $url = $this->_url.'?'.$params.'&'.$sigparam;
        return file_get_contents( $url );
    }
    
    function getItemInfo( $itemid )
    {   
        $data = array(  
            'Service' => 'AWSECommerceService',
            'AWSAccessKeyId' => $this->_appid,
            'Operation' => 'ItemLookup',
            'ItemId' => $itemid,
            'ResponseGroup' => 'Small',
            'Version' => '2009-01-06',
            'Timestamp' => strftime( "%Y-%m-%dT%TZ", time() )
        );
        
        $result = $this->sendRequest( $data );
        
        if( $result === false )
            return false;
            
        $xml = new SimpleXMLElement( $result );
        return new AmazonItemInfo( $xml );
    }
};

/*
$amzn = new AmazonRequest( $_CFG['AMZN_ACCESS_KEY'], $_CFG['AMZN_SECRET_KEY'], $_CFG['AMZN_URL'] );
$item = $amzn->getItemInfo( $data['value'] );
print_r( $item );
*/
?>