in src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp [189:334]
bool AWSAuthV4Signer::SignRequestWithCreds(Aws::Http::HttpRequest& request, const AWSCredentials& credentials,
const char* region, const char* serviceName, bool signBody) const
{
Aws::String signingRegion = region ? region : m_region;
Aws::String signingServiceName = serviceName ? serviceName : m_serviceName;
//don't sign anonymous requests
if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty())
{
return true;
}
request.SetSigningAccessKey(credentials.GetAWSAccessKeyId());
request.SetSigningRegion(signingRegion);
Aws::String payloadHash(UNSIGNED_PAYLOAD);
if (m_signingAlgorithm == AWSSigningAlgorithm::ASYMMETRIC_SIGV4)
{
return SignRequestWithSigV4a(request, signingRegion.c_str(), signingServiceName.c_str(), signBody,
0 /* expirationTimeInSeconds doesn't matter for HttpRequestViaHeaders */, Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders);
}
if (!credentials.GetSessionToken().empty())
{
request.SetAwsSessionToken(credentials.GetSessionToken());
}
// If the request checksum, set the signer to use a unsigned
// trailing payload. otherwise use it in the header
if (request.GetRequestHash().second != nullptr && !request.GetRequestHash().first.empty() && request.GetContentBody() != nullptr) {
AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads="
<< signBody << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme()));
if (request.GetRequestHash().second != nullptr) {
payloadHash = STREAMING_UNSIGNED_PAYLOAD_TRAILER;
Aws::String checksumHeaderValue = Aws::String("x-amz-checksum-") + request.GetRequestHash().first;
request.DeleteHeader(checksumHeaderValue.c_str());
request.SetHeaderValue(Http::AWS_TRAILER_HEADER, checksumHeaderValue);
request.SetTransferEncoding(CHUNKED_VALUE);
request.HasContentEncoding()
? request.SetContentEncoding(Aws::String{Http::AWS_CHUNKED_VALUE} + "," + request.GetContentEncoding())
: request.SetContentEncoding(Http::AWS_CHUNKED_VALUE);
if (request.HasHeader(Http::CONTENT_LENGTH_HEADER)) {
request.SetHeaderValue(Http::DECODED_CONTENT_LENGTH_HEADER, request.GetHeaderValue(Http::CONTENT_LENGTH_HEADER));
request.DeleteHeader(Http::CONTENT_LENGTH_HEADER);
}
}
} else {
payloadHash = ComputePayloadHash(request);
if (payloadHash.empty()) {
// this indicates a hashing error occurred, which was logged
return false;
}
Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + request.GetRequestHash().first;
const auto headers = request.GetHeaders();
if (request.GetRequestHash().second != nullptr && !request.HasHeader(checksumHeaderKey.c_str())) {
Aws::String checksumHeaderValue;
if (request.GetRequestHash().first == "sha256") {
// we already calculated the payload hash so just reverse the hex string to
// a ByteBuffer and Base64Encode it - otherwise we're re-hashing the content
checksumHeaderValue = HashingUtils::Base64Encode(HashingUtils::HexDecode(payloadHash));
} else {
// if it is one of the other hashes, we must be careful if there is no content body
const auto& body = request.GetContentBody();
checksumHeaderValue = (body) ? HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate(*body).GetResult())
: HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate({}).GetResult());
}
request.SetHeaderValue(checksumHeaderKey, checksumHeaderValue);
request.SetRequestHash("", nullptr);
}
}
if(m_includeSha256HashHeader)
{
request.SetHeaderValue(Aws::Auth::AWSAuthHelper::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(DateFormat::ISO_8601_BASIC);
request.SetHeaderValue(AWS_DATE_HEADER, dateHeaderValue);
Aws::StringStream headersStream;
Aws::StringStream signedHeadersStream;
for (const auto& header : Aws::Auth::AWSAuthHelper::CanonicalizeHeaders(request.GetHeaders()))
{
if(ShouldSignHeader(header.first))
{
headersStream << header.first.c_str() << ":" << header.second.c_str() << Aws::Auth::AWSAuthHelper::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
if (!signedHeadersValue.empty())
{
signedHeadersValue.pop_back();
}
AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value:" << signedHeadersValue);
//generate generalized canonicalized request string.
Aws::String canonicalRequestString = Aws::Auth::AWSAuthHelper::CanonicalizeRequestSigningString(request, m_urlEscapePath);
//append v4 stuff to the canonical request string.
canonicalRequestString.append(canonicalHeadersString);
canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE);
canonicalRequestString.append(signedHeadersValue);
canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE);
canonicalRequestString.append(payloadHash);
AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString);
//now compute sha256 on that request string
auto sha256Digest = HashingUtils::CalculateSHA256(canonicalRequestString);
if (sha256Digest.GetLength() == 0)
{
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string");
AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\"");
return false;
}
Aws::String canonicalRequestHash = HashingUtils::HexEncode(sha256Digest);
Aws::String simpleDate = now.ToGmtString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR);
Aws::String stringToSign = GenerateStringToSign(dateHeaderValue, simpleDate, canonicalRequestHash, signingRegion, signingServiceName);
auto finalSignature = GenerateSignature(credentials, stringToSign, simpleDate, signingRegion, signingServiceName);
Aws::StringStream ss;
ss << Aws::Auth::AWSAuthHelper::AWS_HMAC_SHA256 << " " << Aws::Auth::AWSAuthHelper::CREDENTIAL << Aws::Auth::AWSAuthHelper::EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate
<< "/" << signingRegion << "/" << signingServiceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << ", " << Aws::Auth::AWSAuthHelper::SIGNED_HEADERS << Aws::Auth::AWSAuthHelper::EQ
<< signedHeadersValue << ", " << SIGNATURE << Aws::Auth::AWSAuthHelper::EQ << finalSignature;
auto awsAuthString = ss.str();
AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString);
request.SetAwsAuthorization(awsAuthString);
return true;
}