【Go】用 Go 原生以及 Gorm 读取 SQLCipher 加密数据库

  金牌会员 | 2024-7-16 14:05:56 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 993|帖子 993|积分 2979

本文档重要形貌通过 https://github.com/mutecomm/go-sqlcipher 生成和读取 SQLCipher 加密数据库以及其中踩的一些坑


  • 用 go 去生成读取 SQLCipher 数据库
  • 用 gorm 去读取 SQLCipher 数据库
  • 在生成后分别用 DBeaver、db browser 和 sqlcipher 读取 SQLCipher 数据库,基础操作见 用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库
  • 由于创建方式的参数导致读取时间的坑,以及我的分析思路

软件版本

go: v1.22.2
sqlcipher cli(ubuntun):3.15.2
sqlcipher(used for encrypt):v3
go-sqlcipher-package:https://github.com/mutecomm/go-sqlcipher v0.0.0-20190227152316-55dbde17881f
Go 生成和读取 SQLCipher 数据库

生成数据库

创建一个名为 encrypt-data.db,表为 test 的数据库
  1. import _ "github.com/mutecomm/go-sqlcipher"
  2. func NewSQLCipherDB() {
  3.     var (
  4.                 db      *sql.DB
  5.                 testDir = "go-sqlcipher_test"
  6.                 tables  = `CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT);`
  7.                 data    = `INSERT INTO test (data) VALUES ('Hello, World!');`
  8.         )
  9.         // create DB
  10.         key := "passphrase"
  11.         tmpdir, err := os.MkdirTemp("", testDir)
  12.         if err != nil {
  13.                 panic(err)
  14.         }
  15.         dbname := filepath.Join(tmpdir, "encrypt-data.db")
  16.         dbnameWithDSN := dbname + fmt.Sprintf("?_pragma_key=%s", key)
  17.         if db, err = sql.Open("sqlite3", dbnameWithDSN); err != nil {
  18.                 panic(err)
  19.         }
  20.    
  21.     defer db.Close()
  22.         if _, err = db.Exec(tables); err != nil {
  23.                 panic(err)
  24.         }
  25.         if _, err = db.Exec(data); err != nil {
  26.                 panic(err)
  27.         }
  28.    
  29.         return
  30. }
复制代码
判断数据库是否加密

  1. import sqlite3 "github.com/mutecomm/go-sqlcipher"
  2. func IsSQLCipherEncrypted(dbName string) {
  3.     // make sure DB is encrypted
  4.         encrypted, err := sqlite3.IsEncrypted(dbName)
  5.         if err != nil {
  6.                 panic(err)
  7.         }
  8.         if !encrypted {
  9.                 panic(errors.New("go-sqlcipher: DB not encrypted"))
  10.         }
  11.    
  12.     fmt.Println("encrypted")
  13. }
复制代码
读取数据库

  1. import _ "github.com/mutecomm/go-sqlcipher"
  2. func QuerySQLCipherDB(dbPath,key string) {
  3.     var (
  4.                 db  *sql.DB
  5.         err error
  6.         )
  7.         dbnameWithDSN := dbPath + fmt.Sprintf("?_pragma_key=%s", key)
  8.         // open DB for testing
  9.         db, err = sql.Open("sqlite3", dbnameWithDSN)
  10.         if err != nil {
  11.                 panic(err)
  12.         }
  13.         _, err = db.Exec("SELECT count(*) FROM test;")
  14.         if err != nil {
  15.                 panic(err)
  16.         }
  17.    
  18.         return
  19. }
复制代码
假如密码错误大概是数据库错误,Line 15 会报 err
Gorm 毗连 SQLCipher 数据库

用原生方式读取肯定不方便,所以还是找了一下怎样用 gorm 来毗连并读取。实在这个 go-sqlcipher 就是一个驱动,所以跟 gorm 读取 mysql 数据库是差不多的。就是要留意把 “github.com/mutecomm/go-sqlcipher” import 进去。
  1. import         _ "github.com/mutecomm/go-sqlcipher"
  2. var (
  3.         db *gorm.DB
  4. )
  5. func Init(dbPath string) (err error) {
  6.     key := "passphrase"
  7.         dbPath = fmt.Sprintf(dbPath+"?_pragma_key=%s", key)
  8.         db, err = gorm.Open("sqlite3", dbPath)
  9.         if err != nil {
  10.                 return err
  11.         }
  12.         // logger Open
  13.         db.LogMode(true)
  14.         // Set Idle
  15.         db.DB().SetMaxIdleConns(10)
  16.    
  17.     return nil
  18. }
复制代码
可视化工具读取 SQLCipher 加密数据库(1)

