static Http::HeaderValueCollection CanonicalizeHeaders(Http::HeaderValueCollection&& headers) { Http::HeaderValueCollection canonicalHeaders; for (const auto& header : headers) { auto trimmedHeaderName = StringUtils::Trim(header.first.c_str()); auto trimmedHeaderValue = StringUtils::Trim(header.second.c_str()); //multiline gets converted to line1,line2,etc... auto headerMultiLine = StringUtils::SplitOnLine(trimmedHeaderValue); Aws::String headerValue = headerMultiLine[0]; if (headerMultiLine.size() > 1) { for(size_t i = 1; i < headerMultiLine.size(); ++i) { headerValue += ","; headerValue += StringUtils::Trim(headerMultiLine[i].c_str()); } } //duplicate spaces need to be converted to one. Aws::String::iterator new_end = std::unique(headerValue.begin(), headerValue.end(), [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } ); headerValue.erase(new_end, headerValue.end()); canonicalHeaders[trimmedHeaderName] = headerValue; } return canonicalHeaders; }
void StringUtils::Replace(Aws::String &s, const char* search, const char* replace) { if(!search || !replace) { return; } size_t replaceLength = strlen(replace); size_t searchLength = strlen(search); for (std::size_t pos = 0;; pos += replaceLength) { pos = s.find(search, pos); if (pos == Aws::String::npos) break; s.erase(pos, searchLength); s.insert(pos, replace); } }
bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, bool signBody) const { AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); //don't sign anonymous requests if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) { return true; } if (!credentials.GetSessionToken().empty()) { request.SetAwsSessionToken(credentials.GetSessionToken()); } Aws::String payloadHash(UNSIGNED_PAYLOAD); if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) { payloadHash.assign(ComputePayloadHash(request)); if (payloadHash.empty()) { return false; } } else { AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads=" << m_signPayloads << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); } if(m_includeSha256HashHeader) { request.SetHeaderValue("x-amz-content-sha256", payloadHash); } //calculate date header to use in internal signature (this also goes into date header). DateTime now = GetSigningTimestamp(); Aws::String dateHeaderValue = now.ToGmtString(LONG_DATE_FORMAT_STR); request.SetHeaderValue(AWS_DATE_HEADER, dateHeaderValue); Aws::StringStream headersStream; Aws::StringStream signedHeadersStream; for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) { if(ShouldSignHeader(header.first)) { headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; signedHeadersStream << header.first.c_str() << ";"; } } Aws::String canonicalHeadersString = headersStream.str(); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); //calculate signed headers parameter Aws::String signedHeadersValue = signedHeadersStream.str(); //remove that last semi-colon signedHeadersValue.erase(signedHeadersValue.length() - 1); AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value:" << signedHeadersValue); //generate generalized canonicalized request string. Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); //append v4 stuff to the canonical request string. canonicalRequestString.append(canonicalHeadersString); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(signedHeadersValue); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(payloadHash); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); //now compute sha256 on that request string auto hashResult = m_hash->Calculate(canonicalRequestString); if (!hashResult.IsSuccess()) { AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string \"" << canonicalRequestString << "\""); return false; } auto sha256Digest = hashResult.GetResult(); Aws::String cannonicalRequestHash = HashingUtils::HexEncode(sha256Digest); Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); Aws::String stringToSign = GenerateStringToSign(dateHeaderValue, simpleDate, cannonicalRequestHash); auto finalSignature = GenerateSignature(credentials, stringToSign, simpleDate); Aws::StringStream ss; ss << AWS_HMAC_SHA256 << " " << CREDENTIAL << EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate << "/" << m_region << "/" << m_serviceName << "/" << AWS4_REQUEST << ", " << SIGNED_HEADERS << EQ << signedHeadersValue << ", " << SIGNATURE << EQ << finalSignature; auto awsAuthString = ss.str(); AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString); request.SetAwsAuthorization(awsAuthString); return true; }