コード例 #1
0
        /// <summary>
        /// Decrypts a provided XML element.
        /// </summary>
        /// <param name="encryptedElement">Encrypted XML element.</param>
        /// <param name="ct">Cancellation token.</param>
        /// <returns>Decrypted XML element.</returns>
#pragma warning disable S3242 // Not altering Microsoft interface definition
        public async Task <XElement> DecryptAsync(XElement encryptedElement, CancellationToken ct)
#pragma warning restore S3242
        {
            ValidateConfig();

            logger?.LogDebug("Decrypting ciphertext DataProtection key using AWS key {0}", Config.KeyId);

            using (var memoryStream = new MemoryStream())
            {
                byte[] protectedKey = Convert.FromBase64String((string)encryptedElement.Element("value"));
                await memoryStream.WriteAsync(protectedKey, 0, protectedKey.Length, ct);

                var response = await kmsClient.DecryptAsync(new DecryptRequest
                {
                    EncryptionContext = ContextUpdater.GetEncryptionContext(Config, dpOptions.Value),
                    GrantTokens       = Config.GrantTokens,
                    CiphertextBlob    = memoryStream
                },
                                                            ct)
                               .ConfigureAwait(false);

                // Help indicates that Plaintext might be empty if the key couldn't be retrieved but
                // testing shows that you always get an exception thrown first
                using (var plaintext = response.Plaintext)
                {
                    // Ignoring all the good reasons mentioned in KmsXmlEncryptor and that the implementation would
                    // be error-prone, hard to test & review, as well as vary between NET Full & NET Core, it's not
                    // actually permitted to access the buffer of response.Plaintext because it was populated in
                    // the SDK from a constructor which disallows any subsequent writing.
                    //
                    // Yet more reasons that this needs to be handled at a framework level, providing clear Secure* primitives.
                    return(XElement.Load(plaintext));
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Encrypts the provided XML element.
        /// </summary>
        /// <param name="plaintextElement">XML element to encrypt.</param>
        /// <param name="ct">Cancellation token.</param>
        /// <returns>Encrypted XML data.</returns>
#pragma warning disable S3242 // Not altering Microsoft interface definition
        public async Task <EncryptedXmlInfo> EncryptAsync(XElement plaintextElement, CancellationToken ct)
#pragma warning restore S3242
        {
            ValidateConfig();

            logger?.LogDebug("Encrypting plaintext DataProtection key using AWS key {0}", Config.KeyId);

            // Some implementations of this e.g. DpapiXmlEncryptor go to some lengths to create a memory
            // stream, use unsafe code to pin & zero it, and so on.
            //
            // Currently not doing any such zeroing here, as this neglects that the XElement above is in memory,
            // is a managed construct containing ultimately a System.String, and therefore the plaintext is
            // already at risk of compromise, copying during GC, paging to disk etc. If we'd been starting with SecureString,
            // there'd be a good pre-existing case for handling the subsequent memory copies carefully (and it'd
            // essentially be forced as you can't copy or stream a SecureString without unsafe code).
            //
            // Even ignoring that, the subsequent code sending a MemoryStream out over the web to AWS calls ToArray inside
            // the SDK and then stores the result as a System.String, twice, as part of outgoing JSON, and that's
            // before considering HTTP-layer buffering...
            //
            // Since the AWS code eventually just gets UTF8 byte[] for request content, the ideal would be that
            // instead of a memory stream and a standard JSON handler, the AWS code prepares all the usual JSON and then
            // gets a specific UTF8 byte[] entry of the base64 plaintext which a caller can pin and erase (and the SDK could
            // do the same with its own request content byte[]).
            //
            // It doesn't.
            //
            // Even then the HttpClient usage would buffer the plaintext enroute.
            //
            // In conclusion pinning & zeroing this particular stream seems to be complex & error-prone overkill for
            // handling data that is already exposed in memory - not that I wouldn't be thrilled to see
            // a properly reviewed SecureMemoryStream and SecureHttpStreamContent in the framework...
            //
            // To at least reduce stream allocation churn & thus copying, pre-allocate a reasonable capacity.
            using (var memoryStream = new MemoryStream(4096))
            {
                plaintextElement.Save(memoryStream);
                memoryStream.Seek(0, SeekOrigin.Begin);

                var response = await kmsClient.EncryptAsync(new EncryptRequest
                {
                    EncryptionContext = ContextUpdater.GetEncryptionContext(Config, dpOptions.Value),
                    GrantTokens       = Config.GrantTokens,
                    KeyId             = Config.KeyId,
                    Plaintext         = memoryStream
                },
                                                            ct)
                               .ConfigureAwait(false);

                using (var cipherText = response.CiphertextBlob)
                {
                    var element = new XElement("encryptedKey",
                                               new XComment(" This key is encrypted with AWS Key Management Service. "),
                                               new XElement("value", Convert.ToBase64String(cipherText.ToArray())));

                    return(new EncryptedXmlInfo(element, typeof(KmsXmlDecryptor)));
                }
            }
        }