可视化部分的可以通过这一篇文章细看 用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库。
本篇下面的形貌内容重要是,由于创建加密数据库参数收支,而要去修改可视化工具的一些参数,具体见下文。
踩坑 & 分析

上述的方式都是基础的,也正常是应该这么创建以及读取的,但是我接办到的代码是长下面这样子的。
  1. key := "passphrase"
  2. dbPath = fmt.Sprintf(dbPath+"?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
  3. db, err = gorm.Open("sqlite3", dbPath)
  4. if err != nil {
  5.         return err
  6. }
复制代码
奇奇怪怪的事情就开始发生了,用最基础的 sqlcipher 指令读取都会说密码错误。
sqlcipher 密码错误

  1. sqlite> PRAGMA key = x'passphrase'; # 格式错误
  2. sqlite> PRAGMA key = '70617373706872617365'; # passphrase hex 之后,密码错误
  3. sqlite> PRAGMA key = '78277061737370687261736527'; # x'passphrase' hex 之后,密码错误
  4. sqlite> PRAGMA key = "x'passphrase'";
复制代码
  先透露,第四个才是对的
  按正常环境来看,应该这样就可以正常读取了,还是报密码错误。
db browser 密码错误

之前没碰过这个,觉得 sqlcipher 是不是我不会,所以找了这个工具。
不过按照流程输入密码,也还是进不去,也选择了 SQLCipher 3 也不行。
   这边 algorithm 跟源码 README 里面的 AES 256 对不上,我以为是 db browser 不支持我这种加密格式
  

跑单测

按照别人给的不行,就重新开始,本身创建,本身测试。

  • go 代码创建加密数据库,sqlcipher 指令读取,这个是可以的。这一个测试我用的是最上面生成数据库的代码。
  • 由于我收到的代码里面有带,_pragma_cipher_page_size=4096。然后用这个方式创建的就是不行,以为我输入的 key 是不是在第三方包内有做什么动作,所以去分析了源码库。
跑完单元测试,说明密码的输入没错,就是这个 page size 的题目。
   此时我还没意识到是 page size 默认配置的题目
  查源码

以下源码的 README,看得我迷糊,以为还要再 hex,多测了不同的加密方式也不行。
To create and open encrypted database files use the following DSN parameters:
  1. key := "2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99"
  2. dbname := fmt.Sprintf("db?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
  3. db, _ := sql.Open("sqlite3", dbname)
复制代码
_pragma_key is the hex encoded 32 byte key (must be 64 characters long). _pragma_cipher_page_size is the page size of the encrypted database (set if you want a different value than the default size).
  1. key := url.QueryEscape("secret")
  2. dbname := fmt.Sprintf("db?_pragma_key=%s&_pragma_cipher_page_size=4096", key)
  3. db, _ := sql.Open("sqlite3", dbname)
复制代码
This uses a passphrase directly as _pragma_key with the key derivation function in SQLCipher. Do not forget the url.QueryEscape() call in your code!
找 ISSUE

https://github.com/mutecomm/go-sqlcipher/issues/15
这个 issue 是对 SQLCipher V4 的,里面有这么一段:
  1. The parameters seem to be the same. I'm wondering if you have to switch the order of key and cipher_page_size in the sqlcipher call. Also the documentation https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_default_page_size seems to indicate that you have to use cipher_default_page_size in the command line call. But it shouldn't make any difference anyway since 4096 is the default value in SQLCipher 4.
复制代码
说明 SQLCipher 的 cipher_page_size 有默认值,并且在调用 sqlcipher 加密的时间,会受影响。所以,在可视化页面毗连的时间要指定。
回看代码

  1. dbname := fmt.Sprintf("db?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
复制代码
这个库只支持 SQLCipher v3,v4 的默认值才是 4096,v3的默认值是1024(固然我不知道这个什么用)
各个可视化工具默认都是 1024,跟代码里面 4096 对不上,改参数
改参数

sqlcipher
  1. sqlite> PRAGMA key = "x'passphrase'";
  2. sqlite> PRAGMA cipher_page_size=4096;
  3. sqlite> SELECT * from test;
  4. 1|Hello, World!
  5. sqlite> .exit
复制代码
db browser

先选 SQLCipher 3, 然后选择 Custom,再点击 Page size 的下拉选择 4096,就可以了

DBeaver
DBeaver driver 的驱动配置见另一个文档 用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库
修改 legacy_page_size 为 4096 就可以了

总结

实在这个懂的人,估计看到这个 page size 不同就知道要去配置了。对于不懂的人,看密码,又登入不进去就会很烦,就会乱。
后面分析的方法就是从单测入手,用最简单的方式先跑通一个。比如,密码先不要设置那么复杂的,就设置 123456,然后测试。通过再往下一步,往本身收到的题目去靠。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表