脚本之家

电脑版
提示:原网页已由神马搜索转码, 内容由www.jb51.net提供.
您的位置:首页脚本专栏Golang→ Go Gorm读取SQLCipher

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

  更新时间:2024年06月14日 10:06:01  作者:非晓为骁 
本文档主要描述通过Go和Gorm实现生成和读取SQLCipher加密数据库以及其中踩的一些坑,文章通过代码示例讲解的非常详细, 对大家的学习或工作有一定的帮助,需要的朋友可以参考下

本文档主要描述通过 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 的数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import_ "github.com/mutecomm/go-sqlcipher"
funcNewSQLCipherDB() {
    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)
    iferr != nil{
        panic(err)
    }
    dbname := filepath.Join(tmpdir, "encrypt-data.db")
    dbnameWithDSN := dbname + fmt.Sprintf("?_pragma_key=%s", key)
 
    ifdb, err = sql.Open("sqlite3", dbnameWithDSN); err != nil{
        panic(err)
    }
     
    deferdb.Close()
 
    if_, err = db.Exec(tables); err != nil{
        panic(err)
    }
 
    if_, err = db.Exec(data); err != nil{
        panic(err)
    }
     
    return
}

判断数据库是否加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
importsqlite3 "github.com/mutecomm/go-sqlcipher"
 
funcIsSQLCipherEncrypted(dbName string) {
    // make sure DB is encrypted
    encrypted, err := sqlite3.IsEncrypted(dbName)
    iferr != nil{
        panic(err)
    }
    if!encrypted {
        panic(errors.New("go-sqlcipher: DB not encrypted"))
    }
     
    fmt.Println("encrypted")
}

读取数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import_ "github.com/mutecomm/go-sqlcipher"
funcQuerySQLCipherDB(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)
    iferr != nil{
        panic(err)
    }
    _, err = db.Exec("SELECT count(*) FROM test;")
    iferr != nil{
        panic(err)
    }
     
    return
}

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

Gorm 连接 SQLCipher 数据库

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import _ "github.com/mutecomm/go-sqlcipher"
 
var(
    db *gorm.DB
)
 
funcInit(dbPath string) (err error) {
    key := "passphrase"
    dbPath = fmt.Sprintf(dbPath+"?_pragma_key=%s", key)
 
    db, err = gorm.Open("sqlite3", dbPath)
    iferr != nil{
        returnerr
    }
 
    // logger Open
    db.LogMode(true)
    // Set Idle
    db.DB().SetMaxIdleConns(10)
     
    returnnil
}

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

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

踩坑& 分析

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

1
2
3
4
5
6
7
key := "passphrase"
dbPath = fmt.Sprintf(dbPath+"?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", key)
 
db, err = gorm.Open("sqlite3", dbPath)
iferr != nil{
    returnerr
}

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

sqlcipher 密码错误

1
2
3
4
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 代码创建加密数据库,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
2
3
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).

1
2
3
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 的,里面有这么一段:

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 makeany difference anyway since 4096is the defaultvalue 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
2
3
4
5
sqlite> PRAGMA key= "x'passphrase'";
sqlite> PRAGMA cipher_page_size=4096;
sqlite> SELECT* fromtest;
1|Hello, World!
sqlite> .exit

db browser

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

DBeaver

修改 legacy_page_size 为 4096 就可以了

总结

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

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

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

相关文章

    • golang的切片扩容机制是golang面试者绕不开的一扇大门,无论在面试提问,或者面试情景上都绕不开它,今天就说说我理解下的切片扩容机制,感兴趣的小伙伴跟着小编一起来看看吧
      2023-07-07
    • golang实现定时任务很简单,只须要简单几步代码即可以完成,最近在做了几个定时任务,想研究一下它内部是怎么实现的,所以将源码过了一遍,记录和分享在此。需要的朋友可以参考以下内容,希望对大家有帮助
      2022-10-10
    • 这篇文章主要介绍了go开发中引用静态库.a文件的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
      2020-11-11
    • Golang多线程爬虫是一种高效抓取大量数据的利器。Golang语言天生支持并发和多线程,可以轻松实现多线程爬虫的开发。通过使用Golang的协程和通道,可以实现爬虫的高效并发抓取、数据处理和存储
      2023-05-05
    • 本文主要介绍了golang 的高效编码细节,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
      2021-10-10
    • Go语言是一种开源的编程语言,以其强大的并发编程能力而闻名,本文将介绍Go语言并发编程的新境界,探讨如何利用Go语言的特性来实现高效的并发编程,需要的朋友可以参考下
      2023-10-10
    • 社区不少人在谈论 golang 为毛不用try/catch模式,而采用苛刻的recovery、panic、defer组合,本文就来详细的介绍一下,感兴趣的可以了解一下
      2021-07-07
    • 这篇文章主要介绍了Go语言中的通道channel,在Go语言中管道类似于一个数据流,每次放入或者取出一部分数据,数据取出后原通道内的数据就删除掉,在linux操作系统中管道会将函数的返回结果作为下一个函数的参数,下文详细内容需要的朋友可以参考下
      2022-02-02
    • 这篇文章主要给大家介绍了关于Golang处理parquet文件的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用Golang具有一定的参考学习价值,需要的朋友可以参考下
      2023-03-03
    • 这篇文章主要介绍了golang并发锁使用详解的相关资料,需要的朋友可以参考下
      2023-02-02

    最新评论