Azure Storage Service REST APIs: Create Container - java

Getting the below error while making a call to Create Container.
Response Code : 411 Response Message : Length Required
String stringToSign = "PUT\n\n\n\n0\n\n\n\n\n\n\n\nx-ms-date:" + date + "\nx-ms-version:" + "2014-02-14\n" + "/" + storageAccount + "/"+ "container-create-test"+"\nrestype:container"+"\ntimeout:60";
Java code snippet.
HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection();
connection.setRequestMethod(vMethod);
connection.addRequestProperty("Authorization", authHeader);
connection.addRequestProperty("x-ms-date", date);
connection.addRequestProperty("x-ms-version", "2014-02-14");
connection.addRequestProperty("Content-Length", "0");

Nothing wrong with the format of StringToSign.
411 Response Message : Length Required
This error means you don't add Content-Length:0 header in your http request.
Update
As you work with HttpURLConnection in Java, Content-Length header can't be set manually by default, see this thread.
In case of other trouble, here's the complete sample for you to refer.
public static void putContainer() throws Exception {
// Account info
String accountName = "accountName";
String accountKey = "accountKey";
// Request Uri and Method
String containerName = "containerName";
String requestUri = "https://"+accountName+".blob.core.windows.net/"+containerName+"?restype=container&timeout=60";
HttpURLConnection connection = (HttpURLConnection) (new URL(requestUri)).openConnection();
connection.setRequestMethod("PUT");
// Request Headers
// 1. x-ms-version, recommend to use the latest version if possible
String serviceVersion = "2018-03-28";
// 2. x-ms-date
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";
// 3. Authorization
String authKeyFormat = "SharedKey";
String caHeader = "x-ms-date:"+date+"\nx-ms-version:"+serviceVersion+"\n";
String caResource = "/"+accountName+"/"+containerName+"\nrestype:container\ntimeout:60";
String signStr = "PUT\n\n\n\n\n\n\n\n\n\n\n\n"+caHeader+caResource;
String authorization = getAuthorization(accountName, authKeyFormat, signStr, accountKey);
// Send request
connection.setRequestProperty("x-ms-version", serviceVersion);
connection.setRequestProperty("x-ms-date",date);
connection.setRequestProperty("Authorization", authorization);
// Send 0 byte, code sets Content-Length:0 automatically
connection.setDoOutput(true);
connection.setFixedLengthStreamingMode(0);
System.out.println("Response message : " + connection.getResponseMessage());
System.out.println("Response code : " + connection.getResponseCode());
}
private static String getAuthorization(String accountName, String authKeyFormat, String signStr, String accountKey) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), "HmacSHA256");
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
sha256HMAC.init(secretKey);
String signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes("UTF-8")));
return authKeyFormat+" "+accountName+":"+signature;
}

Related

Session Control Java - GET Cookie from Request and Pass it to POST request

