Making a Request

Your application calls the Amazon Mechanical Turk web service to perform an operation, and receive the results of that operation. A request sent by your application to the service contains:

In the next steps for the sample project, you will set up the tools used to make the request, create the routines to authenticate the request, then call the service's GetAccountBalance operation.

In this section, you include the libraries and set up the tools to make the request.

When you make a request to the web service, the service needs to know who you are so it can verify that you are authorized to make the request. You tell the web service who you are by providing two pieces of information with your request:

To get your AWS access key ID and secret key

  1. Go to the Amazon Web Services web site:

    http://aws.amazon.com

  2. Move your mouse over the Your Web Services Account button in the upper-right-hand corner. From the pop-up menu that appears, select View Access Key Identifiers.

  3. Sign in with your e-mail address and password. Your AWS access key ID appears on the screen, as a string of numbers and letters.

  4. To view your secret key, click Show in the corresponding box. Your secret key is a string of numbers, letters and symbols.

The general procedure for calculating a signature for a request is as follows. The code to perform these steps is shown below.

These steps vary in detail depending on your language and toolkits.

For the Java project, define functions for generating the timestamp and signature values, using the time, HMAC-SHA1, and Base64 routines that the Java runtime environment provides.

To calculate a signature for a request in Java

  1. Import the classes.

    // Import classes
    [...]
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.GregorianCalendar;
    import java.util.Date;
    import java.util.TimeZone;
    
    import org.apache.axis.encoding.Base64;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.security.SignatureException;
    	  
  2. Define constant String values for your AWS access key ID and secret key, as well as for the name of the service.

        // Define constants
        private final static String SERVICE_NAME = "AWSMechanicalTurkRequester";
        private final static String AWS_ACCESS_KEY_ID = "[INSERT YOUR ACCESS KEY ID]";
        private final static String SECRET_KEY = "[INSERT YOUR SECRET ACCESS KEY]";
    	  
  3. Define routines for generating the timestamp and signature values.

        // Define authentication routines
        private final static String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
        private final static String TIME_ZONE = "UTC";
        private final static SimpleDateFormat df = new SimpleDateFormat( TIMESTAMP_FORMAT );
        static {
            df.setTimeZone( TimeZone.getTimeZone( TIME_ZONE ) );
        }
    
        public static String generateTimestamp( Calendar time ) {
            return df.format( time.getTime() );
        }
    
        public static String generateSignature( String serviceName,
                                                String operation,
                                                String timestamp,
                                                String key )
            throws java.security.SignatureException {
    
            String data = serviceName + operation + timestamp;
    
            final String HMAC_SHA1_ALGORITHM = "HmacSHA1";        
            String signature;
            try {
                SecretKeySpec signingKey = new SecretKeySpec( key.getBytes(),
                                                              HMAC_SHA1_ALGORITHM );
                Mac mac = Mac.getInstance( HMAC_SHA1_ALGORITHM );
                mac.init( signingKey );
                byte[] rawHmac = mac.doFinal( data.getBytes() );
                signature = Base64.encode( rawHmac );
            } catch ( Exception e ) {
                throw new SignatureException( "Failed to generate Signature: "
                                              + e.getMessage() );
            }
            return signature;
        }
    	  
  4. In the main routine, calculate and set the authentication parameters for the request.

        public static void main( String [] args ) {
    
            try {
    	    [...]
                // Calculate and set the request authentication parameters
                getAccountBalance.setAWSAccessKeyId( AWS_ACCESS_KEY_ID );
                
                Calendar time = new GregorianCalendar();
                time.setTime( new Date() );
                getAccountBalance.setTimestamp( time );
                String timestamp = generateTimestamp( time );
                
                String signature = generateSignature( SERVICE_NAME,
                                                      "GetAccountBalance",
                                                      timestamp,
                                                      SECRET_KEY
                                                      );
                getAccountBalance.setSignature( signature );
    	  

For the C# project, define functions for generating the timestamp and signature values, using the time, HMAC-SHA1, and Base64 routines provided by the System resources.

To calculate a signature for a request in C#

  1. Use the namespaces of the System resources. (These resources have already been added to your project as part of the Console Application template.)

    // Use namespaces
    [...]
    using System.Security.Cryptography;
    using System.IO;
    	  
  2. Define constant String values for your AWS access key ID and secret key, as well as for the name of the service.

            // Define constants
            private static string SERVICE_NAME = "AWSMechanicalTurkRequester";
            private static string AWS_ACCESS_KEY_ID = "[INSERT YOUR ACCESS KEY ID]";
            private static string SECRET_KEY = "[INSERT YOUR SECRET ACCESS KEY]";
    	  
  3. Define routines for generating the timestamp and signature values.

    Note:

    The string value of the timestamp used to calculate the signature must be identical to the value of the Timestamp parameter as it appears in the SOAP request. Because the .NET framework uses its own routine to convert the DateTime value to a String, your code must make sure to produce the same value.

    One method is to format the DateTime value into a String using the desired format, then re-parse the String value back to a DateTime value. When you pass the result to .NET, the value sent over the wire matches the String value.

            // Define authentication routines
            private static String TIMESTAMP_FORMAT = "yyyy-MM-ddTHH:mm:ss.fffZ";
            static String generateTimestampAsString(DateTime time)
            {
                return time.ToUniversalTime().ToString(TIMESTAMP_FORMAT);
            }
            static DateTime generateTimestampAsDateTime(DateTime time)
            {
                return DateTime.ParseExact(generateTimestampAsString(time),
                    TIMESTAMP_FORMAT, null).ToUniversalTime();
            }
    
            static String generateSignature(
                String serviceName,
                String operation,
                String timestamp,
                String key)
            {
                String data = serviceName + operation + timestamp;
    
                byte[] rawData = Encoding.UTF8.GetBytes(data);
                byte[] rawKey = Encoding.UTF8.GetBytes(key);
    
                HMACSHA1 hmac = new HMACSHA1(rawKey);
                CryptoStream cryptoStream =
                    new CryptoStream(Stream.Null, hmac, CryptoStreamMode.Write);
    
                cryptoStream.Write(rawData, 0, rawData.Length);
                cryptoStream.Close();
    
                return Convert.ToBase64String(hmac.Hash);
            }
    	  
  4. In the main routine, calculate and set the authentication parameters for the request.

            static void Main(string[] args)
            {
                try
                {
    	        [...]
                    // Calculate and set the request authentication parameters
                    getAccountBalance.AWSAccessKeyId = AWS_ACCESS_KEY_ID;
    
                    DateTime time = DateTime.Now;
                    String timestampAsString = generateTimestampAsString(time);
                    DateTime timestampAsDateTime = generateTimestampAsDateTime(time);
                    getAccountBalance.Timestamp = timestampAsDateTime;
    
                    String signature = generateSignature(SERVICE_NAME,
                        "GetAccountBalance",
                        timestampAsString,
                        SECRET_KEY);
                    getAccountBalance.Signature = signature;
    	  

For the PHP project, define functions for generating the timestamp and signature values, as well as a support function for calculating the HMAC. Use PHP's built-in routines for SHA-1 digests and base-64 encoding.

For the Ruby (REST) project, define functions for generating the timestamp and signature values, as well as a support function for calculating the HMAC. Use the digest/sha1 and base64 libraries that are part of the standard Ruby distribution.

To calculate a signature for a request in Ruby

  1. Require the libraries.

    # Require libraries
    [...]
    require 'digest/sha1'
    require 'base64'
    	  
  2. Define constants for your AWS access key ID and secret key, as well as the name of the service.

    # Define constants
    AWS_ACCESS_KEY_ID = '[INSERT YOUR ACCESS KEY ID]'
    AWS_SECRET_ACCESS_KEY = '[INSERT YOUR SECRET ACCESS KEY]'
    SERVICE_NAME = 'AWSMechanicalTurkRequester'
    SERVICE_VERSION = '2006-10-31'
    	  
  3. Define routines for calculating the HMAC and for generating the timestamp and signature values.

    # Define authentication routines
    def generate_timestamp(time)
      return time.gmtime.strftime('%Y-%m-%dT%H:%M:%SZ')
    end
    
    def hmac_sha1(key, s)
      ipad = [].fill(0x36, 0, 64)
      opad = [].fill(0x5C, 0, 64)
      key = key.unpack("C*")
      key += [].fill(0, 0, 64-key.length) if key.length < 64
      
      inner = []
      64.times { |i| inner.push(key[i] ^ ipad[i]) }
      inner += s.unpack("C*")
      
      outer = []
      64.times { |i| outer.push(key[i] ^ opad[i]) }
      outer = outer.pack("c*")
      outer += Digest::SHA1.digest(inner.pack("c*"))
      
      return Digest::SHA1.digest(outer)
    end
    
    def generate_signature(service, operation, timestamp, secret_access_key)
      msg = "#{service}#{operation}#{timestamp}"
      hmac = hmac_sha1( secret_access_key, msg )
      b64_hmac = Base64::encode64(hmac).chomp
      return b64_hmac
    end
    	  
  4. Generate the timestamp and signature values using the new routines.

    # Calculate the request authentication parameters
    operation = 'GetAccountBalance'
    timestamp = generate_timestamp(Time.now)
    signature = generate_signature('AWSMechanicalTurkRequester', operation, timestamp, AWS_SECRET_ACCESS_KEY)
    	  

For the Ruby (REST) project, define functions for generating the timestamp and signature values, as well as a support function for calculating the HMAC. Use the digest/sha1 and base64 libraries that are part of the standard Ruby distribution.

To calculate a signature for a request in Ruby

  1. Require the libraries.

    # Require libraries
    [...]
    require 'digest/sha1'
    require 'base64'
    	  
  2. Define constants for your AWS access key ID and secret key, as well as the name of the service.

    # Define constants
    AWS_ACCESS_KEY_ID = '[INSERT YOUR ACCESS KEY ID]'
    AWS_SECRET_ACCESS_KEY = '[INSERT YOUR SECRET ACCESS KEY]'
    SERVICE_NAME = 'AWSMechanicalTurkRequester'
    SERVICE_VERSION = '2006-10-31'
    	  
  3. Define routines for calculating the HMAC, and for generating the timestamp and signature values.

    # Define authentication routines
    def generate_timestamp(time)
      return time.gmtime.strftime('%Y-%m-%dT%H:%M:%SZ')
    end
    
    def hmac_sha1(key, s)
      ipad = [].fill(0x36, 0, 64)
      opad = [].fill(0x5C, 0, 64)
      key = key.unpack("C*")
      key += [].fill(0, 0, 64-key.length) if key.length < 64
      
      inner = []
      64.times { |i| inner.push(key[i] ^ ipad[i]) }
      inner += s.unpack("C*")
      
      outer = []
      64.times { |i| outer.push(key[i] ^ opad[i]) }
      outer = outer.pack("c*")
      outer += Digest::SHA1.digest(inner.pack("c*"))
      
      return Digest::SHA1.digest(outer)
    end
    
    def generate_signature(service, operation, timestamp, secret_access_key)
      msg = "#{service}#{operation}#{timestamp}"
      hmac = hmac_sha1( secret_access_key, msg )
      b64_hmac = Base64::encode64(hmac).chomp
      return b64_hmac
    end
    	  
  4. Generate the timestamp and signature values using the new routines.

    # Calculate the request authentication parameters
    operation = 'GetAccountBalance'
    timestamp = generate_timestamp(Time.now)
    signature = generate_signature('AWSMechanicalTurkRequester', operation, timestamp, AWS_SECRET_ACCESS_KEY)
    	  

In this procedure, you set the parameters and make the request to call the GetAccountBalance operation.