Question

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.

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top