Java Azure Request Signing

混江龙づ霸主 提交于 2019-12-03 14:15:21

问题


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

回答1:


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:

  1. 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.
  2. 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.
  3. 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());
    }
}



回答2:


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



来源:https://stackoverflow.com/questions/12549634/java-azure-request-signing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!