簡體   English   中英

帶有KMS的AWS S3客戶端加密在最后轉移的部分上發出ProtocolViolationException

[英]AWS S3 Client-Side Crypto with KMS in .NET issues ProtocolViolationException on last part transferred

以下代碼嘗試使用多部分傳輸,客戶端信封加密和Amazon KMS服務將17MB測試文件復制到S3存儲桶以處理數據加密密鑰。 多部分塊大小為5MB。

在傳輸最后一個(部分)塊時,並且IsLastPart標志設置為true ,對UploadPart的調用將生成System.Net.ProtocolViolationException指示: UploadPart Bytes to be written to the stream exceed the Content-Length bytes size specified.

這表明Content-Length html標頭未更新以反映加密引擎添加到最后一個密碼塊以進行正確對齊所必需的“填充字節”。 結果,當添加了最后的字節時,它們超過了給定的Content-Length並產生了這個錯誤。

如果設置IsLastPart (即保留為false ),則操作成功,但在下載和解密操作時也會失敗。

注意:AWS .NET SDK不提供KmsAlgorithm類。 此類來自另一個Stack Overflow 帖子,因為AWS SDK的.NET版本不提供KMS和S3之間的連接器類 ,以支持Java SDK所支持的信封加密。

那么使用客戶端加密和KMS管理密鑰將多部分上傳發送到S3的正確方法是什么?

    static string bucketName = "*****************************";
    static string keyName = "test.encrypted.bin";
    static string uploadSourcePath = "c:\\temp\\test.bin";
    static long partSize = 5 * 1024 * 1024;
    static String uploadId = "";

    static void Main(string[] args)
    {
        if (checkRequiredFields())
        {
            String cmkId = "************************************";

            // Prepare our KMS client and kmsAlgorithm
            using (AmazonKeyManagementServiceClient kmsClient = new AmazonKeyManagementServiceClient())
            using (KMSAlgorithm kmsAlgo = new KMSAlgorithm(kmsClient, cmkId))
            {
                // Generate the encryption materials object with the algorithm object
                EncryptionMaterials encryptionMaterials = new EncryptionMaterials(kmsAlgo);

                // Now prepare an S3 crypto client
                using (AmazonS3EncryptionClient cryptoClient = new AmazonS3EncryptionClient(encryptionMaterials))
                {
                    // Initiate the multipart upload request specifying the bucket and key values
                    InitiateMultipartUploadResponse initResp = cryptoClient.InitiateMultipartUpload(
                        new InitiateMultipartUploadRequest()
                        {
                            BucketName = bucketName,
                            Key = keyName
                        });

                    uploadId = initResp.UploadId;

                    long fileLength = new FileInfo(uploadSourcePath).Length;
                    long contentLength = fileLength;
                    long bytesRemaining = fileLength;


                    List<PartETag> partETags = new List<PartETag>();
                    int partNumber = 0;

                    while (bytesRemaining > 0)
                    {
                        long transferSize = bytesRemaining > partSize ? partSize : bytesRemaining;
                        long partIndex = fileLength - bytesRemaining;

                        partNumber++;

                        UploadPartResponse resp =
                            cryptoClient.UploadPart(
                                new UploadPartRequest()
                                {
                                    BucketName = bucketName,
                                    Key = keyName,
                                    FilePath = uploadSourcePath,
                                    FilePosition = partIndex,
                                    PartSize = transferSize,
                                    PartNumber = partNumber,
                                    UploadId = uploadId,
                                    IsLastPart = transferSize < AwsS3FileSystemSample1.Program.partSize
                                });

                        partETags.Add( new PartETag( partNumber, resp.ETag ));

                        bytesRemaining -= transferSize;
                    }

                    // Now complete the transfer
                    CompleteMultipartUploadResponse compResp = cryptoClient.CompleteMultipartUpload(
                        new CompleteMultipartUploadRequest()
                        {
                            Key = keyName,
                            BucketName = bucketName,
                            UploadId = initResp.UploadId,
                            PartETags = partETags
                        });
                }
            }
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

對於任何錯誤和任何幫助的道歉將不勝感激。

許多測試和修改一些代碼,在AWS SDK這里git的樞紐洞穴探險的.NET源代碼中的魔法秘密之后UploadPartRequestPartSize成員設置為零設置IsLastPart成員為true時(0)。

究竟為什么這個工作是一個爭論的問題。 由於最后一部分的數據通常由加密引擎填充以滿足密碼塊邊界,因此實際的內容長度在加密完成之前是未知的。 也許將PartSize設置為零允許底層代碼將Content-Length設置為密碼文本的填充長度,而不是PartSize中的值。 IsLastPart設置為true時,為什么不自動完成這是一個謎。

無論如何,以下摘要可能會有所幫助。 使用客戶端加密和多部分上載時,在上載對象數據的最后部分時,將PartSize成員設置為零(0),將IsLastPart設置為true。

此代碼片段可能有所幫助。

      while (bytesRemaining > 0)
      {
          long transferSize = bytesRemaining > partSize ? partSize : bytesRemaining;
          long partIndex = fileLength - bytesRemaining;

          bytesRemaining -= transferSize;
          bool isLastPart = bytesRemaining == 0;

          partNumber++;

          UploadPartResponse resp =
              cryptoClient.UploadPart(
                  new UploadPartRequest()
                  {
                      BucketName   = bucketName,
                      Key          = keyName,
                      FilePath     = uploadSourcePath,
                      FilePosition = partIndex,
                      PartSize     = isLastPart ? 0 : transferSize,
                      PartNumber   = partNumber,
                      UploadId     = uploadId,
                      IsLastPart   = isLastPart 
                  });

          partETags.Add( new PartETag( partNumber, resp.ETag ));
      }

我希望這可以幫助其他人。

感謝您嘗試在鏈接帖子中使用我的實現。

雖然我沒有閱讀任何明確支持這一點,但我認為Amazon S3加密客戶端可能與Multipart上傳不兼容。 雖然它不是決定性的,但我還是找不到使用Multipart上傳的Amazon S3加密客戶端的Java示例(SDK具有KMS實現)。 我懷疑的原因是文件是使用鏈阻塞加密的,如果分段上傳的每個部分都是零散加密的,鏈斷塊將被破壞,每個部分的各個初始化向量將丟失以進行解密。

我想唯一可以知道的方法是首先用Java測試它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM