如何在SQL

时间:2018-04-06 18:59:01

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

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

澄清需求:

我们将以二进制格式生成数据的软件销售到我们的数据库模式中,因此客户无法使用T-SQL直接查询数据。这既是为了性能,也是为了输出我们输出数据的数据分析算法。目前,客户必须使用我们的软件来解压缩"这个blob数据来自" BusnLogicCore.dll"。只有在调用程序集已经由我们事先批准的证书签名时,才会执行此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和指纹列都没有匹配证书的公钥。

1 个答案:

答案 0 :(得分:1)

假设您首先将证书加载到master数据库中,请从该证书创建登录,并授予该登录UNSAFE ASSEMBLY权限,以便您可以使用{{1}创建程序集然后我不知道为什么你会想要/需要做你要求的事情,因为那个问题已经在那时得到了回答。

意思是,如果您能够将程序集创建为PERMISSION_SET = UNASFE(假设数据库的UNSAFE设置为TRUSTWORTHY,那么使用证书 - 这是更好的方法无论如何),那么只有在大会签署了首次加载到OFF等的证书(或强名称密钥/非对称密钥)时才会发生这种情况(如本回答开头所述)。如果大会未签名或由其他证书(或强名称密钥)签名,则在尝试将其创建为masterUNSAFE时(或甚至{{1})会出错从SQL Server 2017开始。)

但是,EXTERNAL_ACCESS系统目录视图中有一些信息可用于匹配数据库之间的证书。如果您在SAFE中创建证书之前已加载程序集(通过将数据库设置为sys.certificates - 错误的想法,或加载为master以更改为{在TRUSTWORTHY ON中设置证书后,{1}}在SQL Server 2017中开始工作后,您可以使用SAFE从程序集中提取证书,然后抓住来自UNSAFE的该证书的masterCREATE CERTIFICATE [bob] FROM ASSEMBLY [YourAssembly];

并澄清一点:

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

  2. thumbprint的{​​{1}}字段中找到的序列号正好是:序列号,而不是公钥。它匹配"序列号"在命令提示符下执行sys.certificates时返回。

  3. sys.asymmetric_keys的{​​{1}}字段中找到的指纹是公钥的SHA1哈希值(有时称为"证书"),而不是公钥。它匹配" Cert Hash(sha1)"在命令提示符下执行cert_serial_number时返回。

  4. 因此,如果您需要在SQL Server中分析程序集,您可能需要使用.NET方法来执行此操作,但我不知道该库是否在"支持&#34 ;列表,如果没有,则需要添加库并将程序集设置为sys.certificates

    否则,您可以选择一些选项来检查:公钥(如果有),否则指纹和/或序列号。

    或者,由于您拥有公钥,您可以通过certutil -dump certificate.cer获得完整的证书。这将为您提供证书的完整字节序列,其中包括公钥。您可以将公钥作为完整证书的字符串形式的子字符串进行搜索。

    或者,在这些相同的行中,您可以扫描thumbprint的[content]字段以查找是否存在公钥字节。这比扫描证书更直接,但它只允许在程序集中的某个特定字节序列进行验证,并不意味着程序集以任何方式使用这些字节作为公钥进行签名。

    因此,更可靠的方法是从程序集创建证书(即sys.certificates),使用certutil -dump certificate.cer获取完整证书,然后扫描公钥子字符串。通过静态类构造函数在每次加载类时执行一次。当然,这意味着您需要与DB进行常规/外部连接,这意味着程序集至少需要UNSAFE。您需要确保该进程具有创建证书的权限。并且,您应该将SELECT CERTENCODED(CERT_ID(N'certificate_name'))包装在sys.assembly_files构造中,因为它可能已经存在(在第一次之后,它将存在或者您将需要每次都丢弃证书,但这可能只是初始验证过程需要更长的时间,所以我猜你可以把它留在数据库中。

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

    此外,请考虑支持我刚刚提交的建议,将CREATE CERTIFICATE [tmp] FROM ASSEMBLY...字段添加到CERTENCODED()

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