【问题标题】:How to verify code signed assembly in SQL如何在 SQL 中验证代码签名的程序集
【发布时间】:2018-09-16 20:59:53
【问题描述】:

我已经阅读了大量有关 SQL Server (2012) 中的非对称密钥和证书的信息,但它们似乎专门用于签署数据。我拥有的是一个 SQL CLR 程序集,我已使用数字证书签名并成功加载为 UNSAFE 程序集并成功执行了 SqlFunction。但是,我想向 SqlFunction 添加逻辑以验证程序集是否由我的证书签名。对于所有其他程序集,我们提取公钥并将其与许可证文件中的密钥进行比较。但是,当程序集在 SQL 内部执行时,似乎没有办法提取公钥。是否有一些我不理解的 SQL CLR 或 CERT/ASK 体系结构以便访问它?蒂亚!

需求说明:

我们销售的软件可以将二进制格式的数据生成到我们的数据库架构中,因此客户无法轻松地直接使用 T-SQL 查询数据。这样做既是为了提高性能,也是为了黑盒我们输出数据的数据分析算法。目前客户必须使用我们的软件通过“BusnLogicCore.dll”“解压”这个 blob 数据。只有当调用程序集已由我们事先批准的证书签名时,此 dll 才会执行。对于购买我们 SDK 的客户,客户向我们发送他们自己证书的公钥。我们将 pk 添加到我们颁发给他们的许可证文件中,以便程序集可以确认他们被允许调用 BusnLogicCore.dll。我们现在正在创建 SQL CLR 函数,该函数将允许客户使用 T-SQL 调用 BusnLogicCore.dll 并将 blob 数据解压缩为查询的一部分。但是,我们无法确认 SQL CLR 函数已由我们批准调用 BusnLogicCore.dll 的证书签名。如果我们在这个逻辑上戳个洞,让SQL CLR函数不用经过这个验证,那么没有购买SDK的客户可以编写自己的SQL CLR函数调用BusnLogicCore.dll。它比这更复杂一点,我们可以对许可证进行额外的检查,但最好不要绕过这个验证。这是一个边缘案例吗?可能,但我们想尽我们所能保护我们的 IP,没有这个选项是令人沮丧的。

更新: 我检查了 sys.certificates 表,但 cert_serial_number 和指纹列都与证书的公钥不匹配。

【问题讨论】:

  • 我的帖子上线不到 20 分钟,并且已被否决。你能说一下为什么吗?
  • 不知道为什么它被否决了,但我只是投了赞成票。也不知道为什么你甚至想要/需要这样做,因为答案是简单地通过能够首先在 SQL Server 中创建程序集来暗示。不过,我在回答中提供了一些细节。也许您可以更新问题以详细说明确切您要完成的工作;-)

标签: .net sql-server code-signing sqlclr digital-certificate


【解决方案1】:

假设您首先将证书加载到master DB,从该证书创建一个登录,并授予该登录UNSAFE ASSEMBLY 权限,这样您就可以使用PERMISSION_SET = UNASFE 创建程序集,那么我不'不知道你为什么想要/需要做你所要求的,因为那个问题那时已经得到了回答。

意思是,如果您能够将程序集创建为UNSAFE(假设数据库将TRUSTWORTHY 设置为OFF,因此使用证书——无论如何这是更好的方法),那么只有当程序集使用首先加载到master 等的证书(或强名称密钥/非对称密钥)签名时才会发生(如本答案开头所述)。如果程序集未签名,或者由不同的证书(或强名称密钥)签名,则在尝试将其创建为 UNSAFEEXTERNAL_ACCESS(甚至从 SQL Server 2017 开始为 SAFE)时会出错)。

不过,sys.certificates 系统目录视图中仍有一些信息可用于匹配数据库之间的证书。如果您在 master 创建证书之前加载了您的程序集(通过将 DB 设置为 TRUSTWORTHY ON - 坏主意,或者通过加载为 SAFE 以在设置后更改为 UNSAFE master 中的证书 - 从 SQL Server 2017 开始将不起作用),然后您可以使用 CREATE CERTIFICATE [bob] FROM ASSEMBLY [YourAssembly]; 从程序集中提取证书,然后从 sys.certificates 获取该证书的 cert_serial_numberthumbprint .

