当前位置: 代码网 > it编程>前端脚本>Golang > 使用Go和Gorm实现读取SQLCipher加密数据库

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

2024年07月05日 Golang 我要评论
本文档主要描述通过 https://github.com/mutecomm/go-sqlcipher 生成和读取 sqlcipher 加密数据库以及其中踩的一些坑软件版本go: v1.22.2sqlc

本文档主要描述通过 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 代码创建加密数据库,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的资料请关注代码网其它相关文章!

(0)

相关文章:

  • Go自定义数据序列化的流程详解

    Go自定义数据序列化的流程详解

    引言在 go 语言中,自定义数据的序列化是一个常见的需求,尤其是在开发微服务架构或进行网络通信时。本文将深入探讨 go 语言中自定义数据序列化的流程,包括关键概... [阅读全文]
  • golang使用jaeger进行链路追踪

    前言提示:请配置好环境一、什么是链路追踪?链路追踪是指在分布式系统中,将一次请求的处理过程进行记录并聚合展示的一种方法。目的是将一次分布式请求的调用情况集中在一处展示,如各个服务节…

    2024年07月05日 前端脚本
  • go-zero使用goctl生成mongodb的操作使用方法

    go-zero使用goctl生成mongodb的操作使用方法

    mongodb简介mongodb是一种高性能、开源、文档型的nosql数据库,被广泛应用于web应用、大数据以及云计算领域。在使用mongodb之前,需要先在您... [阅读全文]
  • Go变量作用域代码实战详解

    Go变量作用域代码实战详解

    1. 变量的作用域概述在编程中,变量的作用域(scope)定义了变量在程序中的可见性和生命周期。理解变量的作用域对于编写健壮且可维护的代码至关重要。go语言(简... [阅读全文]
  • Golang中时间戳与时区转换的方法详解

    Golang中时间戳与时区转换的方法详解

    引言时间是我们生活的基石,而在计算机科学中,时间处理显得尤为重要。尤其是当你在处理分布式系统、跨时区应用和全球服务时,时间和时区的管理变得不可或缺。在这篇文章中... [阅读全文]
  • Gin框架中的PostForm用法及说明

    Gin框架中的PostForm用法及说明

    gin框架中postform用法最近重新在学习一下gin的:这边学到一个新的postform(),就是用来获取前端输入的字符串用的贴代码:package mai... [阅读全文]

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com