I am actually stuck in a data fetching API. In its documentation it has been mentioned that I have to make a call to the login API first then using its Authorization header and cookie (returned in Login API response) I have to make subsequent calls (keeping session control).
The first call is successful and I have received the Cookie i.e. X-SESSIONID and Authorization Header from response Headers. But the subsequent call is returning: 401 Unauthorized even I am passing session Id and authorization header.
May be I am doing something wrong in session management or making subsequent call. Can someone help?
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.*;
import org.apache.commons.codec.digest.*;
import org.codehaus.jettison.json.*;
import com.sun.jersey.api.*;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
public class DigestClient {
// Dividing into two parts because we need to send the last part of uri in our
// second request to service.
static String baseUri = "http://data.crea.ca";
static String subUri = "/Login.svc/Login";
public static void main(String[] args) throws JSONException, IOException {
ClientConfig cc = new DefaultClientConfig();
Client client = Client.create(cc);
WebResource webResource = client.resource(baseUri + subUri);
ClientResponse response = webResource.get(ClientResponse.class);
// Basically in Digest-Authentication mechanism, we hit the rest service two
// times.
// First time with No Authentication, which returns some values (qop, nonce,
// realm) which are used as inputs in second call to rest service.
/*--------------- First call-----------------*/
// We get 401, Unauthorized
System.out.println(response.getStatus() );
// Here is the complete header information
System.out.println(response.getHeaders());
// We need "WWW-Authenticate" part information for our second call to rest
System.out.println("WWW-Authenticate: \t" + response.getHeaders().get("www-Authenticate"));
String noAuthResp = response.getHeaders().get("www-Authenticate").toString();
noAuthResp = noAuthResp.replace("Digest ", "");
noAuthResp = noAuthResp.replace('[', '{');
noAuthResp = noAuthResp.replace(']', '}');
// Creating a JSONObject for easy information retrieval
JSONObject resp = new JSONObject(noAuthResp);
/*--------------- Second call-----------------*/
// Here client has to set the fields which was returned from the first call
String user = "CXLHfDVrziCfvwgCuL8nUahC"; // username
String password = "mFqMsCSPdnb5WO1gpEEtDCHH"; // password
String realm = resp.getString("realm"); // realm value from the first rest-call response
String qop = resp.getString("qop"); // qop value from the first rest-call response
String nonce = resp.getString("nonce"); // nonce value from the first rest-call response
String opaque = "";//resp.getString("opaque"); // Some times if we don't get this value, set it with ""
String algorithm = "MD5"; // The algorithm set by the client
int nonceCount = 678; // Some numerical input from the client
String clientNonce = "afdjas0"; // Some random text from the client for encryption
String method = "GET"; // HTTP method
String ha1 = new DigestClient().formHA1(user, realm, password);
String ha2 = new DigestClient().formHA2(method, subUri);
String responseCode = new DigestClient().generateResponse(ha1, nonce, nonceCount, clientNonce, qop, ha2);
// Header to be sent to the service
String value = "Digest username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\""
+ subUri + "\", qop=" + qop + ", nc=" + nonceCount + ", cnonce=\"" + clientNonce + "\", response=\""
+ responseCode + "\", opaque=\"" + opaque + "\"";
System.out.println("Digest Header: " + value );
// Hitting the service
response = webResource.header("authorization", value).type(MediaType.TEXT_PLAIN).accept("*")
.get(ClientResponse.class);
System.out.println("\nComplete Response:\n" + response + "\n");
String output = response.getEntity(String.class);
MultivaluedMap<String, String> responseHeaders = response.getHeaders();
System.out.println("Response header: " + prepareParameters(responseHeaders).toString());
System.out.println("Cookie: " + response.getCookies().get(1).toString());
// System.out.println("Response Text: " + output);
String sessionId = response.getCookies().get(1).toString();
/****************************SUBSEQUENT CALL (WHERE I AM PASSING SESSION ID AS COOKIE AND AUTHORIZATION HEADER ***************************/
System.out.println("Getting metadata..");
System.out.println("SessionID: " + sessionId.split(";")[0]);
System.out.println("Digest Header: " + value);
String sid = sessionId.split(";")[0];
String request = "<Employee><Name>Sunil</Name></<Employee>";
webResource = client.resource("http://data.crea.ca/Metadata.svc/GetMetadata");
ClientResponse response2 = webResource.get(ClientResponse.class);
response2 = webResource.header("authorization", value).header("Cookie", sid).type(MediaType.APPLICATION_XML).accept("*")
.post(ClientResponse.class, request);
if (response2.getStatus() != 200) {
System.out.println("MetaDataError: " + response2.getStatus());
}
String output2 = response2.getEntity(String.class);
//
// URL url = new URL("http://data.crea.ca/Metadata.svc/GetMetadata");
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//
// // Set timeout as per needs
// connection.setConnectTimeout(20000);
// connection.setReadTimeout(20000);
//
// // Set DoOutput to true if you want to use URLConnection for output.
// // Default is false
// connection.setDoOutput(true);
//
// connection.setUseCaches(true);
// connection.setRequestMethod("POST");
//
// // Set Headers
// connection.setRequestProperty("Accept", "application/xml");
// connection.setRequestProperty("Content-Type", "application/xml");
// connection.setRequestProperty("Authorization", value);
// connection.setRequestProperty("Cookie", sid);
//
// // Write XML
// OutputStream outputStream = connection.getOutputStream();
// byte[] b = request.getBytes("UTF-8");
// outputStream.write(b);
// outputStream.flush();
// outputStream.close();
//
// // Read XML
// InputStream inputStream = connection.getInputStream();
// byte[] res = new byte[2048];
// int i = 0;
// StringBuilder response2 = new StringBuilder();
// while ((i = inputStream.read(res)) != -1) {
// response2.append(new String(res, 0, i));
// }
// inputStream.close();
System.out.println("Response of metadata= " + output2.toString());
//getMetadata( response.getCookies().get(1).toString(), value);
}
public static void getMetadata(String sessionId, String header) throws IOException {
System.out.println("Getting metadata..");
System.out.println("SessionID: " + sessionId.split(";")[0]);
System.out.println("Digest Header: " + header);
String sid = sessionId.split(";")[0];
String request = "<Employee><Name>Sunil</Name></<Employee>";
URL url = new URL("http://data.crea.ca/Metadata.svc/GetMetadata");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Set timeout as per needs
connection.setConnectTimeout(20000);
connection.setReadTimeout(20000);
// Set DoOutput to true if you want to use URLConnection for output.
// Default is false
connection.setDoOutput(true);
connection.setUseCaches(true);
connection.setRequestMethod("POST");
// Set Headers
connection.setRequestProperty("Accept", "application/xml");
connection.setRequestProperty("Content-Type", "application/xml");
connection.setRequestProperty("Authorization", header);
connection.setRequestProperty("Cookie", sid);
// Write XML
OutputStream outputStream = connection.getOutputStream();
byte[] b = request.getBytes("UTF-8");
outputStream.write(b);
outputStream.flush();
outputStream.close();
// Read XML
InputStream inputStream = connection.getInputStream();
byte[] res = new byte[2048];
int i = 0;
StringBuilder response = new StringBuilder();
while ((i = inputStream.read(res)) != -1) {
response.append(new String(res, 0, i));
}
inputStream.close();
System.out.println("Response of metadata= " + response.toString());
}
private static Map<String,String> prepareParameters(MultivaluedMap<String, String> queryParameters) {
Map<String,String> parameters = new HashMap<String,String>();
for(String str : queryParameters.keySet()){
parameters.put(str, queryParameters.getFirst(str));
}
return parameters;
}
// For generating HA1 value
public String formHA1(String userName, String realm, String password) {
String ha1 = DigestUtils.md5Hex(userName + ":" + realm + ":" + password);
return ha1;
}
// For generating HA2 value
public String formHA2(String method, String uri) {
String ha2 = DigestUtils.md5Hex(method + ":" + uri);
return ha2;
}
// For generating response at client side
public String generateResponse(String ha1, String nonce, int nonceCount, String clientNonce, String qop,
String ha2) {
String response = DigestUtils
.md5Hex(ha1 + ":" + nonce + ":" + nonceCount + ":" + clientNonce + ":" + qop + ":" + ha2);
return response;
}
}
Output
401
{Cache-Control=[private], Server=[Microsoft-IIS/10.0], WWW-Authenticate=[Digest realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", qop="auth"], X-AspNet-Version=[4.0.30319], Set-Cookie=[ARRAffinity=eb0215f43a7fdf079429ba39da6cd0de66afa6b4085fd1016024d539de9de1b3;Path=/;HttpOnly;Domain=data.crea.ca, ASP.NET_SessionId=y5ylejdp2f5q3siitg410lk5; path=/; HttpOnly; SameSite=Lax], Content-Length=[0], Date=[Thu, 07 Apr 2022 18:24:38 GMT], X-Powered-By=[ASP.NET], Content-Type=[application/xml; charset=utf-8]}
WWW-Authenticate: [Digest realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", qop="auth"]
Digest Header: Digest username="CXLHfDVrziCfvwgCuL8nUahC", realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", uri="/Login.svc/Login", qop=auth, nc=678, cnonce="afdjas0", response="f7d2445a27173c5e876e3b2833369e64", opaque=""
Complete Response:
GET http://data.crea.ca/Login.svc/Login returned a response status of 200 OK
Response header: {RETS-Request-ID=, Cache-Control=private, Server=Microsoft-IIS/10.0, X-AspNet-Version=4.0.30319, Set-Cookie=ARRAffinity=eb0215f43a7fdf079429ba39da6cd0de66afa6b4085fd1016024d539de9de1b3;Path=/;HttpOnly;Domain=data.crea.ca, RETS-Version=RETS/1.7.2, Content-Length=591, Date=Thu, 07 Apr 2022 18:24:38 GMT, X-Powered-By=ASP.NET, Content-Type=text/xml}
Cookie: X-SESSIONID=95b7bcc5-6d47-4494-936b-5bfaf3485679;Version=1
Getting metadata..
SessionID: X-SESSIONID=95b7bcc5-6d47-4494-936b-5bfaf3485679
Digest Header: Digest username="CXLHfDVrziCfvwgCuL8nUahC", realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", uri="/Login.svc/Login", qop=auth, nc=678, cnonce="afdjas0", response="f7d2445a27173c5e876e3b2833369e64", opaque=""
MetaDataError: 401
Response of metadata=

HTTP Status 415 - RESTEASY001055: Cannot consume content type

I'm trying to upload an image (eg tiff, scan in PDF). I did a project in SoapUI and the picture was sent correctly, unfortunately my java code is wrong ... receives the 'HTTP Status 415 - RESTEASY001055: Cannot consume content type' message. The image is stored in the database as a blob.
For code 200, I should get json
private void sendRequestToRestService(byte[] imgageBlob) throws IOException {
String user = "testUser";
String password = "testPass";
String restServiceAddressToUpload = //The address of the service on which I want to send the file;
String authData = user + ":" + password;
Tika tika = new Tika();
String mimetype = tika.detect(imgageBlob);
String auth = user + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("ISO-8859-1")));
String authHeader = "Basic " + new String(encodedAuth);
// ByteArrayEntity entity = new ByteArrayEntity(imgageBlob);
Client client = ClientBuilder.newClient();
Response res = client.target(restServiceAddressToUpload)
.request(MediaType.MULTIPART_FORM_DATA)
.header(HttpHeaders.AUTHORIZATION, authHeader)
.post(Entity.entity(imgageBlob, mimetype));
if (res.getStatus() == 200) {
String readEntity = res.readEntity(String.class);
// ...
} else {
String readEntity = res.readEntity(String.class);
// ...
}
}
You need to add the Content-Type header

Java rest calls with get parameters

How do you add parameters or extra line code to rest calls in java.
GET /ObjectName HTTP/1.1
Host: BucketName.s3.amazonaws.com
Date: date Authorization: authorization string (see Authenticating Requests (AWS Signature Version
4)) Range:bytes=byte_range
Putting /Objectname after GET on the set method causes an error.
The code i am using is;
public void getObject() throws Exception {
String fmt = "EEE, dd MMM yyyy HH:mm:ss ";
SimpleDateFormat df = new SimpleDateFormat(fmt, Locale.US);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
String ob2 = "/bucket/test.txt";
String bucket = "/bucket";
String method = "GET";
String contentMD5 = "";
String contentType = "";
String date = df.format(new Date()) + "GMT";
// Generate signature
StringBuffer buf = new StringBuffer();
buf.append(method).append("\n");`enter code here`
buf.append(contentMD5).append("\n");
buf.append(contentType).append("\n");
buf.append(date).append("\n");
buf.append(ob2);
String signature = sign(buf.toString());
HttpURLConnection httpConn = null;
URL url = new URL("https”,”s3demo.s3demosite.com",443,bucket);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setDoInput(true);
httpConn.setDoOutput(true);
httpConn.setUseCaches(false);
httpConn.setDefaultUseCaches(false);
httpConn.setAllowUserInteraction(true);
httpConn.setRequestMethod(method);
httpConn.setRequestProperty("Date", date);
httpConn.setRequestProperty("Content-Length", "0");
String AWSAuth = "AWS " + keyId + ":" + signature;
httpConn.setRequestProperty("Authorization", AWSAuth);
// Send the HTTP PUT request.
int statusCode = httpConn.getResponseCode();
InputStream err = httpConn.getErrorStream();
InputStream is = null;
is = httpConn.getInputStream();
int ch;
StringBuffer sb = new StringBuffer();
while ((ch = err.read()) != -1) {
sb.append((char) ch);
}
if ((statusCode/100) != 2)
{
// Deal with S3 error stream.
InputStream in = httpConn.getErrorStream();
System.out.println("Error: "+errorStr);
}
else {
System.out.println("download worked”);
}
}
In REST Services you can pass a parameter in two ways.
as path variables
As a query argument. Example: GET /students/tom or GET /students?name=tom

Amazon S3 bucket sub objects REST and Java without SDK

I want to get a list of objects and folders that are in a Bucket in Amazon S3 but I can't, I should not use Amazon S3 SDK.
It's important not to use SDK and only with Rest and Java should I sent a Request and then receive a Response.
I have a method like this :
public String BucketSubList(String strPath) throws Exception {
String answer = null;
// S3 timestamp pattern.
String fmt = "EEE, dd MMM yyyy HH:mm:ss ";
SimpleDateFormat df = new SimpleDateFormat(fmt, Locale.US);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
// Data needed for signature
String method = "GET";
String contentMD5 = "";
String contentType = "";
String date = df.format(new Date()) + "GMT";
String bucket = "/" + strPath + "/";
// Generate signature
StringBuffer buf = new StringBuffer();
buf.append(method).append("\n");
buf.append(contentMD5).append("\n");
buf.append(contentType).append("\n");
buf.append(date).append("\n");
buf.append(bucket);
// try {
String signature = sign(buf.toString());
// Connection to s3.amazonaws.com
URL url = new URL("http", "s3.amazonaws.com", 80, bucket);
HttpURLConnection httpConn = null;
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setDoInput(true);
httpConn.setDoOutput(true);
httpConn.setUseCaches(false);
httpConn.setDefaultUseCaches(false);
httpConn.setAllowUserInteraction(true);
httpConn.setRequestMethod(method);
httpConn.setRequestProperty("Date", date);
// httpConn.setRequestProperty("Content-Type", "text/plain");
String AWSAuth = "AWS " + keyId + ":" + signature;
httpConn.setRequestProperty("Authorization", AWSAuth);
// Send the HTTP PUT request.
int statusCode = httpConn.getResponseCode();
System.out.println(statusCode);
if ((statusCode / 100) != 2) {
// Deal with S3 error stream.
InputStream in = httpConn.getErrorStream();
String errorStr = getS3ErrorCode(in);
System.out.println("Error: " + errorStr);
} else {
answer = "";
// System.out.println("Bucket listed successfully");
InputStream inst = httpConn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inst));
String decodedString;
while ((decodedString = in.readLine()) != null) {
answer += decodedString;
System.out.println(answer);
}
in.close();
}
return answer;
}
Without knowing what your problem is, i just can give you the link to the AWS S3 Rest API.
This method does what you want.
I hope this might help you.
Otherwise please give us some more information about your problem.

