private async Task <ProtectedDataEncryptionKey> BuildProtectedDataEncryptionKeyAsync(
            ClientEncryptionKeyProperties clientEncryptionKeyProperties,
            string keyId,
            CancellationToken cancellationToken)
        {
            if (await EncryptionCosmosClient.EncryptionKeyCacheSemaphore.WaitAsync(-1, cancellationToken))
            {
                try
                {
                    KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                        clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
                        clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
                        this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProviderImpl);

                    ProtectedDataEncryptionKey protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate(
                        keyId,
                        keyEncryptionKey,
                        clientEncryptionKeyProperties.WrappedDataEncryptionKey);

                    return(protectedDataEncryptionKey);
                }
                finally
                {
                    EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Release(1);
                }
            }

            throw new InvalidOperationException("Failed to build ProtectedDataEncryptionKey. ");
        }
        public async Task <AeadAes256CbcHmac256EncryptionAlgorithm> BuildEncryptionAlgorithmForSettingAsync(CancellationToken cancellationToken)
        {
            ClientEncryptionKeyProperties clientEncryptionKeyProperties = await this.encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                clientEncryptionKeyId : this.ClientEncryptionKeyId,
                encryptionContainer : this.encryptionContainer,
                databaseRid : this.databaseRid,
                ifNoneMatchEtag : null,
                shouldForceRefresh : false,
                cancellationToken : cancellationToken);

            ProtectedDataEncryptionKey protectedDataEncryptionKey;

            try
            {
                // we pull out the Encrypted Data Encryption Key and build the Protected Data Encryption key
                // Here a request is sent out to unwrap using the Master Key configured via the Key Encryption Key.
                protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync(
                    clientEncryptionKeyProperties,
                    this.ClientEncryptionKeyId,
                    cancellationToken);
            }
            catch (RequestFailedException ex) when(ex.Status == (int)HttpStatusCode.Forbidden)
            {
                // The access to master key was probably revoked. Try to fetch the latest ClientEncryptionKeyProperties from the backend.
                // This will succeed provided the user has rewraped the Client Encryption Key with right set of meta data.
                // This is based on the AKV provider implementaion so we expect a RequestFailedException in case other providers are used in unwrap implementation.
                // first try to force refresh the local cache, we might have a stale cache.
                clientEncryptionKeyProperties = await this.encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                    clientEncryptionKeyId : this.ClientEncryptionKeyId,
                    encryptionContainer : this.encryptionContainer,
                    databaseRid : this.databaseRid,
                    ifNoneMatchEtag : null,
                    shouldForceRefresh : true,
                    cancellationToken : cancellationToken);

                try
                {
                    // try to build the ProtectedDataEncryptionKey. If it fails, try to force refresh the gateway cache and get the latest client encryption key.
                    protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync(
                        clientEncryptionKeyProperties,
                        this.ClientEncryptionKeyId,
                        cancellationToken);
                }
                catch (RequestFailedException exOnRetry) when(exOnRetry.Status == (int)HttpStatusCode.Forbidden)
                {
                    // the gateway cache could be stale. Force refresh the gateway cache.
                    // bail out if this fails.
                    protectedDataEncryptionKey = await this.ForceRefreshGatewayCacheAndBuildProtectedDataEncryptionKeyAsync(
                        existingCekEtag : clientEncryptionKeyProperties.ETag,
                        refreshRetriedOnException : exOnRetry,
                        cancellationToken : cancellationToken);
                }
            }

            AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = new AeadAes256CbcHmac256EncryptionAlgorithm(
                protectedDataEncryptionKey,
                this.EncryptionType);

            return(aeadAes256CbcHmac256EncryptionAlgorithm);
        }
 public override Task <ClientEncryptionKeyResponse> CreateClientEncryptionKeyAsync(
     ClientEncryptionKeyProperties clientEncryptionKeyProperties,
     RequestOptions requestOptions       = null,
     CancellationToken cancellationToken = default)
 {
     return(this.database.CreateClientEncryptionKeyAsync(
                clientEncryptionKeyProperties,
                requestOptions,
                cancellationToken));
 }
