目录
  • 软件版本
  • Go 生成和读取 SQLCipher 数据库
    • 生成数据库
    • 判断数据库是否加密
    • 读取数据库
  • Gorm 连接 SQLCipher 数据库
    • 可视化工具读取 SQLCipher 加密数据库(1)
      • 踩坑 & 分析
      • sqlcipher 密码错误
      • db browser 密码错误
      • 跑单测
      • 查源码
      • 找 ISSUE
      • 回看代码
      • 改参数
    • 总结

      本文档主要描述通过 https://github.com/mutecomm/go-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 的数据库

      import _ "github.com/mutecomm/go-sqlcipher"
      func NewSQLCipherDB() {
          var (
      		db      *sql.DB
      		testDir = "go-sqlcipher_test"
      		tables  = `CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT);`
      		data    = `INSERT INTO test (data) VALUES ('Hello, World!');`
      	)
      
      	// create DB
      	key := "passphrase"
      	tmpdir, err := os.MkdirTemp("", testDir)
      	if err != nil {
      		panic(err)
      	}
      	dbname := filepath.Join(tmpdir, "encrypt-data.db")
      	dbnameWithDSN := dbname + fmt.Sprintf("?_pragma_key=%s", key)
      
      	if db, err = sql.Open("sqlite3", dbnameWithDSN); err != nil {
      		panic(err)
      	}
          
          defer db.Close()
      
      	if _, err = db.Exec(tables); err != nil {
      		panic(err)
      	}
      
      	if _, err = db.Exec(data); err != nil {
      		panic(err)
      	}
          
      	return
      }
      

      判断数据库是否加密

      import sqlite3 "github.com/mutecomm/go-sqlcipher"
      
      func IsSQLCipherEncrypted(dbName string) {
          // make sure DB is encrypted
      	encrypted, err := sqlite3.IsEncrypted(dbName)
      	if err != nil {
      		panic(err)
      	}
      	if !encrypted {
      		panic(errors.New("go-sqlcipher: DB not encrypted"))
      	}
          
          fmt.Println("encrypted")
      } 
      

      读取数据库

      import _ "github.com/mutecomm/go-sqlcipher"
      func QuerySQLCipherDB(dbPath,key string) {
          var (
      		db  *sql.DB
              err error
      	)
      
      	dbnameWithDSN := dbPath + fmt.Sprintf("?_pragma_key=%s", key)
      
      	// open DB for testing
      	db, err = sql.Open("sqlite3", dbnameWithDSN)
      	if err != nil {
      		panic(err)
      	}
      	_, err = db.Exec("SELECT count(*) FROM test;")
      	if err != nil {
      		panic(err)
      	}
          
      	return
      }
      

      如果密码错误或者是数据库错误,Line 15 会报 err

      Gorm 连接 SQLCipher 数据库

      用原生方式读取肯定不方便,所以还是找了一下如何用 gorm 来连接并读取。其实这个 go-sqlcipher 就是一个驱动,所以跟 gorm 读取 mysql 数据库是差不多的。就是要注意把 “github.com/mutecomm/go-sqlcipher” import 进去。

      import 	_ "github.com/mutecomm/go-sqlcipher"
      
      var (
      	db *gorm.DB
      )
      
      func Init(dbPath string) (err error) {
          key := "passphrase"
      	dbPath = fmt.Sprintf(dbPath+"?_pragma_key=%s", key)
      
      	db, err = gorm.Open("sqlite3", dbPath)
      	if err != nil {
      		return err
      	}
      
      	// logger Open
      	db.LogMode(true)
      	// Set Idle
      	db.DB().SetMaxIdleConns(10)
          
          return nil
      }
      

      可视化工具读取 SQLCipher 加密数据库(1)

      本篇下面的描述内容主要是,因为创建加密数据库参数出入,而要去修改可视化工具的一些参数,具体见下文。

      踩坑 & 分析

      上述的方式都是基础的,也正常是应该这么创建以及读取的,但是我接手到的代码是长下面这样子的。

      key := "passphrase"
      dbPath = fmt.Sprintf(dbPath+"?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
      
      db, err = gorm.Open("sqlite3", dbPath)
      if err != nil {
      	return err
      }
      

      奇奇怪怪的事情就开始发生了,用最基础的 sqlcipher 指令读取都会说密码错误。

      sqlcipher 密码错误

      sqlite> PRAGMA key = x'passphrase'; # 格式错误
      sqlite> PRAGMA key = '70617373706872617365'; # passphrase hex 之后,密码错误
      sqlite> PRAGMA key = '78277061737370687261736527'; # x'passphrase' hex 之后,密码错误
      sqlite> PRAGMA key = "x'passphrase'";
      

      先透露,第四个才是对的

      按正常情况来看,应该这样就可以正常读取了,还是报密码错误。

      db browser 密码错误

      之前没碰过这个,觉得 sqlcipher 是不是我不会,所以找了这个工具。

      不过按照流程输入密码,也还是进不去,也选择了 SQLCipher 3 也不行。

      这边 algorithm 跟源码 README 里面的 AES 256 对不上,我以为是 db browser 不支持我这种加密格式

      使用Go和Gorm实现读取SQLCipher加密数据库

      跑单测

      按照别人给的不行,就从头开始,自己创建,自己测试。

      • 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:

      key := "2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99"
      dbname := fmt.Sprintf("db?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
      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).

      key := url.QueryEscape("secret")
      dbname := fmt.Sprintf("db?_pragma_key=%s&_pragma_cipher_page_size=4096", key)
      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 的,里面有这么一段:

      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 加密的时候,会受影响。所以,在可视化页面连接的时候要指定。

      回看代码

      dbname := fmt.Sprintf("db?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
      

      这个库只支持 SQLCipher v3,v4 的默认值才是 4096,v3的默认值是1024(虽然我不知道这个什么用)

      各个可视化工具默认都是 1024,跟代码里面 4096 对不上,改参数

      改参数

      sqlcipher

      sqlite> PRAGMA key = "x'passphrase'";
      sqlite> PRAGMA cipher_page_size=4096;
      sqlite> SELECT * from test;
      1|Hello, World!
      sqlite> .exit
      

      db browser

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

      使用Go和Gorm实现读取SQLCipher加密数据库

      DBeaver

      修改 legacy_page_size 为 4096 就可以了

      使用Go和Gorm实现读取SQLCipher加密数据库

      总结

      其实这个懂的人,估计看到这个 page size 不同就知道要去配置了。对于不懂的人,看密码,又登入不进去就会很烦,就会乱。

      后面分析的方法就是从单测入手,用最简单的方式先跑通一个。比如,密码先不要设置那么复杂的,就设置 123456,然后测试。通过再往下一步,往自己收到的问题去靠。

      以上就是使用Go和Gorm实现读取SQLCipher加密数据库的详细内容,更多关于Go Gorm读取SQLCipher的资料请关注其它相关文章!

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。