internal async Task <(byte[], EncryptionKeyWrapMetadata, InMemoryRawDek)> WrapAsync(
            string id,
            byte[] key,
            string encryptionAlgorithm,
            EncryptionKeyWrapMetadata metadata,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            EncryptionKeyWrapResult keyWrapResponse;

            using (diagnosticsContext.CreateScope("WrapDataEncryptionKey"))
            {
                keyWrapResponse = await this.DekProvider.EncryptionKeyWrapProvider.WrapKeyAsync(key, metadata, cancellationToken);
            }

            // Verify
            DataEncryptionKeyProperties tempDekProperties = new DataEncryptionKeyProperties(id, encryptionAlgorithm, keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata, DateTime.UtcNow);
            InMemoryRawDek roundTripResponse = await this.UnwrapAsync(tempDekProperties, diagnosticsContext, cancellationToken);

            if (!roundTripResponse.DataEncryptionKey.RawKey.SequenceEqual(key))
            {
                throw new InvalidOperationException("The key wrapping provider configured was unable to unwrap the wrapped key correctly.");
            }

            return(keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata, roundTripResponse);
        }
        /// <inheritdoc />
        public override async Task <EncryptionKeyWrapResult> WrapKeyAsync(
            byte[] key,
            EncryptionKeyWrapMetadata metadata,
            CancellationToken cancellationToken)
        {
            if (metadata.Type != AzureKeyVaultKeyWrapMetadata.TypeConstant)
            {
                throw new ArgumentException("Invalid metadata", nameof(metadata));
            }

            if (!KeyVaultKeyUriProperties.TryParse(new Uri(metadata.Value), out KeyVaultKeyUriProperties keyVaultUriProperties))
            {
                throw new ArgumentException("KeyVault Key Uri {0} is invalid.", metadata.Value);
            }

            if (!await this.keyVaultAccessClient.ValidatePurgeProtectionAndSoftDeleteSettingsAsync(keyVaultUriProperties, cancellationToken))
            {
                throw new ArgumentException(string.Format("Key Vault {0} provided must have soft delete and purge protection enabled.", keyVaultUriProperties.KeyUri));
            }

            byte[] result = await this.keyVaultAccessClient.WrapKeyAsync(key, keyVaultUriProperties, cancellationToken);

            EncryptionKeyWrapMetadata responseMetadata = new EncryptionKeyWrapMetadata(metadata.Type, metadata.Value, KeyVaultConstants.RsaOaep256);

            return(new EncryptionKeyWrapResult(result, responseMetadata));
        }
        /// <inheritdoc />
        public override async Task <EncryptionKeyUnwrapResult> UnwrapKeyAsync(
            byte[] wrappedKey,
            EncryptionKeyWrapMetadata metadata,
            CancellationToken cancellationToken)
        {
            if (metadata.Type != AzureKeyVaultKeyWrapMetadata.TypeConstant)
            {
                throw new ArgumentException("Invalid metadata", nameof(metadata));
            }

            if (metadata.Algorithm != KeyVaultConstants.RsaOaep256)
            {
                throw new ArgumentException(
                          string.Format("Unknown encryption key wrap algorithm {0}", metadata.Algorithm),
                          nameof(metadata));
            }

            if (!KeyVaultKeyUriProperties.TryParse(new Uri(metadata.Value), out KeyVaultKeyUriProperties keyVaultUriProperties))
            {
                throw new ArgumentException("KeyVault Key Uri {0} is invalid.", metadata.Value);
            }

            byte[] result = await this.keyVaultAccessClient.UnwrapKeyAsync(wrappedKey, keyVaultUriProperties, cancellationToken);

            return(new EncryptionKeyUnwrapResult(result, this.rawDekCacheTimeToLive));
        }
 public override Task <ItemResponse <DataEncryptionKeyProperties> > CreateDataEncryptionKeyAsync(
     string id,
     string encryptionAlgorithm,
     EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
     ItemRequestOptions requestOptions   = null,
     CancellationToken cancellationToken = default)
 {
     return(TaskHelper.RunInlineIfNeededAsync(() =>
                                              this.dataEncryptionKeyContainerCore.CreateDataEncryptionKeyAsync(id, encryptionAlgorithm, encryptionKeyWrapMetadata, requestOptions, cancellationToken)));
 }
        /// <inheritdoc/>
        public override async Task <ItemResponse <DataEncryptionKeyProperties> > RewrapDataEncryptionKeyAsync(
            string id,
            EncryptionKeyWrapMetadata newWrapMetadata,
            ItemRequestOptions requestOptions   = null,
            CancellationToken cancellationToken = default)
        {
            if (newWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newWrapMetadata));
            }

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            (DataEncryptionKeyProperties dekProperties, InMemoryRawDek inMemoryRawDek) = await this.FetchUnwrappedAsync(
                id,
                diagnosticsContext,
                cancellationToken);

            (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek updatedRawDek) = await this.WrapAsync(
                id,
                inMemoryRawDek.DataEncryptionKey.RawKey,
                dekProperties.EncryptionAlgorithm,
                newWrapMetadata,
                diagnosticsContext,
                cancellationToken);

            if (requestOptions == null)
            {
                requestOptions = new ItemRequestOptions();
            }

            requestOptions.IfMatchEtag = dekProperties.ETag;

            DataEncryptionKeyProperties newDekProperties = new DataEncryptionKeyProperties(dekProperties)
            {
                WrappedDataEncryptionKey  = wrappedDek,
                EncryptionKeyWrapMetadata = updatedMetadata,
            };

            ItemResponse <DataEncryptionKeyProperties> response = await this.DekProvider.Container.ReplaceItemAsync(
                newDekProperties,
                newDekProperties.Id,
                new PartitionKey(newDekProperties.Id),
                requestOptions,
                cancellationToken);

            Debug.Assert(response.Resource != null);

            this.DekProvider.DekCache.SetDekProperties(id, response.Resource);
            this.DekProvider.DekCache.SetRawDek(id, updatedRawDek);
            return(response);
        }
