SAS令牌 - 签名字段格式不正确

时间:2018-02-14 11:48:35

标签: java azure

我想生成一个SAS令牌来访问我的blob容器,这是我的一些媒体文件。

所以我使用以下代码创建了一个SharedAccessSignature.java类:

public class SharedAccessSignature
{
    private final String signature;

    private final String signedPermission;
    private final String signedStart;
    private final String signedExpiry;
    private final String signedIdentifier;
    private final String signedIp;
    private final String signedProtocol;
    private final String signedVersion;
    private final String signedResource;

    private SharedAccessSignature(SasBuilder builder)
    {
        signedPermission = formatAsUrlParameter("sp", builder.signedPermission);
        signedStart = formatAsUrlParameter("st", builder.signedStart);
        signedExpiry = formatAsUrlParameter("se", builder.signedExpiry);
        signedIdentifier = formatAsUrlParameter("si", builder.signedIdentifier);
        signedIp = formatAsUrlParameter("sip", builder.signedIp);
        signedProtocol = formatAsUrlParameter("spr", builder.signedProtocol);
        signedVersion = formatAsUrlParameter("sv", builder.signedVersion);
        signedResource = formatAsUrlParameter("sr", builder.signedResource);

        signature = "sig=" + new SasBuilder().encodeUtf8(builder.signature);
    }

    private String formatAsUrlParameter(String parameterKey, String parameterValue)
    {
        if (StringUtils.isNotBlank(parameterValue))
        {
            return parameterKey + "=" + parameterValue + "&";
        }
        return "";
    }

    @Override
    public String toString()
    {
        return new StringBuilder()
            .append(signedVersion)
            .append(signedResource)
            .append(signedStart)
            .append(signedExpiry)
            .append(signedPermission)
            .append(signedIp)
            .append(signedProtocol)
            .append(signedIdentifier)
            .append(signature)
            .toString();
    }

    public static class SasBuilder
    {
        private String signature = "";

        private String signedPermission = "";
        private String signedStart = "";
        private String signedExpiry = "";
        private String canonicalizedResource = "";
        private String signedIdentifier = "";
        private String signedIp = "";
        private String signedProtocol = "";
        private String signedVersion = "";
        private String signedResource = "";

        public SasBuilder signedVersion(String signedVersion)
        {
            this.signedVersion = signedVersion;
            return this;
        }

        public SasBuilder signedPermission(String signedPermission)
        {
            this.signedPermission = signedPermission;
            return this;
        }

        public SasBuilder canonicalizedResource(String canonicalizedResource)
        {
            this.canonicalizedResource = canonicalizedResource;
            return this;
        }

        public SasBuilder signedIp(String signedIp)
        {
            this.signedIp = signedIp;
            return this;
        }

        public SasBuilder signedProtocol(String signedProtocol)
        {
            this.signedProtocol = signedProtocol;
            return this;
        }

        public SasBuilder signedIdentifier(String signedIdentifier)
        {
            this.signedIdentifier = signedIdentifier;
            return this;
        }

        public SasBuilder signedExpiry(String signedExpiry)
        {
            this.signedExpiry = signedExpiry;
            return this;
        }

        public SasBuilder signedStart(String signedStart)
        {
            this.signedStart = signedStart;
            return this;
        }

        public SasBuilder signedResource(String signedResource)
        {
            this.signedResource = signedResource;
            return this;
        }

        public SharedAccessSignature build()
        {
            String toBeAsEnvironmentVariable_securityKey = "....";
            signature = generateSasSignature(toBeAsEnvironmentVariable_securityKey, stringToSign());
            checkPreconditions();
            return new SharedAccessSignature(this);
        }

        private String generateSasSignature(String key, String input)
        {
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
            Encoder encoder = Base64.getEncoder();
            Mac sha256_HMAC = null;
            String hash = null;

            try
            {
                sha256_HMAC = Mac.getInstance("HmacSHA256");
                sha256_HMAC.init(secret_key);
                hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
            }
            catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
            return hash;
        }

        private String stringToSign()
        {
            StringBuilder strToSign = new StringBuilder();
            strToSign.append(signedPermission).append("\n");
            strToSign.append(signedStart).append("\n");
            strToSign.append(signedExpiry).append("\n");
            strToSign.append(canonicalizedResource).append("\n");
            strToSign.append(signedIdentifier).append("\n");
            strToSign.append(signedIp).append("\n");
            strToSign.append(signedProtocol).append("\n");
            strToSign.append(signedVersion).append("\n");
            strToSign.append("").append("\n");
            strToSign.append("").append("\n");
            strToSign.append("").append("\n");
            strToSign.append("").append("\n");
            strToSign.append("");
            return strToSign.toString();
        }

        private void checkPreconditions()
        {
            if (StringUtils.isBlank(signedVersion) || StringUtils.isBlank(signedResource) || StringUtils.isBlank(signedPermission) || StringUtils.isBlank(signedExpiry) || StringUtils.isBlank(signature))
            {
                throw new IllegalStateException("SAS Builder: SignedVersion, signedResource, SignedPermission, SignedExpiry, Signature must be set.");
            }
        }

