1
votes

Facing issue while consuming Azure Storage Table REST Service . Even Resource is present in my storage account while on consuming service (Insert Entity) facing error "The specified resource does not exist" with 400 Response status.

I tried with Jersy restful API for consuming AZURE Storage Service API. As per microsoft documnetation i tried to consume the service. Doc Link : https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key

2) I tried for GET operation too...in that case Response return 400 : reason=The value for one of the HTTP headers is not in the correct format.

3) Same tried with POSTMAN too by that time also facing the same response as above said.

import java.net.*;
import java.util.*;
import java.text.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.codec.binary.Base64;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class InsertEntity_new {
    private static Base64 base64 = new Base64();

    public static void signRequestSK(WebTarget target, String account, String key, Client client) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        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("POST\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:2009-09-19\n");
        sb.append("/" + account + request.getURL().getPath()); //CanonicalizedResource

        // System.out.println(sb.toString());
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256"));
        String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8"))));
        String auth = "SharedKeyLite " + account + ":" + authKey;
        client.property("x-ms-date", date);
        client.property("Authorization", auth);
        client.property("x-ms-version", "2009-09-19");


        String jsonInString = "{\"PartitionKey\":\"mypartitionkey\",\"RowKey\":\"001\",\"Email\":\"[email protected]\",\"PhoneNumber\":\"908265370\"}";
        Response resp = target.request(MediaType.APPLICATION_JSON).post(Entity.entity(jsonInString, MediaType.APPLICATION_JSON));

        System.out.println("Response" + resp);
    }



    public static void main(String args[]) throws Exception {
        String account = "storageacctname";
        String key = "storageaccountkey";

        Client client = ClientBuilder.newClient();
        WebTarget target = client.target("https://" + account + ".table.core.windows.net/mytable");
        signRequestSK(target, account, key,client);

    }
}

I expect my program needs to insert entity to my storage table.

1
Just out of curiosity, why not use Azure Storage Java SDK (github.com/Azure/azure-storage-java)? Writing code to directly invoke REST API needs much more effort.Zhaoxing Lu

1 Answers

0
votes

ramesh.Two formats for the stringToSign Authorization differ slightly.

Shared Key authorization:

enter image description here

Shared Key Lite authorization:

enter image description here

You mixed them to use which is incorrect. Please refer to my working code as below(I used Shared Key Lite authorization):

package rest;

import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class InsertTableEntityTest {

    private static final String account = "***";
    private static final String key = "***";

    public static void main(String args[]) throws Exception {

        String urlString = "https://" + account + ".table.core.windows.net/jay";
        System.out.println(urlString);

        //prepare for the json body
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("PartitionKey", "mypartitionkey");
        jsonObject.put("RowKey", "myrowkey");

        String jsonStr = jsonObject.toString();
        String encoding = "UTF-8";
        System.out.println(jsonStr);
        byte[] data = jsonStr.getBytes(encoding);

        HttpURLConnection conn = (HttpURLConnection) (new URL(urlString)).openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);
        getFileRequest(conn, account, data);
        OutputStream outStream = conn.getOutputStream();

        outStream.write(data);
        outStream.flush();
        outStream.close();
        System.out.println(conn.getResponseCode());
        System.out.println(conn.getResponseMessage());

        BufferedReader br = null;
        if (conn.getResponseCode() != 200) {
            br = new BufferedReader(new InputStreamReader((conn.getErrorStream())));
        } else {
            br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
        }
        System.out.println("Response body : " + br.readLine());
    }

    public static void getFileRequest(HttpURLConnection request, String account, byte[] data) throws Exception {
        SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";

        String stringToSign = date + "\n" +
                "/" + account + request.getURL().getPath();

        System.out.println("stringToSign : " + stringToSign);
        String auth = getAuthenticationString(stringToSign);
        System.out.println(auth);
        request.setRequestMethod("POST");
        request.setRequestProperty("x-ms-date", date);
        request.setRequestProperty("x-ms-version", "2013-08-15");
        request.setRequestProperty("Authorization", auth);
        request.setRequestProperty("Content-Length", String.valueOf(data.length));
        request.setRequestProperty("Content-Type", "application/json");
        request.setRequestProperty("Accept", "application/json");
    }

    private static String getAuthenticationString(String stringToSign) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(Base64.decode(key), "HmacSHA256"));
        String authKey = Base64.encode(mac.doFinal(stringToSign.getBytes("UTF-8")));
//        String auth = "SharedKey " + account + ":" + authKey;
        String auth = "SharedKeyLite  " + account + ":" + authKey;
        return auth;
    }
}

Insert Result:

enter image description here

BTW,the x-ms-version is not optional (The latest is 2018-03-28) to support this format. See Json format in table service.