Exemple #6
0
 /// <summary>
 /// Initializes a new instance of <see cref="DataEncryptionKeyProperties"/>.
 /// </summary>
 /// <param name="id">Unique identifier for the data encryption key.</param>
 /// <param name="encryptionAlgorithm">Encryption algorithm that will be used along with this data encryption key to encrypt/decrypt data.</param>
 /// <param name="wrappedDataEncryptionKey">Wrapped (encrypted) form of the data encryption key.</param>
 /// <param name="encryptionKeyWrapMetadata">Metadata used by the configured key wrapping provider in order to unwrap the key.</param>
 /// <param name="createdTime">Time at which this data encryption key was created.</param>
 public DataEncryptionKeyProperties(
     string id,
     string encryptionAlgorithm,
     byte[] wrappedDataEncryptionKey,
     EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
     DateTime createdTime)
 {
     this.Id = !string.IsNullOrEmpty(id) ? id : throw new ArgumentNullException(nameof(id));
     this.EncryptionAlgorithm       = encryptionAlgorithm;
     this.WrappedDataEncryptionKey  = wrappedDataEncryptionKey ?? throw new ArgumentNullException(nameof(wrappedDataEncryptionKey));
     this.EncryptionKeyWrapMetadata = encryptionKeyWrapMetadata ?? throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
     this.CreatedTime = createdTime;
 }
        /// <inheritdoc/>
        public override Task <ItemResponse <DataEncryptionKeyProperties> > RewrapDataEncryptionKeyAsync(
            string id,
            EncryptionKeyWrapMetadata newWrapMetadata,
            ItemRequestOptions requestOptions   = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (newWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newWrapMetadata));
            }

            return(TaskHelper.RunInlineIfNeededAsync(() =>
                                                     this.dataEncryptionKeyContainerCore.RewrapDataEncryptionKeyAsync(id, newWrapMetadata, requestOptions, cancellationToken)));
        }
        public override async Task <ItemResponse <DataEncryptionKeyProperties> > CreateDataEncryptionKeyAsync(
            string id,
            string encryptionAlgorithm,
            EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
            ItemRequestOptions requestOptions   = null,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (encryptionAlgorithm != CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)
            {
                throw new ArgumentException(string.Format("Unsupported Encryption Algorithm {0}", encryptionAlgorithm), nameof(encryptionAlgorithm));
            }

            if (encryptionKeyWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
            }

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            byte[] rawDek = DataEncryptionKey.Generate(encryptionAlgorithm);

            (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek inMemoryRawDek) = await this.WrapAsync(
                id,
                rawDek,
                encryptionAlgorithm,
                encryptionKeyWrapMetadata,
                diagnosticsContext,
                cancellationToken);

            DataEncryptionKeyProperties dekProperties = new DataEncryptionKeyProperties(id, encryptionAlgorithm, wrappedDek, updatedMetadata, DateTime.UtcNow);

            ItemResponse <DataEncryptionKeyProperties> dekResponse = await this.DekProvider.Container.CreateItemAsync(dekProperties, new PartitionKey(dekProperties.Id), cancellationToken : cancellationToken);

            this.DekProvider.DekCache.SetDekProperties(id, dekResponse.Resource);
            this.DekProvider.DekCache.SetRawDek(id, inMemoryRawDek);
            return(dekResponse);
        }