再澄清一点:

  1. 您无法通过任何内置机制在 SQL Server 中直接获取实际的公钥(有时称为“证书”,因为这不会造成混淆,对吧?)。您可以通过sys.asymmetric_keys 获取非对称密钥/强名称密钥的公钥,但不幸的是,不能用于证书。

  2. sys.certificatescert_serial_number 字段中找到的序列号正是:序列号,而不是公钥。它与在命令提示符下执行certutil -dump certificate.cer 时返回的“序列号”匹配。

  3. sys.certificatesthumbprint 字段中找到的指纹是公钥(有时称为“证书”)的 SHA1 哈希,而不是公钥。它与在命令提示符下执行 certutil -dump certificate.cer 时返回的“Cert Hash(sha1)”匹配。

因此,如果您需要在 SQL Server 中分析程序集,您可能需要使用 .NET 方法来执行此操作,尽管我不知道该库是否在“支持”列表中,如果没有,那么它需要添加库并将您的程序集设置为UNSAFE

否则,也许您可​​以有几个选项来检查什么:公钥(如果可用),否则指纹和/或序列号。

或者,由于您拥有公钥,您可以通过SELECT CERTENCODED(CERT_ID(N'certificate_name')) 获得完整的证书。这将为您提供证书的完整字节系列,其中包括公钥。您可以将公钥作为完整证书的字符串形式的子字符串进行搜索。

或者,按照同样的思路,您可以扫描 sys.assembly_files 的 [content] 字段以查找公钥字节的存在。这比扫描证书更直接,但它仅允许特定系列的字节在程序集中的某个位置进行验证,并不意味着程序集以任何方式使用这些字节作为公钥进行签名。

因此,也许更有保证的方法是从程序集创建证书(即@9​​87654352@),使用CERTENCODED() 获取完整证书,然后扫描公钥子字符串。通过静态类构造函数每次加载类时执行一次。当然,这意味着您需要与数据库建立常规/外部连接,这意味着程序集至少需要为EXTERNAL_ACCESS。并且您需要确保该过程具有创建证书的权限。并且,您应该将 CREATE CERTIFICATE 包装在 TRY...CATCH 构造中,因为它可能已经存在(在第一次之后,它会存在或者您每次都需要删除证书,但这可能只是初始验证过程需要更长的时间,所以我想你可以把它留在数据库中)。

不确定完整的汇编字节是否可以像版本和其他一些东西一样通过反射获得。可能值得研究。

另外,请考虑支持我刚刚提交的将public_key 字段添加到sys.certificates 的建议:

Expose public_key of Certificate in sys.certificates, just like sys.asymmetric_keys is doing

【讨论】:

  • 所罗门,感谢您的投票和回复。我更新了我的 OP 以进一步解释我为什么要寻找证书的公钥。不幸的是,我检查了 sys.certificates 表,但 cert_serial_number 和指纹列都与证书的公钥不匹配。如果我们不检查 pk,我们的客户可以使用他们自己生成的测试证书来签署它,我们不会知道该证书不是我们允许的证书。我希望我的澄清有所帮助。谢谢!
  • @VG1 我想我明白了。我已经更新了我的答案,接近底部。
  • 谢谢所罗门!这是非常彻底和有帮助的。我同意在静态 ctor 中使用 CERTENCODED() 听起来是最好的选择。我也投票赞成你的建议。再次感谢您!
  • @VG1 欢迎您。我刚刚更新以添加有关该建议的一些说明。感谢您对该建议的投票:-)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 2012-04-16
  • 2013-02-15
  • 2023-02-20
  • 1970-01-01
  • 1970-01-01
  • 2012-02-07
相关资源
最近更新 更多