Exemplo n.º 4
0
        public async Task <AeadAes256CbcHmac256EncryptionAlgorithm> BuildEncryptionAlgorithmForSettingAsync(CancellationToken cancellationToken)
        {
            ClientEncryptionKeyProperties clientEncryptionKeyProperties = await this.encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                clientEncryptionKeyId : this.ClientEncryptionKeyId,
                encryptionContainer : this.encryptionContainer,
                databaseRid : this.databaseRid,
                cancellationToken : cancellationToken);

            ProtectedDataEncryptionKey protectedDataEncryptionKey;

            try
            {
                // we pull out the Encrypted Data Encryption Key and build the Protected Data Encryption key
                // Here a request is sent out to unwrap using the Master Key configured via the Key Encryption Key.
                protectedDataEncryptionKey = this.BuildProtectedDataEncryptionKey(
                    clientEncryptionKeyProperties,
                    this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider,
                    this.ClientEncryptionKeyId);
            }
            catch (RequestFailedException ex)
            {
                // The access to master key was probably revoked. Try to fetch the latest ClientEncryptionKeyProperties from the backend.
                // This will succeed provided the user has rewraped the Client Encryption Key with right set of meta data.
                // This is based on the AKV provider implementaion so we expect a RequestFailedException in case other providers are used in unwrap implementation.
                if (ex.Status == (int)HttpStatusCode.Forbidden)
                {
                    clientEncryptionKeyProperties = await this.encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                        clientEncryptionKeyId : this.ClientEncryptionKeyId,
                        encryptionContainer : this.encryptionContainer,
                        databaseRid : this.databaseRid,
                        cancellationToken : cancellationToken,
                        shouldForceRefresh : true);

                    // just bail out if this fails.
                    protectedDataEncryptionKey = this.BuildProtectedDataEncryptionKey(
                        clientEncryptionKeyProperties,
                        this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider,
                        this.ClientEncryptionKeyId);
                }
                else
                {
                    throw;
                }
            }

            AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = new AeadAes256CbcHmac256EncryptionAlgorithm(
                protectedDataEncryptionKey,
                this.EncryptionType);

            return(aeadAes256CbcHmac256EncryptionAlgorithm);
        }
Exemplo n.º 5
0
        internal ProtectedDataEncryptionKey BuildProtectedDataEncryptionKey(
            ClientEncryptionKeyProperties clientEncryptionKeyProperties,
            EncryptionKeyStoreProvider encryptionKeyStoreProvider,
            string keyId)
        {
            KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
                clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
                encryptionKeyStoreProvider);

            return(new ProtectedDataEncryptionKey(
                       keyId,
                       keyEncryptionKey,
                       clientEncryptionKeyProperties.WrappedDataEncryptionKey));
        }
Exemplo n.º 6
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);
        }
Exemplo n.º 7
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);
        }
Exemplo n.º 8
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);
        }