Exemple #9
0
        /// <summary>
        /// Create a Client Encryption Key used to Encrypt data.
        /// </summary>
        /// <param name="database">Regular cosmos database.</param>
        /// <param name="clientEncryptionKeyId"> Client Encryption Key id.</param>
        /// <param name="dataEncryptionKeyAlgorithm"> Encryption Algorthm. </param>
        /// <param name="encryptionKeyWrapMetadata"> EncryptionKeyWrapMetadata.</param>
        /// <param name="cancellationToken"> cancellation token </param>
        /// <returns>Container to perform operations supporting client-side encryption / decryption.</returns>
        /// <example>
        /// This example shows how to create a new Client Encryption Key.
        ///
        /// <code language="c#">
        /// <![CDATA[
        /// ClientEncryptionKeyResponse response = await this.cosmosDatabase.CreateClientEncryptionKeyAsync(
        ///     "testKey",
        ///     DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256,
        ///     new EncryptionKeyWrapMetadata("metadataName", "MetadataValue"));
        /// ]]>
        /// </code>
        /// </example>
        public static async Task <ClientEncryptionKeyResponse> CreateClientEncryptionKeyAsync(
            this Database database,
            string clientEncryptionKeyId,
            DataEncryptionKeyAlgorithm dataEncryptionKeyAlgorithm,
            EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            string encryptionAlgorithm = dataEncryptionKeyAlgorithm.ToString();

            if (!string.Equals(encryptionAlgorithm, DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.ToString()))
            {
                throw new ArgumentException($"Invalid Encryption Algorithm '{encryptionAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
            }

            if (encryptionKeyWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
            }

            EncryptionCosmosClient encryptionCosmosClient;

            if (database is EncryptionDatabase encryptionDatabase)
            {
                encryptionCosmosClient = encryptionDatabase.EncryptionCosmosClient;
            }
            else
            {
                throw new ArgumentException("Creating a ClientEncryptionKey resource requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
            }

            EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider;

            KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                encryptionKeyWrapMetadata.Name,
                encryptionKeyWrapMetadata.Value,
                encryptionKeyStoreProvider);

            ProtectedDataEncryptionKey protectedDataEncryptionKey = new ProtectedDataEncryptionKey(
                clientEncryptionKeyId,
                keyEncryptionKey);

            byte[] wrappedDataEncryptionKey = protectedDataEncryptionKey.EncryptedValue;

            ClientEncryptionKeyProperties clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
                clientEncryptionKeyId,
                encryptionAlgorithm,
                wrappedDataEncryptionKey,
                encryptionKeyWrapMetadata);

            ClientEncryptionKeyResponse clientEncryptionKeyResponse = await database.CreateClientEncryptionKeyAsync(
                clientEncryptionKeyProperties,
                cancellationToken : cancellationToken);

            return(clientEncryptionKeyResponse);
        }
