I'm using spring, resttemplate to get/delete a file from s3. I receive the file without problems, but when deleting I get a 501 error. From documentation: "A header that you provided implies functionality that is not implemented.";
I calculate this header using code that I took from the s3 library with examples for java. Here's how the signature is calculated (which works for getting a file, but doesn't work for deleting):
public class AWS4SignerForAuthorizationHeader extends AWS4SignerBase {
public AWS4SignerForAuthorizationHeader(URL endpointUrl, String httpMethod,
String serviceName, String regionName) {
super(endpointUrl, httpMethod, serviceName, regionName);
}
/**
* Computes an AWS4 signature for a request, ready for inclusion as an 'Authorization' header.
*
* @param headers The request headers; 'Host' and 'X-Amz-Date' will be added to this set.
* @param queryParameters Any query parameters that will be added to the endpoint. The parameters
* should be specified in canonical format.
* @param bodyHash Precomputed SHA256 hash of the request body content; this value should
* also be set as the header 'X-Amz-Content-SHA256' for non-streaming
* uploads.
* @param awsAccessKey The user's AWS Access Key.
* @param awsSecretKey The user's AWS Secret Key.
* @return The computed authorization string for the request. This value needs to be set as the
* header 'Authorization' on the subsequent HTTP request.
*/
public String computeSignature(Map<String, String> headers,
Map<String, String> queryParameters,
String bodyHash,
String awsAccessKey,
String awsSecretKey) {
// first get the date and time for the subsequent request, and convert
// to ISO 8601 format for use in signature generation
var now = new Date();
var dateTimeStamp = dateTimeFormat.format(now);
// update the headers with required 'x-amz-date' and 'host' values
headers.put("x-amz-date", dateTimeStamp);
var hostHeader = endpointUrl.getHost();
var port = endpointUrl.getPort();
if (port > -1) {
hostHeader = hostHeader.concat(":" + port);
}
headers.put("Host", hostHeader);
// canonicalize the headers; we need the set of header names as well as the
// names and values to go into the signature process
var canonicalizedHeaderNames = getCanonicalizeHeaderNames(headers);
var canonicalizedHeaders = getCanonicalizedHeaderString(headers);
// if any query string parameters have been supplied, canonicalize them
var canonicalizedQueryParameters = getCanonicalizedQueryString(queryParameters);
// canonicalize the various components of the request
var canonicalRequest = getCanonicalRequest(endpointUrl, httpMethod,
canonicalizedQueryParameters, canonicalizedHeaderNames,
canonicalizedHeaders, bodyHash);
// construct the string to be signed
var dateStamp = dateStampFormat.format(now);
var scope = dateStamp + "/" + regionName + "/" + serviceName + "/" + TERMINATOR;
var stringToSign = getStringToSign(SCHEME, ALGORITHM, dateTimeStamp, scope,
canonicalRequest);
// compute the signing key
byte[] kSecret = (SCHEME + awsSecretKey).getBytes();
byte[] kDate = sign(dateStamp, kSecret, "HmacSHA256");
byte[] kRegion = sign(regionName, kDate, "HmacSHA256");
byte[] kService = sign(serviceName, kRegion, "HmacSHA256");
byte[] kSigning = sign(TERMINATOR, kService, "HmacSHA256");
byte[] signature = sign(stringToSign, kSigning, "HmacSHA256");
var credentialsAuthorizationHeader =
"Credential=" + awsAccessKey + "/" + scope;
var signedHeadersAuthorizationHeader =
"SignedHeaders=" + canonicalizedHeaderNames;
var signatureAuthorizationHeader =
"Signature=" + BinaryUtils.toHex(signature);
return SCHEME + "-" + ALGORITHM + " "
+ credentialsAuthorizationHeader + ", "
+ signedHeadersAuthorizationHeader + ", "
+ signatureAuthorizationHeader;
}
}
And here's how I use it:
private HttpEntity<String> prepareHeaders(String bucket, String key, HttpMethod httpMethod) {
Map<String, String> headers = new HashMap<>();
headers.put("x-amz-content-sha256", AWS4SignerBase.EMPTY_BODY_SHA256);
var signer = new AWS4SignerForAuthorizationHeader(
prepareUrl(s3Property.getAddress(), bucket, key), httpMethod.name(),
"s3", "us-west-2");
var authorization = signer.computeSignature(headers, null, AWS4SignerBase.EMPTY_BODY_SHA256,
s3Property.getAccessKey(), s3Property.getSecretKey());
headers.put("Authorization", authorization);
return toHttpEntity(headers);
}
It's likely that I'm sending the argument to the constructor incorrectly to delete the file. Please help if you have encountered this
PS By the way, through Postman, everything works more than fine.
I tried to substitute different methods, tried to write an empty string instead of AWS4SignerBase.EMPTY_BODY_SHA256, but it didn’t work.