使用EncryptSensitiveWithPassword和包级连接管理器以编程方式构建SSIS包

时间:2014-06-18 21:33:59

标签: ssis msbuild sql-server-2012

我尝试使用已保存的项目级连接管理器密码自动部署SSIS项目。这些包连接到仅提供SQL登录的第三方数据库,因此在这种情况下,集成安全性不是一个选项。我使用this作为参考,因为它似乎是针对相同的用例。我已经编译并运行了MSBuild任务,以及一个MSBuild项目文件。我可以通过命令行构建和部署项目而不会出现问题。

问题

当我部署通过Visual Studio生成的ispac文件时,我可以毫无问题地运行包。但是,当我部署由MSBuild任务生成的ispac并尝试运行时,我收到以下验证错误:

  
    

错误:SSIS错误代码DTS_E_CANNOTACQUIRECONNECTIONFROMCONNECTIONMANAGER。 AcquireConnection方法调用连接管理器"<>"失败,错误代码为0xC0202009。在此之前可能会发布错误消息,其中包含有关AcquireConnection方法调用失败原因的更多信息。

         

错误:SSIS错误代码DTS_E_OLEDBERROR。发生OLE DB错误。错误代码:0x80040E4D。     OLE DB记录可用。来源:" Microsoft SQL Server Native Client 11.0" Hresult:0x80040E4D说明:"用户登录失败'<>'。"

  

如果我将其中一个软件包更改为使用软件包级连接管理器到同一个服务器,那么密码将被转移,我可以运行该特定软件包。

连接管理器存储在.conmgr个文件中,DTS:Password节点标记为敏感,并包含加密密码。据我所知,问题出现在构建任务代码中。 code for the build task执行以下操作:

  • 反序列化.dtproj文件以获取项目中的连接管理器文件路径列表

  • 创建一个新的Microsoft.SqlServer.Dts.Runtime.Project(据我所知,它与生成的.ispac而不是.dtproj&#34;项目&#34;)< / p>

  • 对于每个连接管理器文件,请调用project.ConnectionManagerItems.Add(<connection manager name>, <.conmgr file name>)

  • 通过ConnectionManagerItem

  • 加载返回的cm.Load(NULL, <stream of the conmgr file>)

我假设在最后两个步骤的某个地方,密码没有被反序列化,并且新的Project在没有密码的情况下添加连接管理器。我一直在搜索Integration Services开发人员指南,但似乎更专注于从头开始以编程方式创建连接管理器,而不是加载具有需要解密的数据的现有连接管理器。

更新 根据@ billinkc的建议,我解压缩生成的ispac文件以比较连接管理器的保存方式。 MSBuild生成的一个与Visual Studio生成的一个除外,它缺少DTS:Password元素。这支持了我的理论,即将连接管理器添加到Microsoft.SqlServer.Dts.Runtime.Project的代码要么不反序列化,要么不序列化密码。关于SSIS的可编程性,我已经超出了我的深度,所以我不知道从哪里开始。相关文档**并没有提供太多见解。

** ProjectConnectionManagerItemWorking with connection managers programmaticallyAdding connection managers programmatically

1 个答案:

答案 0 :(得分:1)

我只是不能这样做,并且以简单(聪明)的方式做到这一点,所以我的加密知识(几乎没有)和MSDN中的一行**,我开始尝试和解密加密的元素。经过一些反复试验后,我终于明白了:

private string DecryptConnectionManagerPassword(string connectionManagerPath)
{
    // Load the xml and get the encrypted DTS:Password node
    XmlDocument cmDocument = new XmlDocument();
    cmDocument.Load(connectionManagerPath);

    XmlElement passwordElement = cmDocument.GetElementsByTagName("DTS:Password")[0] as XmlElement;

    // Create byte arrays with the data we'll need
    byte[] salt = Convert.FromBase64String(passwordElement.GetAttribute("p4:Salt"));
    byte[] iv = Convert.FromBase64String(passwordElement.GetAttribute("p4:IV"));
    byte[] cipherText = Convert.FromBase64String(passwordElement.InnerText);
    byte[] password = System.Text.Encoding.ASCII.GetBytes(ProjectPassword);

    // Create the cipher key
    PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
    byte[] key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, iv);

    // Decrypt the cipher text
    var csp = new TripleDESCryptoServiceProvider();
    csp.Mode = CipherMode.CBC;
    csp.IV = iv;
    csp.Key = key;

    var plainTextBytes = new byte[512];
    var decryptor = csp.CreateDecryptor();
    decryptor.TransformBlock(cipherText, 0, cipherText.Length, plainTextBytes, 0);

    // Get convert to a string and extract password
    var plainText = new string(System.Text.Encoding.ASCII.GetChars(plainTextBytes));

    // the decrypted text doesn't come out as valid xml
    // so I use a regex to extract the password. Obviously dangerous.
    var regEx = new Regex(">(.*)<");
    var matches = regEx.Match(plainText);

    return matches.Captures[1].Value;
}

从那里开始,只需在连接管理器上设置Password属性,生成的ispac包含带有加密密码元素的连接管理器。

** Also, for the protection levels that use a password, Integration Services uses the Triple DES cipher algorithm with a key length of 192 bits, available in the .NET Framework Class Library (FCL).