2016-02-14 51 views
2

我目前正試圖讓我的elixir網絡服務器爲Google雲端存儲生成已簽名的網址,以便我可以生成過期的文件網址。 不幸的是,當我嘗試使用生成的URL我收到以下錯誤:如何使用Elixir或Erlang創建Google Cloud Storage簽名的URL?

<Code>SignatureDoesNotMatch</Code> 
<Message> 
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. 
</Message> 

我能夠生成簽署的網址是通過gsutil工具工作,儘管它是相當緩慢的,並且還可以通過給出的Python的例子在這裏:

Google Cloud Storage Signed URLs Example

我的靈藥當前實現基於上述Python的例子,看起來像這樣:

@default_expiration 1000 
    def construct_string(http_verb, content_md5, content_type, expiration_timestamp, canonicalized_extension_headers, canonicalized_resource) do 
    "#{http_verb}\n 
    #{content_md5}\n 
    #{content_type}\n 
    #{expiration_timestamp}\n 
    #{canonicalized_extension_headers} 
    #{canonicalized_resource}" 
    end 

    def load_secret_pem do 
    load_local_key_file("/path/to/key") 
    end 

    def load_local_key_file(path) do 
    {ok, pem_bin} = File.read(path) 
    [rsa_entry] = :public_key.pem_decode(pem_bin) 
    key = :public_key.pem_entry_decode(rsa_entry) 
    end 

    def base64Sign(plaintext) do 
    key = load_secret_pem() 
    signature_bytes = :public_key.sign(plaintext, :sha256, key) 
    Base.url_encode64(signature_bytes) 
    |> String.replace("-", "%2B") 
    |> String.replace("_", "%2F") 
    |> URI.encode_www_form 
    end 

    def make_url(verb, path, content_md5 \\ "", content_type \\ "") do 
    client_id = GCloud.Credentials.client_email() |> URI.encode_www_form 
    expiration = :os.system_time(:seconds) + @default_expiration 
    base_url = GCloud.Storage.base_uri() <> path 
    signature_string = construct_string(verb, content_md5, content_type, expiration, "", path) 
    url_encoded_signature = base64Sign(signature_string) 
    IO.puts "#{base_url}?GoogleAccessId=#{client_id}&Expires=#{expiration}&Signature=#{url_encoded_signature}" 
    end 

簽名URL如何使用Elixir或Erlang正確簽名?

回答

1

你在construct_string中的字符串構造可能會做你不知道的事情。請記住,Python的語法不盡相同,並且對空間有其他看法。

defmodule Test do 
    def foo(a,b) do 
    "#{a}\n 
    #{b}" 
    end 
end 
IO.inspect Test.foo(1,2) 
# output: 
"1\n\n 2" 

如果您使用""",而不是定界符,領先的空間消失,但​​你換行仍然重複。不過,這種方法可能是一個壞主意,因爲如果你從Windows機器保存文件,你可能有\r\n作爲編輯器結束的行,而擺脫這些是無用的煩惱。

相反,我認爲你應該在這裏改變你的方法是這樣的:

def construct_string(http_verb, content_md5, content_type, expiration_timestamp, canonicalized_extension_headers, canonicalized_resource) do 
    headers = Enum.join([http_verb, content_md5, content_type, expiration_timestamp], "\n") 
    "#{headers}\n#{canonicalized_extension_headers}#{canonicalized_resource}" 
end 

我不知道是否有任何其他錯誤,但這種立即站出來給我。

+0

我已添加您的建議,現在我的請求已正確格式化,謝謝您的確認。可悲的是,我仍然從Google雲存儲獲得相同的「簽名不匹配」錯誤,我想知道erlang public_key:sign函數是否支持我正在嘗試執行的操作,或者我錯誤地使用了它。 –

1

我設法做到了這一點,我通過並排打開python和elixir REPL來執行此操作,在測試字符串中執行每個步驟並比較輸出的差異,其中散列或簽名後沒有差異測試字符串,但有64位編碼之後,所以我改變:

def base64Sign(plaintext) do 
    key = load_secret_pem() 
    signature_bytes = :public_key.sign(plaintext, :sha256, key) 
    Base.url_encode64(signature_bytes) 
    |> String.replace("-", "%2B") 
    |> String.replace("_", "%2F") 
    |> URI.encode_www_form 
end 

def base64Sign(plaintext) do 
    key = GCloud.Credentials.load_secret_pem() 
    signature_bytes = :public_key.sign(plaintext, :sha256, key) 
    Base.encode64(signature_bytes) 
    |> URI.encode_www_form 
end 

這種結合asonge's string construction advice解決的問題。

相關問題