Ruby on rails 试图生成一个预先签名的url链接,以便用户可以下载AmazonS3对象,但收到的请求无效

Ruby on rails 试图生成一个预先签名的url链接,以便用户可以下载AmazonS3对象,但收到的请求无效,ruby-on-rails,amazon-web-services,amazon-s3,Ruby On Rails,Amazon Web Services,Amazon S3,我目前正在使用Ruby aws sdk,版本2 gem和服务器端客户提供的加密密钥(SSE-C)。我能够将对象从rails表单上传到AmazonS3,没有任何问题 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).rando

我目前正在使用Ruby aws sdk,版本2 gem和服务器端客户提供的加密密钥(SSE-C)。我能够将对象从rails表单上传到AmazonS3,没有任何问题

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链接供用户下载时遇到了一些问题

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
链接是生成的,但当我点击它时,它会将我指向一个亚马逊页面,上面写着

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

获取方法

预签名Url方法

生成预签名的GET对象URL时,需要提供传递给
Aws::S3::object\GET
的所有相同参数

s3.get(sse_customer_algorithm: 'AES256', sse_customer_key: customer_key).body.read
这意味着您需要将相同的sse_customer_*选项传递给
#预签名的url

url = obj.presigned_url(:get,
  sse_customer_algorithm: 'AES256',
  sse_customer_key: customer_key)
这将确保SDK在您发出最终GET请求时正确签署AmazonS3期望的头。下一个问题是,您现在负责将这些值与GET请求一起作为头发送。AmazonS3将不接受算法并输入查询字符串

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
gem的v2.0.33版本的预签名URL实现中发现了一个bug。并且应该是v2.0.34发布后的一部分

请参阅下面的gitst,以获取修补该bug并演示的完整示例:

  • 使用cpk上载对象
  • 使用SDK获取对象
  • 生成预签名的GET url
  • 仅使用Net::HTTP和预先指定的URL下载对象
您可以在此处查看示例脚本:


只需替换脚本顶部的
bucket\u name
object\u key
变量即可。

谢谢。我会试试你的剧本。我在尝试添加参数时遇到了同样的问题。我已经向回购协议提交了一份承诺,这应该可以解决这个错误:太棒了。谢谢你这么快更新gem。我真的很感激。S3是否有可能被更改为在签名url的查询字符串中使用密钥等?这将使从浏览器中使用这些签名URL变得更加容易。或者有没有一种推荐的技术可以将这些加密文件直接在浏览器中交付给最终用户—ajax w/CORS或…?预签名的url确实接受密钥,只是接受的密钥不多。对于cors,您需要使用#put_bucket_cors配置您的bucket以接受这些头。我同意,如果这些值可以作为querystring的一部分发送就更好了。