Exemple #10
0
        /// <summary>
        /// Rewrap an existing Client Encryption Key.
        /// </summary>
        /// <param name="database">Regular cosmos database.</param>
        /// <param name="clientEncryptionKeyId"> Client Encryption Key id.</param>
        /// <param name="newEncryptionKeyWrapMetadata"> EncryptionKeyWrapMetadata.</param>
        /// <param name="cancellationToken"> cancellation token </param>
        /// <returns>Container to perform operations supporting client-side encryption / decryption.</returns>
        /// <example>
        /// This example shows how to rewrap a Client Encryption Key.
        ///
        /// <code language="c#">
        /// <![CDATA[
        /// ClientEncryptionKeyResponse response = await this.cosmosDatabase.RewrapClientEncryptionKeyAsync(
        ///     "keyToRewrap",
        ///     new EncryptionKeyWrapMetadata("metadataName", "UpdatedMetadataValue")));
        /// ]]>
        /// </code>
        /// </example>
        public static async Task <ClientEncryptionKeyResponse> RewrapClientEncryptionKeyAsync(
            this Database database,
            string clientEncryptionKeyId,
            EncryptionKeyWrapMetadata newEncryptionKeyWrapMetadata,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            if (newEncryptionKeyWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newEncryptionKeyWrapMetadata));
            }

            ClientEncryptionKey clientEncryptionKey = database.GetClientEncryptionKey(clientEncryptionKeyId);

            EncryptionCosmosClient encryptionCosmosClient;

            if (database is EncryptionDatabase encryptionDatabase)
            {
                encryptionCosmosClient = encryptionDatabase.EncryptionCosmosClient;
            }
            else
            {
                throw new ArgumentException("Rewraping a ClientEncryptionKey requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
            }

            EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider;

            ClientEncryptionKeyProperties clientEncryptionKeyProperties = await clientEncryptionKey.ReadAsync(cancellationToken : cancellationToken);

            RequestOptions requestOptions = new RequestOptions
            {
                IfMatchEtag = clientEncryptionKeyProperties.ETag,
            };

            KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
                encryptionKeyStoreProvider);

            byte[] unwrappedKey = keyEncryptionKey.DecryptEncryptionKey(clientEncryptionKeyProperties.WrappedDataEncryptionKey);

            keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                newEncryptionKeyWrapMetadata.Name,
                newEncryptionKeyWrapMetadata.Value,
                encryptionKeyStoreProvider);

            byte[] rewrappedKey = keyEncryptionKey.EncryptEncryptionKey(unwrappedKey);

            clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
                clientEncryptionKeyId,
                clientEncryptionKeyProperties.EncryptionAlgorithm,
                rewrappedKey,
                newEncryptionKeyWrapMetadata);

            ClientEncryptionKeyResponse clientEncryptionKeyResponse = await clientEncryptionKey.ReplaceAsync(
                clientEncryptionKeyProperties,
                requestOptions,
                cancellationToken : cancellationToken);

            return(clientEncryptionKeyResponse);
        }
Exemple #11
0
 /// <summary>
 /// Generates a data encryption key, wraps it using the key wrap metadata provided
 /// with the key wrapping provider in the <see cref="CosmosDataEncryptionKeyProvider"/> for encryption,
 /// and saves the wrapped data encryption key as an asynchronous operation in the Azure Cosmos service.
 /// </summary>
 /// <param name="id">Unique identifier for the data encryption key.</param>
 /// <param name="encryptionAlgorithm">Encryption algorithm that will be used along with this data encryption key to encrypt/decrypt data.</param>
 /// <param name="encryptionKeyWrapMetadata">Metadata used by the configured key wrapping provider in order to wrap the key.</param>
 /// <param name="requestOptions">(Optional) The options for the request.</param>
 /// <param name="cancellationToken">(Optional) Token representing request cancellation.</param>
 /// <returns>An awaitable response which wraps a <see cref="DataEncryptionKeyProperties"/> containing the read resource record.</returns>
 /// <exception cref="ArgumentNullException">If <paramref name="id"/> is not set.</exception>
 /// <exception cref="CosmosException">
 /// This exception can encapsulate many different types of errors.
 /// To determine the specific error always look at the StatusCode property.
 /// Some common codes you may get when creating a data encryption key are:
 /// <list type="table">
 ///     <listheader>
 ///         <term>StatusCode</term><description>Reason for exception</description>
 ///     </listheader>
 ///     <item>
 ///         <term>400</term><description>BadRequest - This means something was wrong with the request supplied. It is likely that an id was not supplied for the new encryption key.</description>
 ///     </item>
 ///     <item>
 ///         <term>409</term><description>Conflict - This means an <see cref="DataEncryptionKeyProperties"/> with an id matching the id you supplied already existed.</description>
 ///     </item>
 /// </list>
 /// </exception>
 /// <example>
 ///
 /// <code language="c#">
 /// <![CDATA[
 /// AzureKeyVaultKeyWrapMetadata wrapMetadata = new AzureKeyVaultKeyWrapMetadata("/path/to/my/akv/secret/v1");
 /// await this.cosmosDatabase.CreateDataEncryptionKeyAsync("myKey", wrapMetadata);
 /// ]]>
 /// </code>
 /// </example>
 public abstract Task <ItemResponse <DataEncryptionKeyProperties> > CreateDataEncryptionKeyAsync(
     string id,
     string encryptionAlgorithm,
     EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
     ItemRequestOptions requestOptions   = null,
     CancellationToken cancellationToken = default(CancellationToken));
