I'm trying to create a SAS url for my windows media services file using custom ruby code (ported from official php library for azure). The code looks like that:
def create_signature(path = '/', resource = 'b', permissions = 'r', start = '', expiry = '', identifier = '')
# If resource is a container, remove the last part (which is the filename)
path = path.split('/').reverse.drop(1).reverse.join('/') if resource == 'c'
canonicalizedResource = "/mediasvc78m7lfh2gnn2x/#{path}"
stringToSign = []
stringToSign << permissions
stringToSign << start
stringToSign << expiry
stringToSign << canonicalizedResource
stringToSign << identifier
stringToSign = stringToSign.join("\n")
signature = OpenSSL::HMAC.digest('sha256', wms_api_key, stringToSign.encode(Encoding::UTF_8))
signature = Base64.encode64(signature)
return signature
end
def createSignedQueryString(path = '/', query_string = '', resource = 'b', permissions = 'r', start = '', expiry = '', identifier = '')
base = 'https://mediasvc78m7lfh2gnn2x.blob.core.windows.net'
uri = Addressable::URI.new
# Parts
parts = {}
parts[:st] = URI.unescape(start) unless start == ''
parts[:se] = URI.unescape(expiry)
parts[:sr] = URI.unescape(resource)
parts[:sp] = URI.unescape(permissions)
parts[:si] = URI.unescape(identifier) unless identifier == ''
parts[:sig] = URI.unescape( create_signature(path, resource, permissions, start, expiry) )
uri.query_values = parts
return "#{base}/#{path}?#{uri.query}"
end
When running:
puts createSignedQueryString(
'asset-12514a3b-565f-4150-9543-e3c2b4531428/video.mp4',
nil,
'b',
'r',
(Time.now - 5*60).utc.iso8601,
(Time.now + 30*60).utc.iso8601
)
it gives me the following url: https://mediasvc78m7lfh2gnn2x.blob.core.windows.net/asset-12514a3b-565f-4150-9543-e3c2b4531428/video.mp4?se=2014-02-20T12%3A59%3A19Z&sig=RDc9nVMuf1dy%2BPrnzCkA8pZfgry2ZwrF08u9itf4v%2FA%3D%0A&sp=r&sr=b&st=2014-02-20T12%3A24%3A19Z
When i try to point my browser to it, i'm getting:
<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:ee7fd18f-cd1f-4179-8a58-c8b746d0549c Time:2014-02-20T12:29:27.0468171Z
</Message>
<AuthenticationErrorDetail>
Signature did not match. String to sign used was r 2014-02-20T12:24:19Z 2014-02-20T12:59:19Z /mediasvc78m7lfh2gnn2x/asset-12514a3b-565f-4150-9543-e3c2b4531428/video.mp4
</AuthenticationErrorDetail>
</Error>
Have you any idea what can be causing that error (or how to debug it?) and how to deal with that? Thanks in advance.
Assuming you're using the storage key directly from the portal and using that in your wms_api_key
variable (or in other words your wms_api_key
is a Base64 encoded string, I believe you would need to convert it first as byte array for calculating signature. You would need to do something like:
signature = OpenSSL::HMAC.digest('sha256', Base64.strict_decode64(wms_api_key), stringToSign.encode(Encoding::UTF_8))
This is based on the source code for Azure SDK for Ruby
on Github.
UPDATE
One more issue I discovered. If you notice your SAS URL, you would notice %0A
at the end of sig
query string parameter which is essentially a new line character. Not sure why this is coming but I think it is inserted automatically when you do the following:
signature = Base64.encode64(signature)
However if I use strict_encode64
instead of encode64
method, this is not inserted and everything works great. So try the following code:
signature = Base64.strict_encode64(signature)
I just tried it and it worked great for me.