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.: [AuthenticationFailedServer 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=
");