Exemple #12
0
 /// <summary>
 /// Wraps the raw data encryption key (after unwrapping using the old metadata if needed) using the provided
 /// metadata with the help of the key wrapping provider in the EncryptionSerializer configured on the client via
 /// <see cref="CosmosClientBuilder.WithCustomSerializer"/>, and saves the re-wrapped data encryption key as an asynchronous
 /// operation in the Azure Cosmos service.
 /// </summary>
 /// <param name="id">Unique identifier of the data encryption key.</param>
 /// <param name="newWrapMetadata">The metadata using which the data encryption key needs to now be wrapped.</param>
 /// <param name="requestOptions">(Optional) The options for the request.</param>
 /// <param name="cancellationToken">(Optional) Token representing request cancellation.</param>
 /// <returns>An awaitable response which wraps a <see cref="DataEncryptionKeyProperties"/> containing details of the data encryption key that was re-wrapped.</returns>
 /// <exception cref="CosmosException">
 /// This exception can encapsulate many different types of errors.
 /// To determine the specific error always look at the StatusCode property.
 /// Some common codes you may get when re-wrapping a data encryption key are:
 /// <list type="table">
 ///     <listheader>
 ///         <term>StatusCode</term>
 ///         <description>Reason for exception</description>
 ///     </listheader>
 ///     <item>
 ///         <term>404</term>
 ///         <description>
 ///         NotFound - This means the resource or parent resource you tried to replace did not exist.
 ///         </description>
 ///     </item>
 ///     <item>
 ///         <term>429</term>
 ///         <description>
 ///         TooManyRequests - This means you have exceeded the number of request units per second.
 ///         Consult the CosmosException.RetryAfter value to see how long you should wait before retrying this operation.
 ///         </description>
 ///     </item>
 /// </list>
 /// </exception>
 /// <example>
 /// <code language="c#">
 /// <![CDATA[
 /// AzureKeyVaultKeyWrapMetadata v2Metadata = new AzureKeyVaultKeyWrapMetadata("/path/to/my/master/key/v2");
 /// await key.RewrapAsync(v2Metadata);
 /// ]]>
 /// </code>
 /// </example>
 public abstract Task <ItemResponse <DataEncryptionKeyProperties> > RewrapDataEncryptionKeyAsync(
     string id,
     EncryptionKeyWrapMetadata newWrapMetadata,
     ItemRequestOptions requestOptions   = null,
     CancellationToken cancellationToken = default(CancellationToken));
