尝试生成预签名 url link 以便用户可以下载 Amazon S3 对象,但请求无效

Trying to generate a presigned url link so an user can download an Amazon S3 object, but getting invalid request

我目前正在使用 Ruby aws-sdk,版本 2 gem 和服务器端客户提供的加密密钥 (SSE-C)。我可以毫无问题地将对象从 rails 表单上传到 Amazon S3。

def s3
  Aws::S3::Object.new(
    bucket_name: ENV['S3_BUCKET'],
    key: 'hello',
  )
end

def upload_object
  customer_key = OpenSSL::Cipher::AES.new(256, :CBC).random_key
  customer_key_md5 = Digest::MD5.new.digest(customer_key)
  object_key = 'hello'
  options = {}
  options[:key] = object_key
  options[:sse_customer_algorithm] = 'AES256'
  options[:sse_customer_key] = customer_key
  options[:sse_customer_key_md5] = customer_key_md5
  options[:body] = 'hello world'
  options[:bucket] = ENV['S3_BUCKET']
  s3.put(options)
  test_params = {
    object_key: object_key,
    customer_key: Base64.encode64(customer_key),
    md5_key: Base64.encode64(customer_key_md5),
  }
  Test.create(test_params)
end

但我在检索对象和生成签名的 url link 供用户下载时遇到了一些问题。

def retrieve_object(customer_key, md5)
  options = {}
  options[:key] = 'hello
  options[:sse_customer_algorithm] = 'AES256'
  options[:sse_customer_key] = Base64.decode64(customer_key)
  options[:sse_customer_key_md5] = Base64.decode64(md5)
  options[:bucket] = ENV['S3_BUCKET']
  s3.get(options)
  url = s3.presigned_url(:get)
end

link 已生成,但当我单击它时,它会将我定向到一个亚马逊页面,上面写着。

<Error>
<Code>InvalidRequest</Code>
<Message>
The object was stored using a form of Server Side Encryption. The correct   parameters must be provided to retrieve the object.
</Message>
<RequestId>93684EEBA062B1C2</RequestId>
<HostId>
OCnn5EG7ydfoKzsmEDMbqK5kOhLFpNXxVRdekfhOfnBc6s+jtPYFsKi8IZsEPcd9ConbYUHgwC8=
</HostId>
</Error>

错误消息没有帮助,因为我不确定我需要添加哪些参数。我想我可能缺少一些权限参数。

获取方法 http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#get-instance_method

Presigned_Url 方法 http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method

当您生成 pre-signed GET object URL 时,您需要提供将传递给 Aws::S3::Object#get.[=20= 的所有相同参数]

s3.get(sse_customer_algorithm: 'AES256', sse_customer_key: customer_key).body.read

这意味着您需要将相同的 sse_customer_* 选项传递给 #presigned_url:

url = obj.presigned_url(:get,
  sse_customer_algorithm: 'AES256',
  sse_customer_key: customer_key)

这将确保 SDK 正确签署 Amazon S3 在您发出最终 GET 请求时期望的 headers。下一个问题是您现在负责将这些值与 GET 请求一起作为 headers 发送。 Amazon S3 将不接受查询字符串中的算法和密钥。

uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri, {
  "x-amz-server-side-encryption-customer-algorithm" => 'AES256',
  "x-amz-server-side-encryption-customer-key" => Base64.encode64(cpk),
  "x-amz-server-side-encryption-customer-key-MD5" => Base64.encode64(OpenSSL::Digest::MD5.digest(cpk))
})

请注意 - 在测试时,我发现 aws-sdk 当前 v2.0.33 版本的预签名 URL 实现中存在一个错误 gem。 This has been fixed now 一旦发布,应该成为 v2.0.34 的一部分。

请参阅以下 gitst 以获取修补错误和演示的完整示例:

  • 使用 cpk
  • 上传 object
  • 使用 SDK
  • 获取 object
  • 生成预签名 GET url
  • 仅使用 Net::HTTP 和预先签名的 URL
  • 下载 object

您可以在此处查看示例脚本:

https://gist.github.com/trevorrowe/49bfb9d59f83ad450a9e

只需替换脚本顶部的 bucket_nameobject_key 变量。