繁体   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