Exemple #13
0
        /// <summary>
        /// Rewraps a client encryption key.
        /// This wraps the existing data encryption key using information provided in the newEncryptionKeyWrapMetadata
        /// using the IKeyEncryptionKeyResolver instance configured on the client,
        /// and saves the wrapped data encryption key along with the newEncryptionKeyWrapMetadata at the Cosmos DB service.
        /// </summary>
        /// <param name="database">Database supporting encryption in which the client encryption key properties will be updated.</param>
        /// <param name="clientEncryptionKeyId">Identifier for the client encryption key.</param>
        /// <param name="newEncryptionKeyWrapMetadata">Metadata used to wrap the data encryption key with a key encryption key.</param>
        /// <param name="cancellationToken">Token for request cancellation.</param>
        /// <returns>Response from the Cosmos DB service with updated <see cref="ClientEncryptionKeyProperties"/>.</returns>
        /// <example>
        /// This example shows how to rewrap a client encryption key.
        ///
        /// <code language="c#">
        /// <![CDATA[
        /// Azure.Core.TokenCredential tokenCredential = new Azure.Identity.DefaultAzureCredential();
        /// Azure.Core.Cryptography.IKeyEncryptionKeyResolver keyResolver = new Azure.Security.KeyVault.Keys.Cryptography.KeyResolver(tokenCredential);
        /// CosmosClient client = (new CosmosClient(endpoint, authKey)).WithEncryption(keyResolver, KeyEncryptionKeyResolverName.AzureKeyVault);
        ///
        /// EncryptionKeyWrapMetadata wrapMetadataForNewKeyVersion = new EncryptionKeyWrapMetadata(
        ///    type: KeyEncryptionKeyResolverName.AzureKeyVault,
        ///    name: "myKek",
        ///    value: "https://contoso.vault.azure.net/keys/myKek/0698c2156c1a4e1da5b6bab6f6422fd6",
        ///    algorithm: Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm.RsaOaep.ToString());
        ///
        ///  ClientEncryptionKeyResponse response = await client.GetDatabase("databaseId").RewrapClientEncryptionKeyAsync(
        ///    "myCek",
        ///    wrapMetadataForNewKeyVersion);
        /// ]]>
        /// </code>
        /// </example>
        /// <remarks>
        /// See <see href="https://aka.ms/CosmosClientEncryption">client-side encryption documentation</see> for more details.
        /// </remarks>
        public static async Task <ClientEncryptionKeyResponse> RewrapClientEncryptionKeyAsync(
            this Database database,
            string clientEncryptionKeyId,
            EncryptionKeyWrapMetadata newEncryptionKeyWrapMetadata,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            if (newEncryptionKeyWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newEncryptionKeyWrapMetadata));
            }

            if (newEncryptionKeyWrapMetadata.Algorithm != EncryptionKeyStoreProviderImpl.RsaOaepWrapAlgorithm)
            {
                throw new ArgumentException($"Invalid key wrap algorithm '{newEncryptionKeyWrapMetadata.Algorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details.");
            }

            ClientEncryptionKey clientEncryptionKey = database.GetClientEncryptionKey(clientEncryptionKeyId);

            EncryptionCosmosClient encryptionCosmosClient = database is EncryptionDatabase encryptionDatabase
                ? encryptionDatabase.EncryptionCosmosClient
                : throw new ArgumentException("Rewrapping a client encryption key requires the use of an encryption-enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details.");

            if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionCosmosClient.KeyEncryptionKeyResolverName))
            {
                throw new ArgumentException($"The Type of the EncryptionKeyWrapMetadata '{newEncryptionKeyWrapMetadata.Type}' does not match"
                                            + $" with the keyEncryptionKeyResolverName '{encryptionCosmosClient.KeyEncryptionKeyResolverName}' configured."
                                            + " Please refer to https://aka.ms/CosmosClientEncryption for more details.");
            }

            ClientEncryptionKeyProperties clientEncryptionKeyProperties = await clientEncryptionKey.ReadAsync(cancellationToken : cancellationToken);

            RequestOptions requestOptions = new RequestOptions
            {
                IfMatchEtag = clientEncryptionKeyProperties.ETag,
            };

            KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
                encryptionCosmosClient.EncryptionKeyStoreProviderImpl);

            byte[] unwrappedKey = keyEncryptionKey.DecryptEncryptionKey(clientEncryptionKeyProperties.WrappedDataEncryptionKey);

            keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                newEncryptionKeyWrapMetadata.Name,
                newEncryptionKeyWrapMetadata.Value,
                encryptionCosmosClient.EncryptionKeyStoreProviderImpl);

            byte[] rewrappedKey = keyEncryptionKey.EncryptEncryptionKey(unwrappedKey);

            clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
                clientEncryptionKeyId,
                clientEncryptionKeyProperties.EncryptionAlgorithm,
                rewrappedKey,
                newEncryptionKeyWrapMetadata);

            ClientEncryptionKeyResponse clientEncryptionKeyResponse = await clientEncryptionKey.ReplaceAsync(
                clientEncryptionKeyProperties,
                requestOptions,
                cancellationToken : cancellationToken);

            return(clientEncryptionKeyResponse);
        }
 /// <summary>
 /// Unwraps (i.e. decrypts) the provided wrapped data encryption key.
 /// </summary>
 /// <param name="wrappedKey">Wrapped form of data encryption key that needs to be unwrapped.</param>
 /// <param name="metadata">Metadata for the wrap provider that should be used to wrap / unwrap the key.</param>
 /// <param name="cancellationToken">Cancellation token allowing for cancellation of this operation.</param>
 /// <returns>Awaitable unwrapped (i.e. unencrypted) version of data encryption key passed in and how long the raw data encryption key can be cached on the client.</returns>
 public abstract Task <EncryptionKeyUnwrapResult> UnwrapKeyAsync(byte[] wrappedKey, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken);
 /// <summary>
 /// Initializes a new instance of the result of wrapping a data encryption key.
 /// </summary>
 /// <param name="wrappedDataEncryptionKey">
 /// Wrapped form of data encryption key.
 /// The byte array passed in must not be modified after this call by the <see cref="EncryptionKeyWrapProvider"/>.
 /// </param>
 /// <param name="encryptionKeyWrapMetadata">Metadata that can be used by the wrap provider to unwrap the data encryption key.</param>
 public EncryptionKeyWrapResult(byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata)
 {
     this.WrappedDataEncryptionKey  = wrappedDataEncryptionKey ?? throw new ArgumentNullException(nameof(wrappedDataEncryptionKey));
     this.EncryptionKeyWrapMetadata = encryptionKeyWrapMetadata ?? throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
 }