Java Azure Request Signing

I am using the latest Azure SDK Storage emulator. I am trying to sign a request to my blob. When I run the below code I am getting auth error.
I can't figure out what is wrong, although I have checked several times that the code conforms to the Azure SDK blob access specs.
Here is the console output:
GET
x-ms-date:Sun, 23 Sep 2012 04:04:07 GMT
/devstoreaccount1/tweet/?comp=list
SharedKey devstoreaccount1:Hx3Pm9knGwCb4Hs9ftBX/+QlX0kCGGlUOX5g6JHZ9Kw=
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Here is the code:
public static void signRequest(HttpURLConnection request, String account, String key) 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";
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(request.getURL().getPath() + "/tweet/?comp=list"); // resource TODO: "?comp=..." if present
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 = "SharedKey " + account + ":" + authKey;
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("Authorization", auth);
request.setRequestMethod("GET");
System.out.println(auth);
}
public static void main(String args[]) throws Exception
{
String account = "devstoreaccount1";
String key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
HttpURLConnection connection = (HttpURLConnection) (new URL("http://localhost:10000/devstoreaccount1")).openConnection();
signRequest(connection, account, key);
connection.connect();
System.out.println(connection.getResponseMessage());
}
After feedback from Gaurav and Smarx, here is the code, I still get the same error. Can you show me some code? It is hard to understand otherwise.
public static void sign(HttpURLConnection request, String account, String key, String url) 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";
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:2009-09-19\n"); // headers
sb.append("/devstoreaccount1/devstoreaccount1/\n$maxresults:1\ncomp:list\nrestype:container"); // resource TODO: "?comp=..." if present
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;
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("Authorization", auth);
request.setRequestMethod("GET");
System.out.println(auth);
}
public static void main(String args[]) throws Exception
{
String account = "devstoreaccount1";
String key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
String url = "http://127.0.0.1:10000/devstoreaccount1/?restype=container&comp=list&$maxresults=1";
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
sign(connection, account, key, url);
connection.connect();
System.out.println(connection.getResponseMessage());
}
EDIT Where did Gaurav's answer go? :-) I believe he already answered and mentioned that you appear to be constructing a Shared Key Lite signature and should thus use "SharedKeyLite" in your authorization header.
I think Gaurav is right in his answer, but I noticed three other issues:
You seem to be making a call to http://localhost/devstoreaccount1, but you're computing a signature for http://localhost/devstoreaccount1/tweet/?comp=list. Make sure the URLs match up.
For the storage emulator, I think your canonicalized resource should actually be /devstoreaccount1/devstoreaccount1/tweet/?comp=list. (Note the repetition of the account name.) It should generally be /<account>/<path>, and for the storage emulator, the account name shows up in the path.
Where's the x-ms-version header? I believe that's required.
UPDATE Here's some working code with two methods, one that uses Shared Key and one that uses Shared Key Lite. Hopefully this clears things up. Note that to use the storage emulator, you'll want to switch the URL back to localhost:10000/devstoreaccount1. The signature code should still work for the emulator, but I haven't tested it. The Base64 library came from here: http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html.
import java.net.*;
import java.util.*;
import java.text.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.Base64;
public class Test
{
private static Base64 base64 = new Base64();
public static void signRequestSK(HttpURLConnection request, String account, String key) 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";
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:2009-09-19\n");
sb.append("/" + account + request.getURL().getPath() + "\ncomp:list");
//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 = "SharedKey " + account + ":" + authKey;
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("x-ms-version", "2009-09-19");
request.setRequestProperty("Authorization", auth);
request.setRequestMethod("GET");
System.out.println(auth);
}
public static void signRequestSKL(HttpURLConnection request, String account, String key) 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";
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:2009-09-19\n");
sb.append("/" + account + request.getURL().getPath() + "?comp=list");
//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;
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("x-ms-version", "2009-09-19");
request.setRequestProperty("Authorization", auth);
request.setRequestMethod("GET");
System.out.println(auth);
}
public static void main(String args[]) throws Exception
{
String account = args[0];
String key = args[1];
HttpURLConnection connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection();
signRequestSKL(connection, account, key);
connection.connect();
System.out.println(connection.getResponseMessage());
connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection();
signRequestSK(connection, account, key);
connection.connect();
System.out.println(connection.getResponseMessage());
}
}
Based on the documentation here: http://msdn.microsoft.com/en-us/library/windowsazure/dd179428, I believe there's an issue with the way you're constructing "Canonicalized Resource String" part of your signature.
A few things I noticed:
You're appending query string parameter (comp=list) to this string
which you should not.
If you're constructing this string against development storage
(which you're doing), "devstoreaccount1" should come twice.
For example, if I am trying to list just 1 blob container in my development storage account, this should be the canonocalized resource string based on the following request URL - 127.0.0.1:10000/devstoreaccount1/?restype=container&comp=list&$maxresults=1:
/devstoreaccount1/devstoreaccount1/
$maxresults:1
comp:list
restype:container

Categories