docs: def _extract_encryption_metadata(entity, require_encryption, key_encryption_key, key_resolver):
      '''
      Extracts the encryption metadata from the given entity, setting them to be utf-8 strings.
      If no encryption metadata is present, will return None for all return values unless
      require_encryption is true, in which case the method will throw.
  
      :param entity:
          The entity being retrieved and decrypted. Could be a dict or an entity object.
      :param bool require_encryption:
          If set, will enforce that the retrieved entity is encrypted and decrypt it.
      :param object key_encryption_key:
          The user-provided key-encryption-key. Must implement the following methods:
          unwrap_key(key, algorithm)--returns the unwrapped form of the specified symmetric key using the 
          string-specified algorithm.
          get_kid()--returns a string key id for this key-encryption-key.
      :param function key_resolver(kid):
          The user-provided key resolver. Uses the kid string to return a key-encryption-key implementing
          the interface defined above.
      :returns: a tuple containing the entity iv, the list of encrypted properties, the entity cek,
          and whether the entity was encrypted using JavaV1.
      :rtype: tuple (bytes[], list, bytes[], bool)
      '''
      _validate_not_none('entity', entity)
      
      try:
          encrypted_properties_list = _decode_base64_to_bytes(entity['_ClientEncryptionMetadata2'])
          encryption_data = entity['_ClientEncryptionMetadata1']
          encryption_data = _dict_to_encryption_data(loads(encryption_data))
      except Exception as e:
          # Message did not have properly formatted encryption metadata.
          if require_encryption:
              raise ValueError(_ERROR_ENTITY_NOT_ENCRYPTED)
          else:
              return (None,None,None,None)
  
      if not(encryption_data.encryption_agent.encryption_algorithm == _EncryptionAlgorithm.AES_CBC_256):
          raise ValueError(_ERROR_UNSUPPORTED_ENCRYPTION_ALGORITHM)
  
      content_encryption_key = _validate_and_unwrap_cek(encryption_data, key_encryption_key, key_resolver)
  
      # Special check for compatibility with Java V1 encryption protocol.
      isJavaV1 = (encryption_data.key_wrapping_metadata is None) or \
          ((encryption_data.encryption_agent.protocol == _ENCRYPTION_PROTOCOL_V1) and \
          'EncryptionLibrary' in encryption_data.key_wrapping_metadata and \
          'Java' in encryption_data.key_wrapping_metadata['EncryptionLibrary'])
  
      metadataIV = _generate_property_iv(encryption_data.content_encryption_IV,
                                        entity['PartitionKey'], entity['RowKey'],
                                        '_ClientEncryptionMetadata2', isJavaV1)
  
      cipher = _generate_AES_CBC_cipher(content_encryption_key, metadataIV)
  
      # Decrypt the data.
      decryptor = cipher.decryptor()
      encrypted_properties_list = decryptor.update(encrypted_properties_list) + decryptor.finalize()
  
      # Unpad the data.
      unpadder = PKCS7(128).unpadder()
      encrypted_properties_list = unpadder.update(encrypted_properties_list) + unpadder.finalize()
  
      encrypted_properties_list = encrypted_properties_list.decode('utf-8')
  
      if isJavaV1:
          # Strip the square braces from the ends and split string into list.
          encrypted_properties_list = encrypted_properties_list[1:-1]
          encrypted_properties_list = encrypted_properties_list.split(', ')
      else:
          encrypted_properties_list = loads(encrypted_properties_list)
  
      return (encryption_data.content_encryption_IV, encrypted_properties_list, content_encryption_key, isJavaV1)