本文档主要描述通过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"
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
}
判断数据库是否加密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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"
)
}
读取数据库
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"
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 进去。
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
)
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 代码创建加密数据库,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,就可以了
DBeaver
修改 legacy_page_size
为 4096 就可以了
总结
其实这个懂的人,估计看到这个 page size 不同就知道要去配置了。对于不懂的人,看密码,又登入不进去就会很烦,就会乱。
后面分析的方法就是从单测入手,用最简单的方式先跑通一个。比如,密码先不要设置那么复杂的,就设置 123456,然后测试。通过再往下一步,往自己收到的问题去靠。
以上就是使用Go和Gorm实现读取SQLCipher加密数据库的详细内容,更多关于Go Gorm读取SQLCipher的资料请关注脚本之家其它相关文章!
最新评论