SQLite 加密扩展

通过 NuGet 使用 System.Data.SQLite 和 SQLite 加密扩展
登录

此项目使用了由Mistachkin Systems提供的 Harpy。
Harpy: Secure Software Provisioning

想要一种简单、安全的方式来集中管理数据库密码?
Enterprise Key Management plugin for System.Data.SQLite

关于 System.Data.SQLite 和 SQLite 加密扩展

SQLite 加密扩展允许 SQLite 读取和写入加密的数据库文件。所有数据库内容,包括元数据,都经过加密,因此对于外部观察者来说,数据库看起来就像白噪声。

如何使用 System.Data.SQLite 和 SQLite 加密扩展

为了成功使用发布的 NuGet 包中的 SQLite 加密扩展和 System.Data.SQLite

  1. 添加对System.Data.SQLite.Core NuGet 包的引用添加依赖于它的其中一个(父)包的引用,例如 System.Data.SQLite
确保 System.Data.SQLite 的核心托管程序集(即文件“System.Data.SQLite.dll”)不在全局程序集缓存中。
各种第三方软件可能会尝试将其安装到那里;但是,这种部署方式不受官方支持。
  1. 添加对SQLite.Encryption.Extension NuGet 包的引用。
仔细检查选定的“SQLite.Encryption.Extension”NuGet 包版本与选定的“System.Data.SQLite.Core”NuGet 包版本是否一致。
版本必须完全匹配,例如“System.Data.SQLite.Core 1.2.3.4”和“SQLite.Encryption.Extension 1.2.3.4”,或者至少前三个组件匹配,例如“System.Data.SQLite.Core 1.2.3.4”和“SQLite.Encryption.Extension 1.2.3.5”。
  1. 在为 .NET Core 构建时,在项目文件中设置CopyLocalLockFileAssemblies MSBuild 属性。
显然,有几个 Linux 发行版禁用了使用 SHA1 的 RSA 签名,这样做会导致强名称签名检查失败。更多详细信息请参阅 GitHub 问题OpenSslCryptographicException: error:03000098:digital envelope routines::invalid digest on CentOS Stream 9
以下解决方法似乎可以解决此问题
      /*
       * NOTE: Forcibly enable use of SHA1 with RSA signatures;
       *       otherwise, things will not work correctly.
       */
      System.Environment.SetEnvironmentVariable(
          "OPENSSL_ENABLE_SHA1_SIGNATURES", "1");
  1. 确保在构建过程中将您的“SDS-SEE.exml”许可证证书文件复制到应用程序目录中,并将其包含在应用程序部署文件中。
或者,从1.0.117.0 版开始,在为部署目的构建应用程序时,可以使用类似于以下内容的 MSBuild 项目文件中的内容将您的“SDS-SEE.exml”许可证证书文件嵌入到主应用程序程序集中
需要互联网访问才能进行许可证证书验证。任何持续时间不无限的证书可能需要访问 NTP 和/或 HTTPS。证书也可能需要通过 HTTPS 进行在线吊销检查。需要离线许可证证书验证的组织需要额外的合同条款。
      <ItemGroup>
        <EmbeddedResource Include="Project\Relative\Path\To\SDS-SEE.exml">
          <LogicalName>SDS-SEE.exml</LogicalName>
        </EmbeddedResource>
      </ItemGroup>
当按上述方式嵌入许可证证书文件时,必须执行以下代码片段
      /*
       * NOTE: Use only the file name here, which indicates
       *       that an embedded assembly resource is being
       *       used.
       */
      System.Environment.SetEnvironmentVariable(
          "Override_SEE_Certificate", "SDS-SEE.exml");