Exemplo n.º 9
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>
        /// Builds up and caches the Encryption Setting by getting the cached entries of Client Encryption Policy and the corresponding keys.
        /// Sets up the MDE Algorithm for encryption and decryption by initializing the KeyEncryptionKey and ProtectedDataEncryptionKey.
        /// </summary>
        /// <param name="cancellationToken"> cancellation token </param>
        /// <returns> Task </returns>
        internal async Task InitializeEncryptionSettingsAsync(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (this.isEncryptionSettingsInitDone)
            {
                throw new InvalidOperationException("The Encrypton Processor has already been initialized. ");
            }

            // fetch the cached policy.
            this.ClientEncryptionPolicy = await this.EncryptionCosmosClient.GetClientEncryptionPolicyAsync(
                container : this.Container,
                cancellationToken : cancellationToken,
                shouldForceRefresh : false);

            // no policy was configured.
            if (this.ClientEncryptionPolicy == null)
            {
                this.isEncryptionSettingsInitDone = true;
                return;
            }

            Dictionary <string, EncryptionSettings> settingsByDekId = new Dictionary <string, EncryptionSettings>();

            // update the property level setting.
            foreach (ClientEncryptionIncludedPath propertyToEncrypt in this.ClientEncryptionPolicy.IncludedPaths)
            {
                if (!settingsByDekId.ContainsKey(propertyToEncrypt.ClientEncryptionKeyId))
                {
                    string clientEncryptionKeyId = propertyToEncrypt.ClientEncryptionKeyId;

                    ClientEncryptionKeyProperties clientEncryptionKeyProperties = await this.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                        clientEncryptionKeyId : clientEncryptionKeyId,
                        container : this.Container,
                        cancellationToken : cancellationToken,
                        shouldForceRefresh : false);

                    ProtectedDataEncryptionKey protectedDataEncryptionKey = null;

                    try
                    {
                        // we pull out the Encrypted Data Encryption Key and build the Protected Data Encryption key
                        // Here a request is sent out to unwrap using the Master Key configured via the Key Encryption Key.
                        protectedDataEncryptionKey = this.EncryptionSettings.BuildProtectedDataEncryptionKey(
                            clientEncryptionKeyProperties,
                            this.EncryptionKeyStoreProvider,
                            clientEncryptionKeyId);
                    }
                    catch (RequestFailedException ex)
                    {
                        // The access to master key was probably revoked. Try to fetch the latest ClientEncryptionKeyProperties from the backend.
                        // This will succeed provided the user has rewraped the Client Encryption Key with right set of meta data.
                        // This is based on the AKV provider implementaion so we expect a RequestFailedException in case other providers are used in unwrap implementation.
                        if (ex.Status == (int)HttpStatusCode.Forbidden)
                        {
                            clientEncryptionKeyProperties = await this.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                                clientEncryptionKeyId : clientEncryptionKeyId,
                                container : this.Container,
                                cancellationToken : cancellationToken,
                                shouldForceRefresh : true);

                            // just bail out if this fails.
                            protectedDataEncryptionKey = this.EncryptionSettings.BuildProtectedDataEncryptionKey(
                                clientEncryptionKeyProperties,
                                this.EncryptionKeyStoreProvider,
                                clientEncryptionKeyId);
                        }
                        else
                        {
                            throw;
                        }
                    }

                    settingsByDekId[clientEncryptionKeyId] = new EncryptionSettings
                    {
                        // we cache the setting for performance reason.
                        EncryptionSettingTimeToLive = DateTime.UtcNow + TimeSpan.FromMinutes(Constants.CachedEncryptionSettingsDefaultTTLInMinutes),
                        ClientEncryptionKeyId       = clientEncryptionKeyId,
                        DataEncryptionKey           = protectedDataEncryptionKey,
                    };
                }

                EncryptionType encryptionType = EncryptionType.Plaintext;
                switch (propertyToEncrypt.EncryptionType)
                {
                case CosmosEncryptionType.Deterministic:
                    encryptionType = EncryptionType.Deterministic;
                    break;

                case CosmosEncryptionType.Randomized:
                    encryptionType = EncryptionType.Randomized;
                    break;

                case CosmosEncryptionType.Plaintext:
                    encryptionType = EncryptionType.Plaintext;
                    break;

                default:
                    throw new ArgumentException($"Invalid encryption type {propertyToEncrypt.EncryptionType}. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
                }

                string propertyName = propertyToEncrypt.Path.Substring(1);

                this.EncryptionSettings.SetEncryptionSettingForProperty(
                    propertyName,
                    EncryptionSettings.Create(
                        settingsByDekId[propertyToEncrypt.ClientEncryptionKeyId],
                        encryptionType),
                    settingsByDekId[propertyToEncrypt.ClientEncryptionKeyId].EncryptionSettingTimeToLive);
            }

            this.isEncryptionSettingsInitDone = true;
        }
