locked
Mismatch on Java Generated Signature RRS feed

  • Question

  • Hello,

    I'm using the java function on generate-sas-token documentation:

    public static String getHMAC256(String key, String input) {
        Mac sha256_HMAC = null;
        String hash = null;
        try {
            sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            Encoder encoder = Base64.getEncoder();
    
            hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
    
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    
        return hash;
    }

    Using the following inputs it returns:

    String key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
    
    String input = "PUT\n\n\n\n\napplication/xml\n\n\n\n\n\n\nx-ms-date:Tue, 27 Aug 2019 09:39:42 GMT\nx-ms-version:2018-03-28\n/devstoreaccount1/devstoreaccount1/teste\nrestype:container";

    hash = UkxwTqPuyd2E2VGiVSLk0axe7ZUAs7/mQB2QPo4VSrg=

    Which is not correct according to the api: "Server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature."

    Using node.js I can generate a valid key:

    const crypto = require("crypto-js");
    const storageKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
    const input = "PUT\n\n\n\n\napplication/xml\n\n\n\n\n\n\nx-ms-date:Tue, 27 Aug 2019 09:39:42 GMT\nx-ms-version:2018-03-28\n/devstoreaccount1/devstoreaccount1/teste\nrestype:container";
    const signatureBytes = crypto.HmacSHA256(input, crypto.enc.Base64.parse(storageKey));
    const signatureEncoded = signatureBytes.toString(crypto.enc.Base64);
    console.log("Encoded Signature", signatureEncoded);

    It outputs:

    Encoded Signature w6UkEfJvwquenZaQG6dXAyn3j6/0CFxSvYCkQNmDx+I=

    This signature is valid.

    Is there something wrong is the snippet in the documentation?

    There should be an additional transformation required but I can't see where.

    Help!

    Tuesday, August 27, 2019 4:36 PM

Answers

  • Hi,

    Finally got the correct procedure:

      public static String getHMAC256(String key, String input) {
        String hash = null;
        try {
            // key is in base64 format needs decoding
            Decoder decoder = Base64.getDecoder();
            byte[] keyArray = decoder.decode(key);
            //
    
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");        
            SecretKeySpec secret_key = new SecretKeySpec(keyArray, "HmacSHA256");
            sha256_HMAC.init(secret_key);
    
            Encoder encoder = Base64.getEncoder();
            hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
      }
    As I said in the last post, the problem was that the accountKey was being passed in base64 format and needed to be decoded first.

    Regards.

    • Marked as answer by Patronus Wednesday, August 28, 2019 11:19 AM
    Wednesday, August 28, 2019 11:19 AM

All replies

  • Trying on C# returns the same key as Java does, which is invalid for Azure:

    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    
    class MainClass {
      public static void Main (string[] args) {
        string stringToSign = "PUT\n\n\n\n\napplication/xml\n\n\n\n\n\n\nx-ms-date:Tue, 27 Aug 2019 09:39:42 GMT\nx-ms-version:2018-03-28\n/devstoreaccount1/devstoreaccount1/teste\nrestype:container";
        string key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
        
        HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
        var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        Console.WriteLine (signature);
      }
    }
    UkxwTqPuyd2E2VGiVSLk0axe7ZUAs7/mQB2QPo4VSrg=



    • Edited by Patronus Wednesday, August 28, 2019 10:15 AM
    Wednesday, August 28, 2019 10:15 AM
  • Just for clarification: Check the time zone and Shared access signature (SAS) would been expired. updating (actually making a new one) the shared access signature in portal.azure.com by adding a year (or more) for end date in the future.

    Hope this helps! 

    Kindly let us know if the above helps or you need further assistance on this issue.

    ------------------------------------------------------------------------------------------

    Do click on "Mark as Answer" and Upvote on the post that helps you, this can be beneficial to other community members.

    Wednesday, August 28, 2019 10:36 AM
  • Hello Sumanth,

    The issue appears not be on Azure side, since with the same accountKey and stringtostring I can generate valid signatures with node.js;

    There is something that I'm missing when I translate to java (or c#), based on the official documentation, that breaks the key generation.

    Further tests with node.js show that the difference is being caused by missing the encoding to base64 on the accountKey, still working on how to get this done correctly in java.

    Regards.

    Wednesday, August 28, 2019 11:04 AM
  • Hi,

    Finally got the correct procedure:

      public static String getHMAC256(String key, String input) {
        String hash = null;
        try {
            // key is in base64 format needs decoding
            Decoder decoder = Base64.getDecoder();
            byte[] keyArray = decoder.decode(key);
            //
    
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");        
            SecretKeySpec secret_key = new SecretKeySpec(keyArray, "HmacSHA256");
            sha256_HMAC.init(secret_key);
    
            Encoder encoder = Base64.getEncoder();
            hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
      }
    As I said in the last post, the problem was that the accountKey was being passed in base64 format and needed to be decoded first.

    Regards.

    • Marked as answer by Patronus Wednesday, August 28, 2019 11:19 AM
    Wednesday, August 28, 2019 11:19 AM
  • @Patronus Glad to hear that issue got fixed. Appreciate for sharing the steps which helped you, this would certainly benefit other community members

    Wednesday, August 28, 2019 12:09 PM