        private String encodeUtf8(String textToBeEncoded)
        {
            try
            {
                return URLEncoder.encode(textToBeEncoded, "UTF-8");
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
            return textToBeEncoded;
        }
    }
}

然后我尝试生成这样的SAS令牌:

SharedAccessSignature s = new SharedAccessSignature.SasBuilder()
        .signedPermission("rwd")
        .signedStart("2018-01-31T10:48:41Z")
        .signedExpiry("2018-04-06T18:48:41Z")
        .signedVersion("2015-04-05")
        .signedResource("b")
        .canonicalizedResource("/blob/myaccount")
        .signedProtocol("https")
        .build();

结果:

sv=2015-04-05&sr=b&st=2018-01-31T10:48:41Z&se=2018-04-06T18:48:41Z&sp=rwd&spr=https&sig=kd09Y%2FTL5V%2F570VWRuEfq7XbEHvcgo4Z%2F2y9t4OswY8%3D

GET请求:

https://account.blob.core.cloudapi.de/container/filename.mp4?sv=2015-04-05&sr=b&st=2018-01-31T10:48:41Z&se=2018-04-06T18:48:41Z&sp=rwd&spr=https&sig=kd09Y%2FTL5V%2F570VWRuEfq7XbEHvcgo4Z%2F2y9t4OswY8%3D

但是当我使用这个生成的令牌发送该请求时,会出现来自azure的错误:

<Error>
 <Code>AuthenticationFailed</Code>
 <Message>
   Server failed to authenticate the request. Make sure the value of 
   Authorization header is formed correctly including the signature. 
 </Message>
 <AuthenticationErrorDetail>
   Signature did not match. String to sign used was rwd 2018-01-31T10:48:41Z 
   2018-04-06T18:48:41Z /blob/globalweb/..... https 2015-04-05
 </AuthenticationErrorDetail>
</Error>

编辑:

我很绝望......我不明白......这个“字符串到标志”有什么问题?为什么“签名不匹配”?

--------
rwd\n
2018-01-31T10:48:41Z\n
2018-04-06T18:48:41Z\n
/blob/globalweb/videos-martindale\n
\n
\n
https\n
2015-04-05\n
\n
\n
\n
\n

-------

//link: https://globalweb.blob.core.cloudapi.de/videos-martindale/somevideo.mp4?sv=2015-04-05&sr=c&st=2018-01-31T10:48:41Z&se=2018-04-06T18:48:41Z&sp=rwd&spr=https&sig=kd09Y%2FTL5V%2F570VWRuEfq7XbEHvcgo4Z%2F2y9t4OswY8%3D

<Error>
    <Code>AuthenticationFailed</Code>
    <Message>
        Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:644e47a6-001e-0050-3f20-abc0f0000000 Time:2018-02-21T14:31:10.9429817Z
    </Message>
    <AuthenticationErrorDetail>
        Signature did not match. String to sign used was rwd 2018-01-31T10:48:41Z 2018-04-06T18:48:41Z /blob/globalweb/videos-martindale https 2015-04-05
    </AuthenticationErrorDetail>
</Error>

3 个答案:

答案 0 :(得分:1)

主要问题在于generateSasSignature方法。它应解码来自Base64的密钥。如下所示:

public static String generateSasSignature(String key, String input) {
    SecretKeySpec secret_key = new SecretKeySpec(Base64.getDecoder().decode(key), "HmacSHA256");
    Encoder encoder = Base64.getEncoder();
    Mac sha256_HMAC = null;
    String hash = null;

    try {
        sha256_HMAC = Mac.getInstance("HmacSHA256");
        sha256_HMAC.init(secret_key);
        hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
    }
    catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return hash;
}

然后,假设您有兴趣访问名为mycontainer的容器,那么您应该这样做:

SharedAccessSignature s = new SharedAccessSignature.SasBuilder()
    .signedPermission("rwd")
    .signedStart("2018-01-31T10:48:41Z")
    .signedExpiry("2018-04-06T18:48:41Z")
    .signedVersion("2015-04-05")
    .signedResource("c")  // <<---- note here
    .canonicalizedResource("/blob/globalweb/mycontainer") // No ending slash!
    .signedProtocol("https")
    .build();

但是,如果要生成帐户SAS,以下代码可以解决问题:

public static void main(String[] args) throws UnsupportedEncodingException {
    String accountName = "globalweb";
    String signedPermissions = "rl"; //read and list
    String signedService = "b";  //blob
    String signedResType = "sco";  //service, container, objects
    String start = "2018-02-22T17:16:25Z";
    String expiry = "2018-02-28T01:16:25Z";
    String signedIp = "";
    String protocol = "https";
    String signedVersion = "2017-07-29";

    String stringToSign = 
            accountName + "\n" +  
        signedPermissions + "\n" +  
        signedService + "\n" +  
        signedResType + "\n" +  
        start + "\n" +  
        expiry + "\n" +  
        signedIp + "\n" +  
        protocol + "\n" +  
        signedVersion + "\n";

    //outputs SAS Token
    System.out.println(
            "?sv="+signedVersion +
            "&ss="+signedService +
            "&srt="+signedResType + 
            "&sp="+signedPermissions +
            "&st="+start+
            "&se="+expiry+
            "&spr="+protocol+
            "&sig="+
            URLEncoder.encode(SasBuilder.generateSasSignature(MY_KEY_BASE64, stringToSign), "UTF-8"));
}

答案 1 :(得分:0)

遇到了同样的错误,导致我发表了这篇文章:

  

403服务器无法验证请求。请确保正确构成Authorization标头的值(包括签名)。

就我而言,我已经编码的uri上有一个EncodeURI()

删除它也可以修复错误。

答案 2 :(得分:0)

如果使用12.5,请尝试此操作。我可以通过以下方法实现此目的:

{{1}}