Creating a Directory in azure storage with REST API - java

I'm trying to create a directory using REST API. Below is the code for Signature. Can you help me to find the issue in the code:
string storageKey = 'storage key';
string storageName = '<storageName>';
Datetime dt = Datetime.now();
string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss')+ ' GMT';
system.debug('formattedDate--'+formattedDate);
string CanonicalizedHeaders = 'x-ms-date:'+formattedDate+'\nx-ms-version:2016-05-31';
string CanonicalizedResource = '/' + storageName + '/<myShareName>/<DirectoryName>\nrestype:directory';
string StringToSign = 'PUT\n\n\n\n\napplication/xml;charset=utf-8\n\n\n\n\n\n\n' + CanonicalizedHeaders+'\n'+CanonicalizedResource;
system.debug('StringToSign--'+StringToSign);
Blob temp = EncodingUtil.base64Decode(storageKey);
Blob hmac = Crypto.generateMac('HmacSHA256',Blob.valueOf(StringToSign),temp ); //StringToSign
system.debug('oo-'+EncodingUtil.base64Encode(hmac));
HttpRequest req = new HttpRequest();
req.setMethod('PUT');
req.setHeader('content-type', 'application/xml;charset=utf-8');
req.setHeader('content-length', '0');
req.setHeader('x-ms-version','2016-05-31' );
req.setHeader('x-ms-date', formattedDate);
string signature = EncodingUtil.base64Encode(hmac);
string authHeader = 'SharedKey <storageName>'+':'+signature;
req.setHeader('Authorization',authHeader);
req.setEndpoint('https://<storageName>.file.core.windows.net/<myShareName>/<DirectoryName>&restype=directory');
Http http = new Http();
HTTPResponse res= http.send(req);
Refer the link for azure documentation: https://learn.microsoft.com/en-us/rest/api/storageservices/create-directory

I wrote the following java code for your reference, and it works well for me.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.microsoft.windowsazure.core.utils.Base64;
public class CreateDirectory {
private static final String account = "jaygong";
private static final String key = "******";
public static void main(String args[]) throws Exception {
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
String urlString = "http://" + account + ".file.core.windows.net/testshare/testdirectory?restype=directory";
// Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP,new InetSocketAddress("127.0.0.1", 8888));
// URL serverUrl = new URL(urlString);
// HttpURLConnection connection = (HttpURLConnection) serverUrl.openConnection(proxy);
HttpURLConnection connection = (HttpURLConnection) (new URL(urlString)).openConnection();
getFileRequest(connection, account, key);
connection.connect();
System.out.println("Response message : " + connection.getResponseMessage());
System.out.println("Response code : " + connection.getResponseCode());
BufferedReader br = null;
if (connection.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((connection.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((connection.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
public static void getFileRequest(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";
String stringToSign = "PUT\n" + "\n" // content encoding
+ "\n" // content language
+ "\n"// content length
+ "\n" // content md5
+ "\n" // content type
+ "\n" // date
+ "\n" // if modified since
+ "\n" // if match
+ "\n" // if none match
+ "\n" // if unmodified since
+ "\n" // range
+ "x-ms-date:" + date + "\nx-ms-version:2015-02-21\n" // headers
+ "/" + account + request.getURL().getPath()+"\nrestype:directory"; // resources
System.out.println("stringToSign : " + stringToSign);
String auth = getAuthenticationString(stringToSign);
System.out.println(auth);
request.setRequestMethod("PUT");
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("x-ms-version", "2015-02-21");
request.setRequestProperty("Authorization", auth);
request.setRequestProperty("Content-Length", "0");
}
private static String getAuthenticationString(String stringToSign) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(Base64.decode(key), "HmacSHA256"));
String authKey = new String(Base64.encode(mac.doFinal(stringToSign.getBytes("UTF-8"))));
String auth = "SharedKey " + account + ":" + authKey;
return auth;
}
}
Notes:
Please note that System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); in the above code is necessary.
I found a parameter called allowRestrictedHeaders from source code, which was originally designed to limit the use of Http Header for security in the design of API.All of the following are limited:
private static final String[] restrictedHeaders = {
/* Restricted by XMLHttpRequest2 */
//"Accept-Charset",
//"Accept-Encoding",
"Access-Control-Request-Headers",
"Access-Control-Request-Method",
"Connection", /* close is allowed */
"Content-Length",
//"Cookie",
//"Cookie2",
"Content-Transfer-Encoding",
//"Date",
"Expect",
"Host",
"Keep-Alive",
"Origin",
// "Referer",
// "TE",
"Trailer",
"Transfer-Encoding",
"Upgrade",
//"User-Agent",
"Via"
};
allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction(
"sun.net.http.allowRestrictedHeaders"))).booleanValue();
Hope it helps you.

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=

Azure Storage Service REST APIs: Create Container

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;
}