Exemple #16
0
        /// <summary>
        /// Creates a client encryption key.
        /// This generates a cryptographically random data encryption key, wraps it using
        /// information provided in the encryptionKeyWrapMetadata using the IKeyEncryptionKeyResolver instance configured on the client,
        /// and saves the wrapped data encryption key along with the encryptionKeyWrapMetadata at the Cosmos DB service.
        /// </summary>
        /// <param name="database">Database supporting encryption in which the client encryption key properties will be saved.</param>
        /// <param name="clientEncryptionKeyId">Identifier for the client encryption key.</param>
        /// <param name="encryptionAlgorithm">Algorithm which will be used for encryption with this key.</param>
        /// <param name="encryptionKeyWrapMetadata">Metadata used to wrap the data encryption key with a key encryption key.</param>
        /// <param name="cancellationToken">Token for request cancellation.</param>
        /// <returns>Response from the Cosmos DB service with <see cref="ClientEncryptionKeyProperties"/>.</returns>
        /// <example>
        /// This example shows how to create a client encryption key.
        ///
        /// <code language="c#">
        /// <![CDATA[
        /// Azure.Core.TokenCredential tokenCredential = new Azure.Identity.DefaultAzureCredential();
        /// Azure.Core.Cryptography.IKeyEncryptionKeyResolver keyResolver = new Azure.Security.KeyVault.Keys.Cryptography.KeyResolver(tokenCredential);
        /// CosmosClient client = (new CosmosClient(endpoint, authKey)).WithEncryption(keyResolver, KeyEncryptionKeyResolverName.AzureKeyVault);
        ///
        /// EncryptionKeyWrapMetadata wrapMetadata = new EncryptionKeyWrapMetadata(
        ///    type: KeyEncryptionKeyResolverName.AzureKeyVault,
        ///    name: "myKek",
        ///    value: "https://contoso.vault.azure.net/keys/myKek/78deebed173b48e48f55abf87ed4cf71",
        ///    algorithm: Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm.RsaOaep.ToString());
        ///
        ///  ClientEncryptionKeyResponse response = await client.GetDatabase("databaseId").CreateClientEncryptionKeyAsync(
        ///    "myCek",
        ///    DataEncryptionAlgorithm.AeadAes256CbcHmacSha256,
        ///    wrapMetadata);
        /// ]]>
        /// </code>
        /// </example>
        /// <remarks>
        /// See <see href="https://aka.ms/CosmosClientEncryption">client-side encryption documentation</see> for more details.
        /// </remarks>
        public static async Task <ClientEncryptionKeyResponse> CreateClientEncryptionKeyAsync(
            this Database database,
            string clientEncryptionKeyId,
            string encryptionAlgorithm,
            EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (string.IsNullOrWhiteSpace(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            if (!string.Equals(encryptionAlgorithm, DataEncryptionAlgorithm.AeadAes256CbcHmacSha256))
            {
                throw new ArgumentException($"Invalid encryption algorithm '{encryptionAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details.");
            }

            if (encryptionKeyWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
            }

            if (encryptionKeyWrapMetadata.Algorithm != EncryptionKeyStoreProviderImpl.RsaOaepWrapAlgorithm)
            {
                throw new ArgumentException($"Invalid key wrap algorithm '{encryptionKeyWrapMetadata.Algorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details.");
            }

            EncryptionCosmosClient encryptionCosmosClient = database is EncryptionDatabase encryptionDatabase
                ? encryptionDatabase.EncryptionCosmosClient
                : throw new ArgumentException("Creating a client encryption key requires the use of an encryption-enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details.");

            if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionCosmosClient.KeyEncryptionKeyResolverName))
            {
                throw new ArgumentException($"The Type of the EncryptionKeyWrapMetadata '{encryptionKeyWrapMetadata.Type}' does not match"
                                            + $" with the keyEncryptionKeyResolverName '{encryptionCosmosClient.KeyEncryptionKeyResolverName}' configured."
                                            + " Please refer to https://aka.ms/CosmosClientEncryption for more details.");
            }

            KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                encryptionKeyWrapMetadata.Name,
                encryptionKeyWrapMetadata.Value,
                encryptionCosmosClient.EncryptionKeyStoreProviderImpl);

            ProtectedDataEncryptionKey protectedDataEncryptionKey = new ProtectedDataEncryptionKey(
                clientEncryptionKeyId,
                keyEncryptionKey);

            byte[] wrappedDataEncryptionKey = protectedDataEncryptionKey.EncryptedValue;

            // cache it.
            ProtectedDataEncryptionKey.GetOrCreate(
                clientEncryptionKeyId,
                keyEncryptionKey,
                wrappedDataEncryptionKey);

            ClientEncryptionKeyProperties clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
                clientEncryptionKeyId,
                encryptionAlgorithm,
                wrappedDataEncryptionKey,
                encryptionKeyWrapMetadata);

            ClientEncryptionKeyResponse clientEncryptionKeyResponse = await database.CreateClientEncryptionKeyAsync(
                clientEncryptionKeyProperties,
                cancellationToken : cancellationToken);

            return(clientEncryptionKeyResponse);
        }
