Как перевести AWS Задача 1. Создание канонического запроса на подпись версии 4

(Отказ от ответственности: это подразумевается как "практическое руководство", так как я не мог найти никаких примеров CF, когда я реализовывал AWS Signature Version 4)

Как реализовать задачу 1: создать канонический запрос на подпись версии 4 в CF?

Резюме:

  1. Начните с метода HTTP-запроса (GET, PUT, POST и т. Д.), За которым следует символ новой строки.
  2. Добавьте канонический параметр URI, за которым следует символ новой строки.
  3. Добавьте строку канонического запроса, за которой следует символ новой строки
  4. Добавьте канонические заголовки, за которыми следует символ новой строки.
  5. Добавьте подписанные заголовки, за которыми следует символ новой строки.
  6. Используйте хеш-функцию (дайджест), например SHA256, чтобы создать хеш-значение из полезной нагрузки в теле запроса.
  7. Создайте законченный канонический запрос, объединив компоненты из каждого шага в одну строку.
  8. Создайте дайджест (хеш) канонического запроса с тем же алгоритмом, который использовался для хэширования полезной нагрузки.

1 ответ

Решение

Ниже приведена реализация задачи 1 в cfscript : создание канонического запроса на подпись версии 4

Результат:

f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59

Код:

  1. Начните с метода HTTP-запроса (GET, PUT, POST и т. Д.)

    requestMethod = "GET";
    writeOutput("<br>requestMethod: <code>"& requestMethod &"</code>");
    
  2. Добавьте (закодированный) канонический параметр URI, за которым следует символ новой строки.

    originalURI = "";
    // If the absolute path is empty, use a forward slash (/)
    originalURI  = len(trim(originalURI)) ? originalURI : "/"& originalURI;
    // Encode URI and preserve forward slashes 
    canonicalURI = replace( encodeRFC3986( originalURI ), "%2F", "/", "all");
    writeOutput("<br>canonicalURI: <code>"& canonicalURI &"</code>");
    
  3. Добавьте строку канонического запроса, за которой следует символ новой строки

    queryParams = { "Action"="ListUsers", "Version"="2010-05-08" };
    
    // a) Encode parameter names and values 
    encodedParams = {};
    structEach( queryParams, function(key, value) {
        encodedParams[ encodeRFC3986(arguments.key) ] = encodeRFC3986( arguments.value);
    });
    
    // b) Sort the encoded parameter in ascending order (ASCII order)
    encodedKeyNames = structKeyArray( encodedParams );
    arraySort( encodedKeyNames, "text" );
    
    // c) Build the canonical query string. Starting with first parameter, append encoded 
    // parameter name, followed by character '=' (ASCII code 61), followed by the encoded value
    encodedPairs  = [];
    for (key in encodedKeyNames) {
        arrayAppend( encodedPairs, key &"="& encodedParams[ key ] ); 
    }
    // d) Append the character '&' (ASCII code 38) after each parameter value, except for the last value in the list. 
    canonicalQueryString = arrayToList( encodedPairs, "&");
    writeOutput("<br>canonicalQueryString: <code>"& canonicalQueryString &"</code>");
    
  4. Добавьте канонические заголовки, за которыми следует символ новой строки.

    requestHeaders = { "Content-type"= "application/x-www-form-urlencoded; charset=utf-8"
                        , "Host" = "iam.amazonaws.com"
                        , "X-Amz-Date" = "20150830T123600Z"
                    };
    
    // a) Convert all header names to lowercase and remove leading spaces and trailing spaces. 
    // Convert sequential spaces in the header value to a single space.         
    cleanedHeaders = {};
    structEach( requestHeaders, function(key, value) {
        headerName = reReplace( trim(arguments.key), "\s+", " ", "all");
        headerValue = reReplace( trim(arguments.value), "\s+", " ", "all");
        cleanedHeaders[ lcase(headerName) ] = headerValue;
    });
    
    // b) [sort] the (lowercase) headers by character code
    sortedHeaderNames = structKeyArray( cleanedHeaders );
    arraySort( sortedHeaderNames, "text" );
    
    // c) Append the lowercase header name followed by a colon.
    // Do not sort the values in headers that have multiple values.
    cleanedPairs  = [];
    for (key in sortedHeaderNames) {
        arrayAppend( cleanedPairs, key &":"& cleanedHeaders[ key ] ); 
    }
    
    // d) Append new line after each header pair. Should END WITH a new line
    canonicalHeaderString = arrayToList( cleanedPairs, chr(10) ) & chr(10) ;
    writeOutput("<br> canonicalHeaderString: <code>"& canonicalHeaderString &"</code>");
    
  5. Добавьте подписанные заголовки с последующим символом перевода строки

    // To create the signed headers list, convert all header names to lowercase, 
    // sort them by character code, and use a semicolon to separate the header names. 
    // Note, we already have the sorted names from the canonical header logic (step 4)
    signedHeaderString = arrayToList( sortedHeaderNames, ";" );
    writeOutput("<br>signedHeaderString: <code>"& signedHeaderString &"</code>");
    
  6. Создайте хэш полезной нагрузки в теле запроса http/https.

    requestPayload = "";
    payloadChecksum = lcase( hash( requestPayload , "SHA256" ) );
    writeOutput("<br>payloadChecksum: <code>"& payloadChecksum &"</code>");
    
  7. Построить канонический запрос, объединив компоненты из каждого шага в одну строку

    canonicalRequest = requestMethod & chr(10)
                        & canonicalURI & chr(10)
                        & canonicalQueryString & chr(10)
                        & canonicalHeaderString & chr(10)
                        & signedHeaderString & chr(10)
                        & payloadChecksum ;
    
    writeOutput("<br>canonicalRequest: <pre>"& canonicalRequest &"</pre>");
    
  8. Создайте дайджест (хеш) канонического запроса с тем же алгоритмом, который использовался для хэширования полезной нагрузки.

    requestDigest = lcase( hash( canonicalRequest , "SHA256" ) );
    writeOutput("<br>requestDigest: <code>"& requestDigest &"</code>");
    

UDF encodeRFC3986:

    /**
    * URI encoding per RFC 3986:
    *  <ul>
    *     <li>Unreserved characters that should not be escaped: ALPHA / DIGIT / "-" / "." / "_" / "~" </li>
    *     <li>Spaces should be encoded as %20 instead of +</li>
    *     <li>Reserved characters that should be escaped include:   ? ## [ ] @ ! $ & ' ( ) * + , ; =</li>
    *  </ul>
    *  
    * @text String to encode
    * @returns URI encoded text
    */
    public function encodeRFC3986(required string text) {
        // Requires CF10+
        Local.encoded = encodeForURL(arguments.text);

        // Undo encoding of tilde "~"
        Local.encoded = replace( Local.encoded, "%7E", "~", "all" );
        // Change space encoding from "+" to "%20"
        Local.encoded = replace( Local.encoded, "+", "%20", "all" );
        // URL encode asterisk "*" 
        Local.encoded = replace( Local.encoded, "*", "%2A", "all" );

        return Local.encoded;
    }
Другие вопросы по тегам