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

如何在Spring Boot应用中基于Azure Cosmos DB实现Always Encrypted?

Spring Boot + Cosmos DB 实现类 Always Encrypted 字段级加密方案

核心说明

Always Encrypted 原是 SQL Server 的专属特性,但如果要在 Cosmos DB 中实现客户端侧敏感字段加密(即应用层加密后存入数据库,读取时解密),完全可以通过 JDBC/SDK 结合 Azure Key Vault 实现类似效果,无需依赖 Windows/Docker 环境的 SQL Server。

具体实现步骤

1. 依赖配置(Maven 示例,Gradle 可对应转换)

在 pom.xml 中添加必要依赖:

<dependencies>
    <!-- Azure Key Vault 密钥管理 -->
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-security-keyvault-keys</artifactId>
        <version>4.8.0</version>
    </dependency>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity</artifactId>
        <version>1.12.0</version>
    </dependency>
    <!-- Cosmos DB Java SDK -->
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>4.56.0</version>
    </dependency>
    <!-- 加密工具依赖 -->
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.77</version>
    </dependency>
</dependencies>

2. Azure Key Vault 密钥准备

  • 在 Azure 门户创建 Key Vault,生成RSA 密钥(作为密钥加密密钥 KEK,用于加密数据密钥 DEK)
  • 给 Spring Boot 应用分配 Key Vault 的访问权限(托管身份或服务主体均可),确保应用具备 get、encrypt、decrypt 密钥的权限

3. 客户端加密工具类实现

封装加密解密逻辑,负责从 Key Vault 获取密钥、管理数据密钥、处理字段加密:

import com.azure.security.keyvault.keys.KeyClient;
import com.azure.security.keyvault.keys.cryptography.CryptographyClient;
import com.azure.security.keyvault.keys.cryptography.models.DecryptResult;
import com.azure.security.keyvault.keys.cryptography.models.EncryptResult;
import com.azure.security.keyvault.keys.models.KeyVaultKey;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class EncryptionUtils {
    private final CryptographyClient cryptoClient;

    public EncryptionUtils(KeyClient keyClient, String keyName) {
        KeyVaultKey key = keyClient.getKey(keyName);
        this.cryptoClient = new CryptographyClient(key, keyClient.getHttpPipeline());
    }

    // 生成并加密数据密钥(DEK)
    public String generateAndEncryptDEK() throws Exception {
        SecretKey dek = Cipher.getInstance("AES").getKeyGenerator().generateKey();
        byte[] dekBytes = dek.getEncoded();
        EncryptResult encryptResult = cryptoClient.encrypt("RSA-OAEP", dekBytes);
        return Base64.getEncoder().encodeToString(encryptResult.getCiphertext());
    }

    // 解密数据密钥
    public SecretKey decryptDEK(String encryptedDEK) throws Exception {
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedDEK);
        DecryptResult decryptResult = cryptoClient.decrypt("RSA-OAEP", encryptedBytes);
        return new SecretKeySpec(decryptResult.getPlaintext(), "AES");
    }

    // 加密敏感字段(GCM模式,带IV)
    public String encryptField(String plainText, SecretKey dek) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, dek);
        byte[] encrypted = cipher.doFinal(plainText.getBytes());
        byte[] iv = cipher.getIV();
        byte[] combined = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, combined, 0, iv.length);
        System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
        return Base64.getEncoder().encodeToString(combined);
    }

    // 解密敏感字段
    public String decryptField(String encryptedText, SecretKey dek) throws Exception {
        byte[] combined = Base64.getDecoder().decode(encryptedText);
        byte[] iv = new byte[12];
        byte[] encrypted = new byte[combined.length - iv.length];
        System.arraycopy(combined, 0, iv, 0, iv.length);
        System.arraycopy(combined, iv.length, encrypted, 0, encrypted.length);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, dek, new javax.crypto.spec.GCMParameterSpec(128, iv));
        byte[] plainBytes = cipher.doFinal(encrypted);
        return new String(plainBytes);
    }
}

4. Spring Boot 与 Cosmos DB 集成

  • 配置文件 application.properties 添加连接信息:
azure.cosmos.uri=你的CosmosDB URI
azure.cosmos.key=你的CosmosDB主键
azure.cosmos.database=目标数据库名
azure.keyvault.uri=你的Key Vault URI
azure.keyvault.key-name=之前创建的RSA密钥名
  • 实体类定义(存储加密后的字段和DEK):
import com.azure.spring.data.cosmos.core.mapping.Container;
import com.azure.spring.data.cosmos.core.mapping.PartitionKey;

@Container(containerName = "users")
public class User {
    private String id;
    @PartitionKey
    private String username;
    private String encryptedEmail; // 加密后的邮箱
    private String encryptedDEK; // 加密后的DEK

    // getter、setter省略
}
  • Service 层业务逻辑处理:
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private final UserRepository userRepository;
    private final EncryptionUtils encryptionUtils;

    public UserService(UserRepository userRepository, EncryptionUtils encryptionUtils) {
        this.userRepository = userRepository;
        this.encryptionUtils = encryptionUtils;
    }

    public User createUser(String username, String email) throws Exception {
        User user = new User();
        user.setUsername(username);
        // 生成并加密DEK,存入文档
        String encryptedDEK = encryptionUtils.generateAndEncryptDEK();
        user.setEncryptedDEK(encryptedDEK);
        // 解密DEK后加密敏感字段
        SecretKey dek = encryptionUtils.decryptDEK(encryptedDEK);
        user.setEncryptedEmail(encryptionUtils.encryptField(email, dek));
        return userRepository.save(user);
    }

    public String getUserEmail(String userId) throws Exception {
        User user = userRepository.findById(userId).orElseThrow();
        SecretKey dek = encryptionUtils.decryptDEK(user.getEncryptedDEK());
        return encryptionUtils.decryptField(user.getEncryptedEmail(), dek);
    }
}

关键注意事项

  • 此方案是客户端字段级加密,和 Always Encrypted 逻辑一致:敏感数据在应用层加密,传输、存储全程密文,只有授权应用能解密
  • 每个文档存储独立的加密DEK,用Key Vault的KEK加密,避免单密钥泄露影响全部数据
  • 可通过 Spring AOP 封装加密解密逻辑,减少业务代码重复

内容的提问来源于stack exchange,提问作者Andy Escobar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 08:01:01