The EncryptionUtils class encrypts and decrypts data stored in S3. It can be used to prepare requests for encryption before they are stored in S3 and to decrypt objects that are retrieved from S3.
        /// <summary>
        /// Updates the request where the metadata contains encryption information
        /// and the input stream contains the encrypted object contents.
        /// </summary>
        /// <param name="putObjectRequest">
        /// The request whose contents are to be encrypted.
        /// </param>
        private void GenerateEncryptedObjectRequestUsingMetadata(PutObjectRequest putObjectRequest)
        {
            // Create instruction
            EncryptionInstructions instructions = EncryptionUtils.GenerateInstructions(this.encryptionMaterials);

            EncryptionUtils.AddUnencryptedContentLengthToMetadata(putObjectRequest);

            // Encrypt the object data with the instruction
            putObjectRequest.InputStream = EncryptionUtils.EncryptRequestUsingInstruction(putObjectRequest.InputStream, instructions);

            // Update the metadata
            EncryptionUtils.UpdateMetadataWithEncryptionInstructions(putObjectRequest, instructions);
        }
        /// <summary>
        /// Updates the request where the instruction file contains encryption information
        /// and the input stream contains the encrypted object contents.
        /// </summary>
        /// <param name="putObjectRequest"></param>
        private void GenerateEncryptedObjectRequestUsingInstructionFile(PutObjectRequest putObjectRequest)
        {
            // Create instruction
            EncryptionInstructions instructions = EncryptionUtils.GenerateInstructions(this.encryptionMaterials);

            EncryptionUtils.AddUnencryptedContentLengthToMetadata(putObjectRequest);

            // Encrypt the object data with the instruction
            putObjectRequest.InputStream = EncryptionUtils.EncryptRequestUsingInstruction(putObjectRequest.InputStream, instructions);

            // Create request for uploading instruction file
            PutObjectRequest instructionFileRequest = EncryptionUtils.CreateInstructionFileRequest(putObjectRequest, instructions);

            S3ClientForInstructionFile.PutObject(instructionFileRequest);
        }
        /// <summary>
        /// Performs encryption for PutObject and MultiPartUpload,
        /// also adding encryption information to object metadata or
        /// instruction file
        /// </summary>
        /// <param name="request">AmazonWebServiceRequest on which encryption is performed </param>
        protected override void ProcessPreRequestHandlers(AmazonWebServiceRequest request)
        {
            base.ProcessPreRequestHandlers(request);

            var putObjectRequest = request as PutObjectRequest;

            if (putObjectRequest != null)
            {
                if (this.amazonS3CryptoConfig.StorageMode == CryptoStorageMode.InstructionFile)
                {
                    GenerateEncryptedObjectRequestUsingInstructionFile(putObjectRequest);
                }
                else
                {
                    GenerateEncryptedObjectRequestUsingMetadata(putObjectRequest);
                }
            }

            var initiateMultiPartRequest = request as InitiateMultipartUploadRequest;

            if (initiateMultiPartRequest != null)
            {
                EncryptionInstructions instructions = EncryptionUtils.GenerateInstructions(this.encryptionMaterials);

                if (this.amazonS3CryptoConfig.StorageMode == CryptoStorageMode.ObjectMetadata)
                {
                    EncryptionUtils.UpdateMetadataWithEncryptionInstructions(initiateMultiPartRequest, instructions);
                }

                initiateMultiPartRequest.EnvelopeKey = instructions.EnvelopeKey;
                initiateMultiPartRequest.IV          = instructions.InitializationVector;
            }

            var uploadPartRequest = request as UploadPartRequest;

            if (uploadPartRequest != null)
            {
                GenerateEncryptedUploadPartRequest(uploadPartRequest);
            }
        }
        /// <summary>
        /// Performs decryption of data by getting encryption information
        /// from object metadata or instruction file.
        /// </summary>
        /// <param name="response">AmazonWebServiceResponse on which decryption is performed</param>
        /// <param name="request">IRequest</param>
        /// <param name="webResponseData">IWebResponseData</param>
        protected override void ProcessResponseHandlers(AmazonWebServiceResponse response, IRequest request, IWebResponseData webResponseData)
        {
            base.ProcessResponseHandlers(response, request, webResponseData);

            var initiateMultiPartUploadRequest = request.OriginalRequest as InitiateMultipartUploadRequest;
            var initiateMultiPartResponse      = response as InitiateMultipartUploadResponse;

            if (initiateMultiPartResponse != null)
            {
                byte[] envelopeKey = initiateMultiPartUploadRequest.EnvelopeKey;
                byte[] iv          = initiateMultiPartUploadRequest.IV;

                UploadPartEncryptionContext contextForEncryption = new UploadPartEncryptionContext();
                contextForEncryption.EnvelopeKey = envelopeKey;
                contextForEncryption.NextIV      = iv;
                contextForEncryption.FirstIV     = iv;
                contextForEncryption.PartNumber  = 0;

                //Add context for encryption of next part
                currentMultiPartUploadKeys.Add(initiateMultiPartResponse.UploadId, contextForEncryption);
            }

            var uploadPartRequest  = request.OriginalRequest as UploadPartRequest;
            var uploadPartResponse = response as UploadPartResponse;

            if (uploadPartResponse != null)
            {
                string uploadID = uploadPartRequest.UploadId;
                UploadPartEncryptionContext encryptedUploadedContext = null;

                if (!currentMultiPartUploadKeys.TryGetValue(uploadID, out encryptedUploadedContext))
                {
                    throw new AmazonS3Exception("encryption context for multi part upload not found");
                }

                if (uploadPartRequest.IsLastPart == false)
                {
                    object stream = null;

                    if (!uploadPartRequest.RequestState.TryGetValue(S3CryptoStream, out stream))
                    {
                        throw new AmazonS3Exception("cannot retrieve S3 crypto stream from request state, hence cannot get Initialization vector for next uploadPart ");
                    }

                    var encryptionStream = stream as AESEncryptionUploadPartStream;
                    encryptedUploadedContext.NextIV = encryptionStream.InitializationVector;
                }
            }

            var getObjectResponse = response as GetObjectResponse;

            if (getObjectResponse != null)
            {
                if (EncryptionUtils.IsEncryptionInfoInMetadata(getObjectResponse) == true)
                {
                    DecryptObjectUsingMetadata(getObjectResponse);
                }
                else
                {
                    GetObjectResponse instructionFileResponse = null;
                    try
                    {
                        GetObjectRequest instructionFileRequest = EncryptionUtils.GetInstructionFileRequest(getObjectResponse);
                        instructionFileResponse = S3ClientForInstructionFile.GetObject(instructionFileRequest);
                    }
                    catch (AmazonServiceException ace)
                    {
                        throw new AmazonServiceException(string.Format(CultureInfo.InvariantCulture, "Unable to decrypt data for object {0} in bucket {1}",
                                                                       getObjectResponse.Key, getObjectResponse.BucketName), ace);
                    }

                    if (EncryptionUtils.IsEncryptionInfoInInstructionFile(instructionFileResponse) == true)
                    {
                        DecryptObjectUsingInstructionFile(getObjectResponse, instructionFileResponse);
                    }
                }
            }

            var completeMultiPartUploadRequest  = request.OriginalRequest as CompleteMultipartUploadRequest;
            var completeMultipartUploadResponse = response as CompleteMultipartUploadResponse;

            if (completeMultipartUploadResponse != null)
            {
                if (amazonS3CryptoConfig.StorageMode == CryptoStorageMode.InstructionFile)
                {
                    UploadPartEncryptionContext context = currentMultiPartUploadKeys[completeMultiPartUploadRequest.UploadId];
                    byte[] envelopeKey                  = context.EnvelopeKey;
                    byte[] iv                           = context.FirstIV;
                    byte[] encryptedEnvelopeKey         = EncryptionUtils.EncryptEnvelopeKey(envelopeKey, this.encryptionMaterials);
                    EncryptionInstructions instructions = new EncryptionInstructions(EncryptionMaterials.EmptyMaterialsDescription, envelopeKey, encryptedEnvelopeKey, iv);

                    PutObjectRequest instructionFileRequest = EncryptionUtils.CreateInstructionFileRequest(completeMultiPartUploadRequest, instructions);

                    S3ClientForInstructionFile.PutObject(instructionFileRequest);
                }

                //Clear Context data since encryption is completed
                currentMultiPartUploadKeys.Clear();
            }
        }