Exemplo n.º 11
0
        private async Task <CachedEncryptionSettings> FetchCachedEncryptionSettingsAsync(
            string propertyName,
            EncryptionProcessor encryptionProcessor,
            CancellationToken cancellationToken)
        {
            ClientEncryptionPolicy clientEncryptionPolicy = await encryptionProcessor.EncryptionCosmosClient.GetClientEncryptionPolicyAsync(
                encryptionProcessor.Container,
                cancellationToken,
                false);

            if (clientEncryptionPolicy != null)
            {
                foreach (ClientEncryptionIncludedPath propertyToEncrypt in clientEncryptionPolicy.IncludedPaths)
                {
                    if (string.Equals(propertyToEncrypt.Path.Substring(1), propertyName))
                    {
                        ClientEncryptionKeyProperties clientEncryptionKeyProperties = await encryptionProcessor.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                            clientEncryptionKeyId : propertyToEncrypt.ClientEncryptionKeyId,
                            container : encryptionProcessor.Container,
                            cancellationToken : cancellationToken,
                            shouldForceRefresh : false);

                        ProtectedDataEncryptionKey protectedDataEncryptionKey = null;

                        try
                        {
                            protectedDataEncryptionKey = this.BuildProtectedDataEncryptionKey(
                                clientEncryptionKeyProperties,
                                encryptionProcessor.EncryptionKeyStoreProvider,
                                propertyToEncrypt.ClientEncryptionKeyId);
                        }
                        catch (RequestFailedException ex)
                        {
                            // the key was revoked. Try to fetch the latest EncryptionKeyProperties from the backend.
                            // This should succeed provided the user has rewraped the key with right set of meta data.
                            if (ex.Status == (int)HttpStatusCode.Forbidden)
                            {
                                clientEncryptionKeyProperties = await encryptionProcessor.EncryptionCosmosClient.GetClientEncryptionKeyPropertiesAsync(
                                    clientEncryptionKeyId : propertyToEncrypt.ClientEncryptionKeyId,
                                    container : encryptionProcessor.Container,
                                    cancellationToken : cancellationToken,
                                    shouldForceRefresh : true);

                                protectedDataEncryptionKey = this.BuildProtectedDataEncryptionKey(
                                    clientEncryptionKeyProperties,
                                    encryptionProcessor.EncryptionKeyStoreProvider,
                                    propertyToEncrypt.ClientEncryptionKeyId);
                            }
                            else
                            {
                                throw;
                            }
                        }

                        EncryptionSettings encryptionSettings = new EncryptionSettings
                        {
                            EncryptionSettingTimeToLive = DateTime.UtcNow + TimeSpan.FromMinutes(Constants.CachedEncryptionSettingsDefaultTTLInMinutes),
                            ClientEncryptionKeyId       = propertyToEncrypt.ClientEncryptionKeyId,
                            DataEncryptionKey           = protectedDataEncryptionKey,
                        };

                        EncryptionType encryptionType = EncryptionType.Plaintext;
                        switch (propertyToEncrypt.EncryptionType)
                        {
                        case CosmosEncryptionType.Deterministic:
                            encryptionType = EncryptionType.Deterministic;
                            break;

                        case CosmosEncryptionType.Randomized:
                            encryptionType = EncryptionType.Randomized;
                            break;

                        case CosmosEncryptionType.Plaintext:
                            encryptionType = EncryptionType.Plaintext;
                            break;

                        default:
                            throw new ArgumentException($"Invalid encryption type {propertyToEncrypt.EncryptionType}. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
                        }

                        encryptionSettings = EncryptionSettings.Create(encryptionSettings, encryptionType);
                        return(new CachedEncryptionSettings(encryptionSettings, encryptionSettings.EncryptionSettingTimeToLive));
                    }
                }
            }

            return(null);
        }