通常,以上代码片段将位于下一项(#5)中描述的代码片段旁边。
  1. 在访问加密数据库(即使用其中一个“Password”连接字符串属性的数据库)之前,必须执行以下代码片段
      System.AppDomain.CurrentDomain.SetData(System.String.Format(
          "Id_from_License_Certificate_{0}",
          System.Diagnostics.Process.GetCurrentProcess().Id),
          "EntityName_from_License_Certificate");

      System.Data.SQLite.SQLiteCommand.Execute(
          "PRAGMA activate_extensions='see-7bb07b8d471d642e';",
          System.Data.SQLite.SQLiteExecuteType.NonQuery,
          "Data Source=:memory:;");

      System.Data.SQLite.SQLiteCommand.Execute(
          "SELECT COUNT(*) FROM sqlite_schema;",
          System.Data.SQLite.SQLiteExecuteType.Scalar,
          "Data Source=:memory:;Password=1234;");
在上面的代码中,字符串“Id_from_License_Certificate”和“EntityName_from_License_Certificate”必须分别与许可证证书文件中的“Id”和“EntityName”值的文本匹配,并将在您的许可证证书文件中提供。
应注意保留“Id_from_License_Certificate”占位符和格式字符串其余部分之间的尾随字面下划线。
以上“System.Data.SQLite.SQLiteCommand.Execute”方法调用必须逐字使用。
在使用非默认应用程序域 (AppDomain) 的情况下,例如 Microsoft Office、其他第三方应用程序、测试框架、Web 服务等,也可能需要一些类似以下的代码
      /*
       * NOTE: The .NET Core (and later) runtimes support only
       *       one application domain.  On those runtimes, the
       *       following environment variable has no effect.
       */
      System.Environment.SetEnvironmentVariable(
          "LicenseOtherAppDomain", "1");

      /*
       * NOTE: Depending on exactly how the application domain
       *       has been configured, the following environment
       *       variable may not be necessary; however, as long
       *       as it is set to the directory containing the
       *       correct "System.Data.SQLite.SEE.License.dll"
       *       file, setting it should be harmless.
       */
      System.Environment.SetEnvironmentVariable(
          "LicenseAssemblyPath",
          System.AppDomain.CurrentDomain.BaseDirectory);
要确定代码是否在非默认应用程序域中执行,请检查System.AppDomain.CurrentDomain.IsDefaultAppDomain属性。如果结果值不是true,则正在使用的应用程序域不是默认应用程序域。
  1. 然后,使用连接字符串属性“Password”、“HexPassword”或“TextPassword”为数据库连接启用加密。
有关更多详细信息,请参阅System.Data.SQLite 文档
根据README中“8.1 使用密钥前缀选择加密算法”部分,可以通过在连接字符串属性值上使用简短的前缀来选择要使用的特定加密算法,例如,要使用 AES-256 加密算法,请在所需的密码前添加字符串“aes256:”。
这是一个简短的示例
      SQLiteConnection connection = new SQLiteConnection();

      connection.ConnectionString =
          "Data Source=test.db;Password=aes256:secret;";

      connection.Open();
这是另一个使用连接池的示例,该示例有助于在重复打开给定数据库时减少连接设置开销
      /*
       * NOTE: Setting the following environment variable is optional;
       *       when set, it prevents the use of weak references in the
       *       connection pool, which then prevents pooled connections
       *       from being cleaned out too aggressively.  The handling
       *       enabled by setting this environment variable is supported
       *       starting with the 1.0.116.0 release of System.Data.SQLite.
       */
      System.Environment.SetEnvironmentVariable(
          "SQLite_StrongConnectionPool", "1");

      SQLiteConnection connection = new SQLiteConnection();

      connection.ConnectionString =
          "Data Source=test.db;Password=aes256:secret;Pooling=true;";

      connection.Open();
  1. 部署应用程序时,以下文件必须存在于应用程序二进制目录中
      <bin>\System.Data.SQLite.dll
      <bin>\x86\SQLite.Interop.dll
      <bin>\x64\SQLite.Interop.dll
      <bin>\System.Data.SQLite.SEE.License.dll
      <bin>\Eagle.dll
      <bin>\Harpy.dll
      <bin>\SDS-SEE.exml (not when using embedded resource)
在 Visual Studio 中使用 NuGet 包时,这些文件应通过项目构建过程自动复制到应用程序二进制目录中。
在构建过程中复制到应用程序二进制目录中的某些文件未使用 System.Data.SQLite 的 SQLite 加密扩展 NuGet 包(即通过传递性 NuGet 包依赖项包含),它们包括
      <bin>\lib\Badge1.0\Badge.dll
      <bin>\lib\Badge1.0\pkgIndex.eagle (release 1.0.118.0 or before)
      <bin>\lib\Badge1.0\pkgIndex.eagle.harpy (release 1.0.118.0 or before)
      <bin>\lib\Badge1.0\pkgIndex_8bf43b4749e46a0b.eagle (release 1.0.119.0 or later)
      <bin>\lib\Badge1.0\pkgIndex_8bf43b4749e46a0b.eagle.harpy (release 1.0.119.0 or later)
      <bin>\lib\Harpy1.0\keyRing.General.demo.eagle
      <bin>\lib\Harpy1.0\keyRing.General.demo.eagle.harpy
      <bin>\lib\Harpy1.0\keyRing.zero.eagle
      <bin>\lib\Harpy1.0\keyRing.zero.eagle.harpy
      <bin>\lib\Harpy1.0\pkgIndex.eagle (release 1.0.118.0 or before)
      <bin>\lib\Harpy1.0\pkgIndex.eagle.harpy (release 1.0.118.0 or before)
      <bin>\lib\Harpy1.0\pkgIndex_8bf43b4749e46a0b.eagle (release 1.0.119.0 or later)
      <bin>\lib\Harpy1.0\pkgIndex_8bf43b4749e46a0b.eagle.harpy (release 1.0.119.0 or later)
      <bin>\lib\Harpy1.0\test.eagle
      <bin>\lib\Harpy1.0\test.eagle.harpy
      <bin>\lib\Harpy1.0\Certificates\trial-certificate.xml
      <bin>\lib\Harpy1.0\Configurations\Harpy.v1.eagle (release 1.0.116.0 or before)
      <bin>\lib\Harpy1.0\Configurations\Harpy.v1.eagle.b64sig (release 1.0.116.0 or before)
      <bin>\lib\Harpy1.0\Configurations\Harpy.v1.eeagle (release 1.0.117.0 or 1.0.118.0)
      <bin>\lib\Harpy1.0\Configurations\Harpy.v1.eeagle.b64sig (release 1.0.117.0 or 1.0.118.0)
      <bin>\lib\Harpy1.0\Configurations\Harpy.Debugger.v1.eeagle (release 1.0.119.0 or later)
      <bin>\lib\Harpy1.0\Configurations\Harpy.Debugger.v1.eeagle.b64sig (release 1.0.119.0 or later)
1.0.117.0 版开始,在为部署目的构建应用程序时,可以通过在引用任何“Harpy.*.targets”文件的“<Import>”元素之前将以下代码片段复制到 MSBuild 项目文件中来排除它们
      <PropertyGroup>
        <CopyBadgeCoreFiles Condition="'$(CopyBadgeCoreFiles)' == ''">false</CopyBadgeCoreFiles>
        <CopyHarpyLibraryFiles Condition="'$(CopyHarpyLibraryFiles)' == ''">false</CopyHarpyLibraryFiles>
      </PropertyGroup>
  1. 调试应用程序时(例如在 Visual Studio 中)如果看到包含“native method forbidden by license”或“managed method forbidden by license”的错误消息,以下其他文件也必须存在于应用程序二进制目录中
      <bin>\Eagle.Eye.dll
      <bin>\Configurations\Harpy.v1.eagle (release 1.0.116.0 or before)
      <bin>\Configurations\Harpy.v1.eagle.b64sig (release 1.0.116.0 or before)
      <bin>\Configurations\Harpy.v1.eeagle (release 1.0.117.0 or 1.0.118.0)
      <bin>\Configurations\Harpy.v1.eeagle.b64sig (release 1.0.117.0 or 1.0.118.0)
      <bin>\Configurations\Harpy.Debugger.v1.eeagle (release 1.0.119.0 or later)
      <bin>\Configurations\Harpy.Debugger.v1.eeagle.b64sig (release 1.0.119.0 or later)
如果正在使用利用“影子复制”的 ASP.NET(或任何其他框架),则也可能需要以下环境变量才能让 Harpy 找到其配置文件
      System.Environment.SetEnvironmentVariable(
          "ConfigurationDirectory", System.IO.Path.Combine(
          System.AppDomain.CurrentDomain.BaseDirectory,
          "lib\\Harpy1.0\\Configurations"));
需要注意的是,即使使用了上述环境变量,“Eagle.Eye.dll”程序集文件也应位于与“Eagle.dll”程序集文件相同的目录中(即应用程序二进制目录)。或者,可以使用“StubPath”覆盖其父目录,例如
      System.Environment.SetEnvironmentVariable(
          "StubPath", System.AppDomain.CurrentDomain.BaseDirectory);
仅当 ASP.NET 或其他内容正在移动文件时(例如,为了支持“影子复制”)才应使用“StubPath”环境变量。
在 Visual Studio 中使用 NuGet 包时,这些文件应通过项目构建过程自动复制到应用程序二进制目录中。
如果看到包含“Eagle._Components.Public.Interpreter.DemandCertificate”的错误消息,请确保应用程序配置文件未禁用发布者证据的生成,即,如果在应用程序配置文件中看到类似以下内容的 XML 代码段,则应将其删除
      <generatePublisherEvidence enabled="false" />
  1. 如果System.Data.SQLite.SQLiteExtra.InnerVerify方法引发异常,则可能需要设置以下环境变量以捕获相关诊断信息
      System.Environment.SetEnvironmentVariable(
          "ForceEnableTrace", "1");

      System.Environment.SetEnvironmentVariable(
          "ForceEnableTraceLogFile", "1");

      System.Environment.SetEnvironmentVariable(
          "TracePriorities", "HasPrioritiesMask");
设置上述环境变量后,应在当前用户的临时目录(即“%TEMP%”中)生成一个日志文件,其名称类似于“HarpyLicensingSdk_<pid>_<aid>.log”,其中<pid>是当前进程的整数标识符,<aid>是当前 AppDomain 的整数标识符。
在某些情况下(例如故障排除部署),使用(以前称为“SysInternals”的)Microsoft DebugView工具捕获诊断输出可能更简单,可以从此处下载
Microsoft DebugView 下载页面
使用 DebugView 工具捕获诊断跟踪输出时,无需设置“ForceEnableTraceLogFile”环境变量。
  1. SQLiteConnection类包含一个DecryptLegacyDatabase方法,可用于解密使用旧版 CryptoAPI 编解码器加密的数据库。
DecryptLegacyDatabase方法是静态的;无需创建SQLiteConnection实例即可调用它。应为每个(旧版加密的)数据库解密调用一次。第一个参数是要解密的(旧版)加密数据库的完全限定名称。第二个参数是与(旧版)加密数据库对应的密码的 UTF-8 编码字节数组。第三个参数几乎总是应为 null,这意味着“自动检测页面大小”;否则,它将是页面大小(以字节为单位),必须是 512 到 65536 之间的 2 的整数幂。第四个参数是要使用的可选进度回调(如果有)。通常,第四个参数将为 null。对该方法的相当典型的调用如下所示
      SQLiteConnection.DecryptLegacyDatabase(
          "C:\\full\\path\\to\\some\\legacy.db",
          System.Text.Encoding.UTF8.GetBytes("the legacy password"),
          null, null);
返回值是已解密数据库的完全限定名称,该名称将存在于原始加密(旧版加密)数据库所在的同一目录中。解密后,可以使用ChangePassword方法或命令行 shell 工具使用 SQLite 加密扩展重新加密数据库。