You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 4.7应用使用Azure.Security.KeyVault.Keys创建对称密钥问题

问题根因

报错和功能不生效来自两个核心问题:

  • CryptographyClient与KeyResolver仅支持Key Vault中存储的Key类型资源(资源路径格式为/keys/[密钥名称]),不支持直接读取/secrets/路径下的Secret资源,传入Secret URI会触发资源解析失败,抛出URI格式错误。
  • 原有旧逻辑是直接将Secret中存储的Base64字符串作为AES对称密钥完成Blob客户端加解密,没有使用Key Vault托管的非对称密钥做密钥包装,之前代码中硬编码KeyWrapAlgorithm = "RSA-OAEP"完全不符合原有业务逻辑,且新版SDK默认的V2加密格式和旧版SDK生成的V1加密Blob不兼容,会导致解密失败。
正确实现方案

新版Azure Blob SDK支持直接传入自定义实现的密钥对象完成客户端加解密,不需要依赖Key Vault托管Key资源,只需要先读取Secret中的对称密钥值,实现IKeyEncryptionKey接口包装对称密钥逻辑,再传入加密配置即可。

依赖包要求(.NET 4.7兼容)

安装以下稳定版NuGet包,注意选择支持.NET Framework 4.7的版本:

  • Azure.Security.KeyVault.Secrets:用于读取Key Vault中存储的Secret
  • Azure.Storage.Blobs:新版Blob存储基础客户端
  • Azure.Storage.Blobs.Specialized:提供Blob客户端加密扩展能力
  • Azure.Identity:提供Azure AD身份认证能力

步骤1:实现对称密钥包装类

新版SDK没有提供旧版SymmetricKey的内置实现,需要自己实现IKeyEncryptionKey接口,对齐旧版对称密钥的加解密逻辑:

using Azure.Core.Cryptography;
using System;
using System.Security.Cryptography;

public class AesSymmetricKey : IKeyEncryptionKey
{
    private readonly byte[] _keyContent;
    public string KeyId { get; }

    public AesSymmetricKey(string keyId, byte[] keyContent)
    {
        KeyId = keyId;
        _keyContent = keyContent;
    }

    public byte[] WrapKey(string algorithm, ReadOnlyMemory<byte> dataKey)
    {
        using var aes = Aes.Create();
        aes.Key = _keyContent;
        aes.GenerateIV();
        using var encryptor = aes.CreateEncryptor();
        var encryptedKey = encryptor.TransformFinalBlock(dataKey.ToArray(), 0, dataKey.Length);
        // 拼接IV与加密后的数据密钥,解密时需要拆分使用
        var result = new byte[aes.IV.Length + encryptedKey.Length];
        Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
        Buffer.BlockCopy(encryptedKey, 0, result, aes.IV.Length, encryptedKey.Length);
        return result;
    }

    public byte[] UnwrapKey(string algorithm, ReadOnlyMemory<byte> encryptedDataKey)
    {
        using var aes = Aes.Create();
        aes.Key = _keyContent;
        var ivLength = aes.BlockSize / 8;
        var iv = new byte[ivLength];
        var cipherText = new byte[encryptedDataKey.Length - ivLength];
        encryptedDataKey.Slice(0, ivLength).CopyTo(iv);
        encryptedDataKey.Slice(ivLength).CopyTo(cipherText);
        aes.IV = iv;
        using var decryptor = aes.CreateDecryptor();
        return decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);
    }

    // 本地对称密钥不需要签名/验签逻辑,直接抛出未实现异常即可
    public byte[] Sign(string algorithm, ReadOnlyMemory<byte> digest) => throw new NotImplementedException();
    public bool Verify(string algorithm, ReadOnlyMemory<byte> digest, ReadOnlyMemory<byte> signature) => throw new NotImplementedException();
}

步骤2:实现加密Blob下载逻辑

注意:旧版Microsoft.Azure.Storage.Blob使用V1版本客户端加密格式,配置时必须选择ClientSideEncryptionVersion.V1_0,否则会出现密文格式不兼容问题。

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Blobs.Specialized;
using System;
using System.IO;
using System.Threading.Tasks;

public async Task DownloadEncryptedBlobs(string storageConnStr, string keyVaultEndpoint, string secretName, string containerName, string blobPrefix, string localSavePath)
{
    // 1. 从Key Vault读取存储对称密钥的Secret
    var secretClient = new SecretClient(new Uri(keyVaultEndpoint), new DefaultAzureCredential());
    KeyVaultSecret encryptionSecret = await secretClient.GetSecretAsync(secretName);
    byte[] symmetricKeyBytes = Convert.FromBase64String(encryptionSecret.Value);

    // 2. 构造对称密钥实例
    var symmetricKey = new AesSymmetricKey(secretName, symmetricKeyBytes);

    // 3. 配置客户端加密选项
    var encryptionOptions = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V1_0)
    {
        KeyEncryptionKey = symmetricKey,
        KeyResolver = null // 直接使用本地对称密钥,不需要密钥解析器
    };

    // 4. 初始化Blob客户端
    var blobClientOptions = new SpecializedBlobClientOptions { ClientSideEncryption = encryptionOptions };
    var blobServiceClient = new BlobServiceClient(storageConnStr, blobClientOptions);
    var targetContainer = blobServiceClient.GetBlobContainerClient(containerName);

    // 5. 遍历指定前缀的Blob,SDK会自动完成解密下载
    await foreach (BlobItem blob in targetContainer.GetBlobsAsync(prefix: blobPrefix))
    {
        var blobClient = targetContainer.GetBlobClient(blob.Name);
        var localFilePath = Path.Combine(localSavePath, blob.Name.Replace(blobPrefix, "").TrimStart('/'));
        // 自动创建本地目录
        var fileDir = Path.GetDirectoryName(localFilePath);
        if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
        
        await blobClient.DownloadToAsync(localFilePath);
    }
}
注意事项
  • 确保DefaultAzureCredential使用的身份拥有Key Vault对应Secret的Get权限,以及目标Blob容器的读权限。
  • 如果旧逻辑自定义了AES加密模式、填充方式,需要对应调整AesSymmetricKey类中的加解密实现,和旧逻辑保持一致即可正常解密。
  • 禁止将Secret类型资源的URI传入CryptographyClient,该类型仅面向Key Vault托管的Key资源设计,无法读取Secret内容。

内容的提问来源于stack exchange,提问作者Shailesh Prajapati

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:18:15