Reputation: 3
I am trying to get the details of azure storage account containers from rest API using the credentials of a an registered application in azure. I have built the authentication header parameter and when I am calling the rest API I get this error which says Audience validation failed. Audience did not match.
19:47:16.826 [main] INFO - <-- 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. https://shagundonotdeleteaccount.blob.core.windows.net/?comp=list (288 ms, 426-byte body)
19:47:16.828 [main] INFO - 426-byte body:
?<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:deb7a983-401e-008d-30e7-0c884e000000
Time:2020-04-07T14:17:14.0081901Z</Message><AuthenticationErrorDetail>Audience validation failed. Audience did not match.</AuthenticationErrorDetail></Error>
19:47:16.828 [main] INFO - <-- END HTTP
This is my code:
ApplicationTokenCredentials credentials = new ApplicationTokenCredentials("clientID",
"domain", "secret", AzureEnvironment.AZURE);
RestClient restClient = new RestClient.Builder()
.withBaseUrl(AzureEnvironment.AZURE, AzureEnvironment.Endpoint.RESOURCE_MANAGER)
.withSerializerAdapter(new AzureJacksonAdapter())
.withReadTimeout(150, TimeUnit.SECONDS)
.withLogLevel(LogLevel.BODY)
.withResponseBuilderFactory(new AzureResponseBuilder.Factory())
.withCredentials(credentials).build();
Azure azure = Azure.authenticate(restClient, credentials.domain()).withDefaultSubscription();
Base64 base64 = new Base64();
for (StorageAccount account : azure.storageAccounts().list()) {
URL url = new URL("https://" + account.name() + ".blob.core.windows.net/?comp=list");
StorageAccountKey key = account.getKeys().get(0);
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";
StringBuilder sb = new StringBuilder();
sb.append("GET\n"); // method
sb.append('\n'); // md5 (optional)
sb.append('\n'); // content type
sb.append('\n'); // legacy date
sb.append("x-ms-date:" + date + '\n'); // headers
sb.append("x-ms-version:2017-11-09\n");
sb.append("/" + account.name() + url.getPath() + "?comp=list");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(base64.decode(key.value()), "HmacSHA256"));
String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8"))));
String auth = "SharedKeyLite " + account.name() + ":" + authKey;
OkHttpClient httpClient = restClient.httpClient();
Request request = new Request.Builder()
.url(url)
// .addHeader("content-type", "application/json")
// .addHeader("cache-control", "no-cache")
.addHeader("x-ms-version", "2017-11-09")
.addHeader("x-ms-date", date)
.addHeader("Authorization", auth)
.get()
.build();
okhttp3.Response response = httpClient.newCall(request).execute();
httpClient.newCall(request);
if (!response.isSuccessful()) {
throw new RuntimeException("Request Failed" + response.code() + "\n" + response.message());
}
JSONParser parser = new JSONParser();
String apiResponse = response.body().string();
if (apiResponse != null) {
org.json.simple.JSONObject responseJSON = (org.json.simple.JSONObject) parser.parse(apiResponse);
System.out.println(responseJSON);
}
}
This is the authsignstring
GET
x-ms-date:Tue, 07 Apr 2020 14:17:13 GMT
x-ms-version:2017-11-09
/mystorageaccount/?comp=list
I have tried this as well:
StringBuilder sb = new StringBuilder();
sb.append("GET\n"); // method
sb.append('\n'); // content encoding
sb.append('\n'); // content language
sb.append('\n'); // content length
sb.append('\n'); // md5 (optional)
sb.append('\n'); // content type
sb.append('\n'); // legacy date
sb.append('\n'); // if-modified-since
sb.append('\n'); // if-match
sb.append('\n'); // if-none-match
sb.append('\n'); // if-unmodified-since
sb.append('\n'); // range
sb.append("x-ms-date:" + date + '\n'); // headers
sb.append("x-ms-version:2017-11-09\n");
sb.append("/" + account.name() + "/" + "\ncomp:list");
and
String auth = "SharedKey " + account.name() + ":" + authKey;
Can you help me with this? I am stuck here from a long time. I want the JSON response for the list of containers.
Upvotes: 0
Views: 3457
Reputation: 23161
If you want to use SharedKeyLite auth to call Azur blob rest api, please refer to the following code
String StorageAccountName = "blobstorage0516";
String StorageAccountKey = "";
URL url = new URL("https://" + StorageAccountName + ".blob.core.windows.net/?comp=list");
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";
StringBuilder sb = new StringBuilder();
sb.append("GET\n"); // method
sb.append("\n") ;//Content-MD5
sb.append("\n") ;//Content-Type
sb.append("\n") ;//data
sb.append("x-ms-date:" + date + '\n'); // headers
sb.append("x-ms-version:2017-11-09\n");
sb.append("/" + StorageAccountName + url.getPath() + "?comp=list");
System.out.println(sb.toString()); // print stringtosign
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(StorageAccountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
String authKey=Base64.getEncoder().encodeToString(sha256HMAC.doFinal(sb.toString().getBytes("UTF-8")));
String auth = "SharedKeyLite " + StorageAccountName + ":" + authKey;
System.out.println(auth);
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url(url)
.method("GET", null)
.addHeader("x-ms-version", "2017-11-09")
.addHeader("x-ms-date", date)
.addHeader("Authorization", auth)
.build();
Response response = client.newCall(request).execute();
if(response.isSuccessful()){
System.out.println(response.body().string());
}
When we use RestClient to call the Azure rest api, it will use Azure AD access token to do auth. But the restclient use wrong resource to get Azure AD access token. It uses https://management.core.windows.net/
but we needs https:\\storage.azure.com\
My test code
OkHttpClient httpClient = restClient.httpClient().newBuilder().build();
//OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url(url)
.method("GET", null)
.addHeader("x-ms-version", "2017-11-09")
//.addHeader("x-ms-date", date)
.build();
//Response response = client.newCall(request).execute();
okhttp3.Response response1 = httpClient.newCall(request).execute();
System.out.println(response1.body().string());
Analyze access token via the link
So if you want to use sharekey to call the api, please create a new httoclient.
Upvotes: 1
Reputation: 5549
I see that you want to use the credentials of a registered application in azure. So, you may want to Authorize access to blobs and queues using Azure Active Directory
To be able to read your storage account, you need to at least add Storage Blob Data Reader
role.
public static void main(String[] args) throws IOException {
String clientId = "your application id here";
String clientSecret = "application secret here";
String tenantId = "your tenant id";
String storageResource = "https://storage.azure.com/";
String url = "https://{your_storage_account_name}.core.windows.net/?comp=list";
ApplicationTokenCredentials credentials = new ApplicationTokenCredentials(clientId,
tenantId, clientSecret, AzureEnvironment.AZURE);
String token = credentials.getToken(storageResource);
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url(url)
.get()
.addHeader("x-ms-version", "2017-11-09")
.addHeader("Authorization", "Bearer "+token)
.build();
Response response = client.newCall(request).execute();
if(response.isSuccessful()){
System.out.println(response.body().string());
}
}
Output
Upvotes: 0