java - SAS token - 签名字段格式不正确

标签 java azure

我想生成一个 SAS token 来访问我的 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 token :

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

获取请求:

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

但是当我使用生成的 token 发送该请求时, 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>

最佳答案

主要问题出在您的generateSasSignature方法上。它应该从 Base64 解码 key 。就像下面这样:

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

但是,如果您想生成 Account 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"));
}

关于java - SAS token - 签名字段格式不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48786501/

相关文章:

java - 运行列表时出现堆栈溢出错误

Java web 启动小程序无法与本地主机 servlet 连接

java - 如何使用java将多个多页tiff文件合并为单个pdf?

java - 如果语句不能正常工作

c# - Azure 服务总线权限是否传递到其子主题和队列

azure - Blob 存储可以在不同区域之间共享吗?

linq - TableServiceContext和动态查询

java - Android 模拟器错误,控制台错误日志重复错误

c# - Azure Key Vault .NET - 找不到方法

postgresql - Azure DevOps CI 中 Azure 服务集成的自动化集成测试