Azure Storage service REST API for block Blobs : Content Length Issue

I am trying to use azure storage rest api to push block blob type but issue is about Content Length to be known before hand in order to upload.
Do we have a workaround for cases wherein inputStream need to be relayed without having this length info available.
String accesskey = "accesskey";
String storageAccount = "storageAccount";
String containerName = "containerName";
String workgroupId = UUID.randomUUID().toString();
String objectId = "1." + UUID.randomUUID().toString();
String blobName = getAzureAccessKey(containerName, workgroupId, objectId);
String version = "2018-03-28";
String putData = "testData";
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String currentDate = fmt.format(Calendar.getInstance().getTime()) + " GMT";
String urlResource = "/"+ Paths.get(storageAccount, containerName, blobName).toString();
String headerResource = "x-ms-blob-type:BlockBlob\nx-ms-date:" + currentDate + "\nx-ms-version:" + version;
String putUrl = "https://" + storageAccount + ".blob.core.windows.net/" + containerName + "/" + blobName;
System.out.println(putUrl);
String newline = "\n";
List listToSign = Lists.newArrayList();
listToSign.add("PUT");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add("application/octet-stream");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add("");
listToSign.add(headerResource);
listToSign.add(urlResource);
String stringToSign = String.join(newline, listToSign);
Base64 base64 = new Base64();
System.out.println(stringToSign);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(base64.decode(accesskey), "HmacSHA256"));
String authKey = new String(base64.encode(mac.doFinal(stringToSign.getBytes("UTF-8"))));
String authHeader = "SharedKey " + storageAccount + ":"+ authKey;
System.out.println(authHeader);
InputStreamEntity entity = new InputStreamEntity(
new ByteArrayInputStream(putData.getBytes(StandardCharsets.UTF_8)), -1,
ContentType.APPLICATION_OCTET_STREAM);
// set chunked transfer encoding ie. no Content-length
entity.setChunked(true);
HttpPut httpPut = null;
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.removeRequestInterceptorByClass(org.apache.http.protocol.RequestContent.class);
httpPut = new HttpPut(putUrl);
httpPut.setHeader("Host", storageAccount + ".blob.core.windows.net");
httpPut.setHeader("Transfer-Encoding","chunked");
//httpPut.setHeader("Content-Length","0");
httpPut.setHeader("Content-Type","application/octet-stream");
httpPut.addHeader("x-ms-blob-type", "BlockBlob");
httpPut.addHeader("x-ms-date", currentDate);
httpPut.addHeader("x-ms-version", version);
httpPut.addHeader("Authorization",authHeader);
httpPut.setEntity(entity);
System.out.println("Request Headers");
for (Header header : httpPut.getAllHeaders()) {
System.out.println(header.getName() + ":" + header.getValue());
}
HttpResponse response = httpClient.execute(httpPut);
System.out.println(response.getStatusLine());
for (Header header: response.getAllHeaders()) {
System.out.println(header.getName()+":"+ header.getValue());
}
// Read the contents of an entity and return it as a String.
String content = EntityUtils.toString(response.getEntity());
System.out.println(content);
} finally {
if(httpPut != null ){
httpPut.releaseConnection();
}
}
Response from server
HTTP Error 400. There is an invalid content length or chunk length in the request.
Above code works if i set content length as part of signature and header info.
We can use ByteArrayEntity to get the content length instead of using InputStreamEntity
Here is a simple demo for your reference:
FileInputStream fileInputStream=null;
ByteArrayOutputStream bos = null ;
try {
fileInputStream=new FileInputStream("D:/Test/Test.txt");
bos = new ByteArrayOutputStream();
byte[] bytes=new byte[102400];
int x=0;
while ((x=fileInputStream.read(bytes))!= -1){
bos.write(bytes,0,x);
}
byte[] data = bos.toByteArray();
org.apache.http.entity.ByteArrayEntity byteArrayEntity=new ByteArrayEntity(data);
int contentLength=data.length;
} catch (Exception e) {
e.printStackTrace();
}
As Zhaoxing Lu said above, we can use Java SDK to achieve this, here is the demo that use Java SDK for your reference:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.microsoft.azure.storage.blob.ListBlobItem;
public class Main {
public static final String ConnString="DefaultEndpointsProtocol=https;AccountName=xxxxxxxxb;AccountKey=O7xxxx8e86XQSy2vkvSi/x/e9l9FhLqxxxxjkly1DsQPYY5dF2JrAVxxxxo29ZrrGJA==;EndpointSuffix=core.windows.net";
public static void main(String[] args) {
// TODO Auto-generated method stub
uploadBlob("mycontainer","TechTalk.pptx","E:\\Test\\TechTalk.pptx");
System.out.println("Success");
}
public static void uploadBlob(String containerName, String blobName,String filePath) {
CloudStorageAccount account = null;
CloudBlobContainer container = null;
try {
account = CloudStorageAccount.parse(ConnString);
CloudBlobClient client = account.createCloudBlobClient();
container = client.getContainerReference(containerName);
container.createIfNotExists();
CloudBlockBlob cloudBlockBlob = container.getBlockBlobReference(blobName);
FileInputStream fileinputStream=new FileInputStream(filePath);
cloudBlockBlob.upload(fileinputStream, fileinputStream.available());
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
We can get Java SDK at: Java SDK

Java, URL output different in java output

I have a question about URI and URL
when i pass a url is work good but result is worst need help!!
as my code look like this.
import java.io.*;
import java.net.*;
import java.net.URL;
public class isms {
public static void main(String[] args) throws Exception {
try {
String user = new String ("boo");
String pass = new String ("boo");
String dstno = new String("60164038811"); //You are going compose a message to this destination number.
String msg = new String("你的哈达哈达!"); //Your message over here
int type = 2; //for unicode change to 2, normal will the 1.
String sendid = new String("isms"); //Malaysia does not support sender id yet.
// Send data
URI myUrl = new URI("http://www.isms.com.my/isms_send.php?un=" + user + "&pwd=" + pass
+ "&dstno=" + dstno + "&msg=" + msg + "&type=" + type + "&sendid=" + sendid);
URL url = new URL(myUrl.toASCIIString());
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
// Get the response
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
// Print the response output...
System.out.println(line);
}
rd.close();
System.out.println(url);
} catch (Exception e) {
e.printStackTrace();
}
}
}
the output in web is different..
on my java output is
你的哈达哈达!
but on my the site is
ÄãµÄ¹þ´ï¹þ´ï!
Help!!
String user = new String ("boo");
You don't need to (and shouldn't) do new String in Java—String user = "boo"; is fine.
String msg = new String("你的哈达哈达!");
Writing non-ASCII characters in your source means that you have to get the -encoding flag to javac to match the encoding you have saved your text files with. It is possible you have saved the .java file as UTF-8 but not configured your build environment to use UTF-8 at compile time.
If you are not sure that you've got this right, you can use ASCII-safe \u escapes in the meantime:
String msg = "\u4F60\u7684\u54C8\u8FBE\u54C8\u8FBE!"; // 你的哈达哈达!
Finally:
URI myUrl = new URI("http://www.isms.com.my/isms_send.php?un=" + user + "&pwd=" + pass
+ "&dstno=" + dstno + "&msg=" + msg + "&type=" + type + "&sendid=" + sendid);
When you're putting a URI together you should URL-escape each of the parameters you include in the string. Otherwise any & or other invalid character in the value will break the query. This also allows you to choose what charset is used to create the query string.
String enc = "UTF-8";
URI myUrl = new URI("http://www.isms.com.my/isms_send.php?" +
"un=" + URLEncoder.encode(user, enc) +
"&pwd=" + URLEncoder.encode(pass, enc) +
"&dstno=" + URLEncoder.encode(dstno, enc) +
"&msg=" + URLEncoder.encode(msg, enc) +
"&type=" + URLEncoder.encode(Integer.toString(type), enc) +
"&sendid=" + URLEncoder.encode(sendid, enc)
);
What the right value for enc is depends on the service you are connecting to, but UTF-8 is a good guess.

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