SQL Server Always Encrypted:CEK的ENCRYPTED_VALUE生成及非SSMS方式探讨
嘿,我来帮你把SQL Server Always Encrypted里CEK的ENCRYPTED_VALUE相关问题掰扯清楚,分点给你讲明白:
先理清楚核心逻辑:ENCRYPTED_VALUE其实是**列加密密钥(CEK)被列主密钥(CMK)**加密后的产物。Always Encrypted的加密链路是:用AES-256对称密钥(也就是CEK明文)加密业务数据,再用非对称的CMK(比如RSA密钥)加密这个CEK明文——所以ENCRYPTED_VALUE就是加密后的CEK二进制数据,一般会转成十六进制字符串方便在SQL语句里使用。
生成的核心步骤就两步:
- 生成一个随机的32字节AES-256密钥(这就是CEK的明文,绝对不能泄露给SQL Server)
- 用CMK的公钥,通过指定的算法(比如
RSA_OAEP)加密这个CEK明文,得到的结果就是ENCRYPTED_VALUE
当然有!SSMS只是把底层操作做了可视化封装,你完全可以用脚本或者代码手动实现,这里给你两种常用方案:
1. PowerShell脚本(官方推荐的自动化方式)
先确保安装了SqlServer模块,然后按步骤来:
# 安装并导入模块(如果没装过的话) Install-Module -Name SqlServer -Force Import-Module SqlServer # 获取已有的CMK对象(这里假设你的CMK已经在SQL Server里注册了) $cmk = Get-SqlColumnMasterKey -Name "MyCMK" -ServerInstance "你的SQL服务器地址" -Database "目标库" # 生成随机的AES-256 CEK明文 $cekPlaintext = New-Object byte[] 32 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($cekPlaintext) # 用CMK加密CEK明文 $encryptedCek = Invoke-SqlColumnMasterKeyEncrypt -ColumnMasterKey $cmk -Plaintext $cekPlaintext # 转成带0x前缀的十六进制字符串(符合SQL语法要求) $encryptedValueHex = "0x" + [System.BitConverter]::ToString($encryptedCek).Replace("-", "")
最后把$encryptedValueHex的值直接填到CREATE语句里就行。
2. C#/.NET自定义实现
如果需要更灵活的逻辑,直接用.NET的加密API写代码也可以:
using System; using System.Security.Cryptography; // 第一步:获取CMK的公钥(这里以Azure Key Vault为例,也可以从本地证书存储取) RSA cmkPublicKey = GetCmkPublicKeyFromYourKeyStore("你的密钥存储地址/名称"); // 第二步:生成随机AES-256 CEK明文 byte[] cekPlaintext = new byte[32]; RandomNumberGenerator.Fill(cekPlaintext); // 第三步:用RSA_OAEP算法加密CEK byte[] encryptedCek = cmkPublicKey.Encrypt(cekPlaintext, RSAEncryptionPadding.OaepSHA256); // 第四步:转成带0x前缀的十六进制字符串 string encryptedValueHex = "0x" + BitConverter.ToString(encryptedCek).Replace("-", "");
要是你的CMK存在本地Windows证书存储,直接用X509Certificate2类加载证书就能拿到公钥,完全不用碰AKV。
注意:SQL Server本身不会帮你做这个操作,因为Always Encrypted是客户端加密设计——密钥操作全在客户端完成,绝对不会把明文密钥传到SQL Server实例里,避免泄露风险。
先纠正一个认知:Always Encrypted的核心是客户端加密,数据在客户端加密后才会传到SQL Server,SQL Server自始至终看不到任何明文密钥和明文数据。
关于“未知密钥”:
- 不存在用完全未知的密钥加密数据的情况,因为加密数据必须用到CEK的明文,而CEK明文只能通过CMK的私钥解密ENCRYPTED_VALUE才能拿到——你连密钥是什么都不知道,根本没法加密数据。
关于脱离AKV解密:
- 完全可以!AKV只是CMK的一种存储方式,你可以把CMK存在本地证书存储、本地HSM(硬件安全模块),甚至自己管理的密钥服务里,只要你的客户端能访问到CMK的私钥,就能解密CEK,进而解密数据,完全不需要依赖AKV。
举个实际例子:如果你的CMK是客户端机器上的自签名证书,只要运行解密程序的机器能访问到这个证书的私钥,就能直接解密数据,连网络都不用开。
用上面生成的十六进制字符串,执行下面的SQL语句就能创建CEK:
CREATE COLUMN ENCRYPTION KEY MyCEK WITH VALUES ( COLUMN_MASTER_KEY = MyCMK, ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0xABCDEF123456... -- 这里填你生成的十六进制字符串 );
内容的提问来源于stack exchange,提问作者sglogowski

