Reputation: 31
I am using restapi to fetch data from azure blob storage using the access keys. I have a postman collection which i can use to generate the signature string for the azure using the access keys.
But when i implement the same logic in java language it fails.
It is giving me the error "403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.: [AuthenticationFailed
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly inclu... (435 bytes)]"
here is the postman test pre req part which generate the signature string.
// Should be UTC GMT string
pm.environment.set("utcStr", new Date().toUTCString());
// Get hash of all header-name:value
const headers = pm.request.getHeaders({ ignoreCase: true, enabled: true });
// Construct Signature value for Authorization header
var signatureParts = [
pm.request.method.toUpperCase(),
headers["content-encoding"] || "",
headers["content-language"] || "",
headers["content-length"] || "",
// pm.request.body ? pm.request.body.toString().length || "" : "",
headers["content-md5"] || "",
headers["content-type"] || "",
headers["x-ms-date"] ? "" : (pm.environment.get("utcStr") || ""),
headers["if-modified-since"] || "",
headers["if-match"] || "",
headers["if-none-match"] || "",
headers["if-unmodified-since"] || "",
headers["range"] || ""
];
// Construct CanonicalizedHeaders
const canonicalHeaderNames = [];
Object.keys(headers).forEach(key => {
if (key.startsWith("x-ms-")) {
canonicalHeaderNames.push(key);
}
});
// Sort headers lexographically by name
canonicalHeaderNames.sort();
const canonicalHeaderParts = [];
canonicalHeaderNames.forEach(key => {
let value = pm.request.getHeaders({ ignoreCase: true, enabled: true })[key];
// Populate variables
value = pm.environment.replaceIn(value);
// Replace whitespace in value but not if its within quotes
if (!value.startsWith("\"")) {
value = value.replace(/\s+/, " ");
}
canonicalHeaderParts.push(`${key}:${value}`);
});
// Add headers to signature
signatureParts.push.apply(signatureParts, canonicalHeaderParts);
// Construct CanonicalizedResource
const canonicalResourceParts = [
`/${pm.environment.get("storeAcc")}${pm.request.url.getPath()}`
];
const canonicalQueryNames = [];
pm.request.url.query.each(query => {
canonicalQueryNames.push(query.key.toLowerCase());
});
canonicalQueryNames.sort();
canonicalQueryNames.forEach(queryName => {
const value = pm.request.url.query.get(queryName);
// NOTE: This does not properly explode multiple same query params' values
// and turn them into comma-separated list
canonicalResourceParts.push(`${queryName}:${value}`);
});
// Add resource to signature
signatureParts.push.apply(signatureParts, canonicalResourceParts);
console.log("Signature Parts", signatureParts);
// Now, construct signature raw string
const signatureRaw = signatureParts.join("\n");
console.log("Signature String", JSON.stringify(signatureRaw));
// Hash it using HMAC-SHA256 and then encode using base64
const storageKey = pm.variables.get("accountKey");
const signatureBytes = CryptoJS.HmacSHA256(signatureRaw, CryptoJS.enc.Base64.parse(storageKey));
console.log("signatureBytes",signatureBytes);
const signatureEncoded = signatureBytes.toString(CryptoJS.enc.Base64);
console.log("signatureEncoded",signatureEncoded);
console.log("Storage Account", pm.environment.get("storeAcc"));
console.log("Storage Key", storageKey);
// Finally, make it available for headers
pm.environment.set("sigStr",
`SharedKey ${pm.environment.get("storeAcc")}:${signatureEncoded}`);
This code works and the signature i generate is authenticated and i get access to blob apis.
but i implemented the same code in java and every time it gives me the same problem "403 Authentication error."
Java code
public String generateOauthHeader(String method, UserDetails userFields, String baseUrl, String azureFileListUrl) {
String key = userFields.getAccountKey();
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String startDate = dateFormat.format(new Date());
String stringToSign = method + "\\n\\n\\n\\n\\napplication/json\\n\\n\\n\\n\\n\\n\\nx-ms-date:" + "Tue, 05 Jul 2022 11:45:00 "
+ "GMT\\nx-ms-version:2020-04-08\\n/" + userFields.getAccountName() + "/"
+ userFields.getContainerName();
if (null != azureFileListUrl) {
azureFileListUrl =azureFileListUrl.replace("?", "");
azureFileListUrl =azureFileListUrl.replace("=", ":");
String[] paramlist = azureFileListUrl.split("&");
stringToSign = stringToSign + "\\n" + paramlist[1] + "\\n" + paramlist[0];
}
stringToSign="\""+stringToSign+"\"";
System.err.println("Signature string = " + stringToSign);
String signature = getHMAC256(key, stringToSign);
signature = "SharedKey " + userFields.getAccountName() + ":" + signature;
System.err.println("Final Signature string = " + signature);
return signature;
}
private static String getHMAC256(String accountKey, String signStr) {
String signature = null;
try {
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
I am adding below two string which are generated around same time.
The sample signatureString before signing are identical. I have printed and checked them as well.
postman= "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:45:00 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
code = "GET\n\n\n\n\napplication/json\n\n\n\n\n\n\nx-ms-date:Tue, 05 Jul 2022 11:44:56 GMT\nx-ms-version:2020-04-08\n/osmosconnectortest/blobcontainer\ncomp:list\nrestype:container"
EDIT: here are the headers sent with the request.
requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
requestHeaders.put("x-ms-version", "2020-04-08");
requestHeaders.put("Content-Type", "application/json");
requestHeaders.put("Authorization", "SharedKey accountName:yvfdsfdssdfF0lGGtEWGU+b7BqFY0UHMkvI=
");
Upvotes: 2
Views: 1894
Reputation: 136346
I believe the issue is with the following line of code:
requestHeaders.put("x-ms-date", "Tue, 05 Jul 2022 11:45:00 GMT");
Essentially in your request headers, you are sending the date as Tue, 05 Jul 2022 11:45:00 GMT
however in your stringToSign
you are sending the date as Tue, 05 Jul 2022 11:44:56 GMT
in x-ms-date
. This would cause the stringToSign
that you are sending and the one computed by the server to mismatch and thus resulting in different authorization header values.
To fix this, please ensure that the value for x-ms-date
header is the same in both stringToSign
and your request headers. I would recommend passing the date object to generateOauthHeader
method and use the same date object in your request headers for x-ms-date
.
Upvotes: 0