Exemple #17
0
        /// <inheritdoc/>
        public override async Task <ItemResponse <DataEncryptionKeyProperties> > RewrapDataEncryptionKeyAsync(
            string id,
            EncryptionKeyWrapMetadata newWrapMetadata,
            ItemRequestOptions requestOptions   = null,
            CancellationToken cancellationToken = default)
        {
            if (newWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newWrapMetadata));
            }

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            (DataEncryptionKeyProperties dekProperties, InMemoryRawDek inMemoryRawDek) = await this.FetchUnwrappedAsync(
                id,
                diagnosticsContext,
                cancellationToken);

            (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek updatedRawDek) = await this.WrapAsync(
                id,
                inMemoryRawDek.DataEncryptionKey.RawKey,
                dekProperties.EncryptionAlgorithm,
                newWrapMetadata,
                diagnosticsContext,
                cancellationToken);

            if (requestOptions == null)
            {
                requestOptions = new ItemRequestOptions();
            }

            requestOptions.IfMatchEtag = dekProperties.ETag;

            DataEncryptionKeyProperties newDekProperties = new DataEncryptionKeyProperties(dekProperties)
            {
                WrappedDataEncryptionKey  = wrappedDek,
                EncryptionKeyWrapMetadata = updatedMetadata,
            };

            ItemResponse <DataEncryptionKeyProperties> response;

            try
            {
                response = await this.DekProvider.Container.ReplaceItemAsync(
                    newDekProperties,
                    newDekProperties.Id,
                    new PartitionKey(newDekProperties.Id),
                    requestOptions,
                    cancellationToken);

                Debug.Assert(response.Resource != null);
            }
            catch (CosmosException ex)
            {
                if (!ex.StatusCode.Equals(HttpStatusCode.PreconditionFailed))
                {
                    throw;
                }

                // Handle if exception is due to etag mismatch. The scenario is as follows - say there are 2 clients A and B that both have the DEK properties cached.
                // From A, rewrap worked and the DEK is updated. Now from B, rewrap was attempted later based on the cached properties which will fail due to etag mismatch.
                // To address this, we do an explicit read, which reads the key from storage and updates the cached properties; and then attempt rewrap again.
                await this.ReadDataEncryptionKeyAsync(
                    newDekProperties.Id,
                    requestOptions,
                    cancellationToken);

                return(await this.RewrapDataEncryptionKeyAsync(
                           id,
                           newWrapMetadata,
                           requestOptions,
                           cancellationToken));
            }

            this.DekProvider.DekCache.SetDekProperties(id, response.Resource);
            this.DekProvider.DekCache.SetRawDek(id, updatedRawDek);
            return(response);
        }