带有Google Storage的C#-带有客户加密密钥的签名URL

时间:2019-01-27 10:36:28

标签: c# google-cloud-storage

我将文件上传到Google存储桶。现在,我为此生成了一个签名网址(基于此solution

private static Uri SignUrl(Stream jsonCertificateStream, string bucketName, string objectName, TimeSpan expireAfter, string[] scopes, HttpMethod verb)
{
    string url;
    var urlSigner = UrlSigner.FromServiceAccountData(jsonCertificateStream);
    url = urlSigner.Sign(
                bucketName,
                objectName,
                expireAfter,
                verb);

    return new Uri(url);
}

我找回了一个可以在我的浏览器上正常运行的URI。到这里为止-一切正常。

现在,我正在使用客户加密密钥来加密我上传的文件。因此,我删除了文件,然后再次上传-现在已加密。

问题在于,现在SignUrl()方法不再起作用。

在使用浏览器测试SignUrl()方法的结果时-我得到了:

<Error>
    <Code>ResourceIsEncryptedWithCustomerEncryptionKey</Code>
    <Message>
        The resource is encrypted with a customer encryption key.
    </Message>
    <Details>
        The requested object is encrypted by a customer-supplied encryption key.
    </Details>
</Error>

我想这是因为我的文件是加密的其他密钥。

如何正确处理?

1 个答案:

答案 0 :(得分:1)

基于Customer-Supplied Encryption Keys documentation ,在使用客户提供的加密密钥时,您需要添加以下标头

+------------------------------+--------+------------------------------------------------------------------------------+
|         Header name          | Value  |                                 Description                                  |
+------------------------------+--------+------------------------------------------------------------------------------+
| x-goog-encryption-algorithm  | string | The encryption algorithm to use. You must use the value AES256.              |
| x-goog-encryption-key        | string | An RFC 4648 Base64-encoded string of your AES-256 encryption key.            |
| x-goog-encryption-key-sha256 | string | An RFC 4648 Base64-encoded string of the SHA256 hash of your encryption key. |
+------------------------------+--------+------------------------------------------------------------------------------+

在使用 urlSigner 时,还必须传递 x-goog-encryption-algorithm

此处为代码示例

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Google.Cloud.Storage.V1;
using System.IO;
using System.Net.Http;
using Google.Apis.Auth.OAuth2;

namespace stackoverflow54387198
{
    class Program
    {
        static void Main(string[] args)
        {

            // step 1 create customer encryption key
            var key = EncryptionKey.Generate().Base64Key;

            var encryptionKey = EncryptionKey.Create(Convert.FromBase64String(key));


            // step 2 get you service Acount cert for auth

            string serviceAcountCert = "stackoverflow54387198-xxxxxxxx.json";

            // step 3 get you service Acount cert for auth

            string bucketName = "stackoverflow_54387198_bucket";

            string localPath = "FileToUpload.txt";

            string objectName = null;


            // step 4 create a local text file to upload

            File.WriteAllText(localPath, "test");


            // step 5 Create Google Storage Client

            var storage = StorageClient.Create(
                GoogleCredential.FromJson(File.ReadAllText(serviceAcountCert)));


            // step 6 upload the file with the customer encryption key from step 1

            using (var f = File.OpenRead(localPath))

            {

                objectName = objectName ?? Path.GetFileName(localPath);

                storage.UploadObject(bucketName, objectName, null, f,

                    new UploadObjectOptions()

                    {

                        EncryptionKey = encryptionKey

                    });

                Console.WriteLine($"Uploaded {objectName}.");

            }


            // step 7 create a url


            // step 7.1 create add x-goog-encryption-algorithm hear 
            //to tell google you are using  customer encryption key

            var requestHeaders = new Dictionary<string, IEnumerable<string>>
            {
                {
                    "x-goog-encryption-algorithm", new [] { "AES256" }
                }
            };


            // step 7.2  set other parameters

            var expireAfter = TimeSpan.FromHours(30.0);

            var verb = HttpMethod.Get;


            // step 7.3  create a Url Signer

            var urlSigner = UrlSigner.FromServiceAccountPath(serviceAcountCert);


            // step 7.4  create a secure url
            var url = urlSigner.Sign(
                        bucketName,
                        objectName,
                        expireAfter,
                        verb,
                        requestHeaders);



            // step 8  use the Your Url


            // step 8.1 create HttpClient

            var client = new HttpClient();


            // step 8.1  add x-goog-encryption-algorithm header the same from step 7

            client.DefaultRequestHeaders.Add("x-goog-encryption-algorithm", "AES256");


            // step 8.2  add x-goog-encryption-key header with customer encryption key (Base64Hash)

            client.DefaultRequestHeaders.Add("x-goog-encryption-key", encryptionKey.Base64Key);


            // step 8.3  add x-goog-encryption-key header with customer encryption key (Base64Hash)

            client.DefaultRequestHeaders.Add("x-goog-encryption-key-sha256", encryptionKey.Base64Hash);

            // step 8.4  Download the file 
            Task.Run(async () =>
            {
                var response = await client.GetAsync(url);
                var contents = await response.Content.ReadAsStringAsync();
                // contents == "test"
                Console.WriteLine($"contents=>{contents}");

            }).GetAwaiter().GetResult();

            Console.ReadLine();

        }


    }
}

您还可以使用邮递员添加标题

ref。 1

额外

  

您好,穆罕默德(Mohamed),感谢您的全面答复。根据你的   答案,我了解我不能公开签署网址   将用于下载文件。我总是必须添加其他内容   标头(x-goog-encryption-algorithm,x-goog-encryption-key等)   为了得到文件?无法将此数据包含在   网址字段以某种方式出现?

要回答这个问题,我将使用下面的问题:

  • 跳到问题3寻求解决方案

问题1

  

无法以某种方式在URL字段中包含此数据?

答案1

  • 我不知道

为什么要回答1?

  • 基于我的逻辑

  • google Storage API是由Google程序员编写的软件

  • 他们希望文档中的加密密钥位于标题中

  • 如果您在没有输入标头的情况下调用API,则会返回错误消息

  • 所以也许有未记录的方法,但我不知道

问题2

HTTP参数和HTTP标头用法之间有什么区别

答案2

问题3:

如何解决这个问题?

答案3

问题4:

什么是Google存储数据加密选项

答案4

  

云存储始终encrypts your data on the server side,   在将其写入磁盘之前,无需任何额外费用。除此之外   标准行为,还有其他方式可以在以下情况下加密数据   使用云存储。以下是加密选项的摘要   可供您使用:

     
      
  • 服务器端加密:在Cloud Storage接收数据之后但在将数据写入磁盘并存储之前进行的加密。

         
        
    • Customer-supplied encryption keys:   您可以为服务器端创建和管理自己的加密密钥   加密,作为加密基础之上的附加加密层   标准的云存储加密。
    •   
    • Customer-managed encryption keys:   您可以使用Cloud Key生成和管理加密密钥   管理服务,充当顶部的附加加密层   标准云存储加密。
    •   
  •   
  • Client-side encryption:在将数据发送到Cloud Storage之前发生的加密。这样的数据   到达已经加密但还会经历的Cloud Storage   服务器端加密。

  •   

问题5:

最好使用客户提供的加密密钥

答案5:

  • 这取决于您的软件方案,但是

      

    Cloud Storage总是先加密服务器端的数据,然后再加密   已写入磁盘,无需额外付费

  • 参考:Data Encryption Options  |  Cloud Storage  |  Google Cloud

  • 也许您不需要使用客户提供的加密密钥来加密数据,因为google默认情况下也会加密静态数据一天之内,如果您害怕Google暗中监视您(只是我作为完美主义者的假设),这是不合逻辑的,因为当您请求文件

  • 时,您会将加密密钥发送到那里的服务器

ref。 2: