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)); }
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); }
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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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; }
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); }