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

news2025/1/12 20:58:10

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

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)

可视化部分的可以通过这一篇文章细看 用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库。

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

踩坑 & 分析

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

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 不支持我这种加密格式

在这里插入图片描述

跑单测

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

  1. go 代码创建加密数据库,sqlcipher 指令读取,这个是可以的。这一个测试我用的是最上面生成数据库的代码。
  2. 因为我收到的代码里面有带,_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

DBeaver driver 的驱动配置见另一个文档 用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库

修改 legacy_page_size 为 4096 就可以了

在这里插入图片描述

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1819914.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GIS开发到底能应用在哪些行业 ?

GIS应用的领域到底有多广?恐怕很多GIS从业者都想不到。 尤其是近些年,互联网GIS的普及与发展,GIS技术的应用领域越来越多,涉及的范围也越来越广。很多我们以为跟GIS不相关的行业,都在悄悄用GIS技术。 从大类上分析&a…

EFuse概念解析

EFuse概念解析 EFUSE Key Parameter iNOM 代表的是,Efuse运行时候的电流 tNOM 代表的是,Efuse电流与时间的曲线 INOM通过VOC_Thrs设置 VOC_THRS VOC_THRS/Rsense Vsense采样小于VOC_THRS时候不动作 Vsense采样大于VOC_THRS时候根据Efuse_I2T曲线来…

SpringBoot之请求映射原理

前言 我们发出的请求,SpringMVC是如何精准定位到那个Controller以及具体方法?其实这都是 HandlerMapping 发挥的作用,这篇博文我们以 RequestMappingHandlerMapping 为例并结合源码一步步进行分析。 定义HandlerMapping 默认 HandlerMappi…

数据结构的队列,链表,栈的基础操作

1&#xff1a;队列 #include <stdio.h>#include <stdlib.h>#include "./02队列.h"/** function: 创建一个空的队列* param [ in] * param [out] * return */Sequeue* xinduilie(){Sequeue* sq (Sequeue*)malloc(sizeof(Sequeue)); if(N…

Linux基础IO【II】真的很详细

目录 一.文件描述符 1.重新理解文件 1.推论 2.证明 2.理解文件描述符 1.文件描述符的分配规则 3.如何理解文件操作的本质&#xff1f; 4.输入重定向和输出重定向 1.原理 2.代码实现重定向 3.dup函数 ​编辑 4.命令行中实现重定向 二.关于缓冲区 1.现象 …

Ubuntu系统中网易云音乐编译安装

项目地址&#xff1a; netease-cloud-music-gtk: Linux 平台下基于 Rust GTK 开发的网易云音乐播放器 目录 1.README.md中按照步骤来 2.安装git 3.报错 sudo apt install cmake sudo apt-get install libdbus-1-dev sudo apt install dnf sudo dnf install gettext 继…

【Pytorch】一文向您详细介绍 torch.nn.DataParallel() 的作用和用法

【Pytorch】一文向您详细介绍 torch.nn.DataParallel() 的作用和用法 下滑查看解决方法 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高…

云原生Kubernetes系列项目实战-k8s集群+高可用负载均衡层+防火墙

一、Kubernetes 区域可采用 Kubeadm 方式进行安装&#xff1a; 名称主机部署服务master192.168.91.10docker、kubeadm、kubelet、kubectl、flannelnode01192.168.91.11docker、kubeadm、kubelet、kubectl、flannelnode02192.168.91.20docker、kubeadm、kubelet、kubectl、flan…

【Numpy】一文向您详细介绍 np.trunc()

【Numpy】一文向您详细介绍 np.trunc() 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff0c;…

诊所管理系统免费软件哪个好一点?

不少诊所管理者&#xff0c;想要寻找一款适合自己诊所的免费诊所管理系统。市场上有多个选择&#xff0c;那么&#xff0c;哪个会好一点呢?在选择适合自己诊所的免费诊所管理系统时&#xff0c;考虑系统的易用性、功能全面性、技术支持以及未来可扩展性是非常重要的。下面&…

算法分析与设计期末考试复习GDPU

重点内容&#xff1a; 绪论&#xff1a; 简单的递推方程求解 1.19(1)(2) 、 教材例题 多个函数按照阶的大小排序 1.18 分治法&#xff1a; 分治法解决芯片测试问题 计算a^n的复杂度为logn的算法&#xff08;快速幂&#xff09; 分治法解决平面最近点对问…

值得关注的BTC二层 BitlayerLabs,以及好用的bitget钱包

值得关注的比特币二层 BitlayerLabs Bitlayer BitlayerLabs 是基于 BitVM 的与比特币安全等效的 Layer2。创新的BitRC-20资产框架为生态系统提供了丰富的资产类别。 为什么推荐bitlayer&#xff1f; Framework Ventures 和 ABCDE Capital 领投&#xff0c;OKX Ventures 等参…

微软开发基于深度学习的地球大气基础模型-Aurora

微软研究院的研究人员开发了大气基础模型“Aurora”&#xff0c;https://arxiv.org/abs/2405.13063v1 利用超过一百万小时的多样化天气和气候数据进行训练&#xff0c;能够预测各种大气条件&#xff0c;包括数据有限、变量异构和极端事件&#xff0c;并在全球空气污染和高分辨…

什么是CPT❓CPT中卖方责任是什么?

CPT :CPT 运费付至 &#xff08;……指定的目的地&#xff09; 运费付至&#xff08;……指定的地点&#xff09;”是指卖方向其指定的承运人交货&#xff0c;但卖方还必须支付将货物运至目的地的运费。即买方承担交货之后一切风险和其他费用。 “承运人”是指任何人&#xff…

LabVIEW开发CAN通讯协议

在LabVIEW中开发CAN&#xff08;Controller Area Network&#xff09;通讯协议时&#xff0c;需要全面考虑硬件选择、驱动安装、通信配置、错误处理、数据解析和实时性等多个方面。本文详细介绍了在LabVIEW中实现CAN通讯时的关键点和最佳实践&#xff0c;确保系统的稳定性、可靠…

VScode对比代码功能

CtrlShiftP选择第一行Compare&#xff0c;当前文件就和选中文件对比改动了。

梯度下降法、牛顿法、条件熵

梯度下降法、牛顿法&#xff08;可见南瓜书&#xff09; 梯度下降法 深度学习&#xff1a;梯度下降法数学表示式的详细推导 牛顿法 条件熵 在李航老师的书中&#xff0c;第六章最大熵模型这里有个条件熵的定义&#xff1a; 推导如下&#xff1a; 其他一些事实&#xff1a;…

最小生成树prim算法详解

prim算法解决的是最小生成树问题&#xff0c;即在一个给定的无向图G中求一棵生成树T&#xff0c;使得这棵树拥有图G中的所有顶点&#xff0c;且所有边都是来自图G中的边&#xff0c;并且满足整棵树的边权之和最小。 prim算法的基本思想是对图G设置集合S来存放已被访问的顶点&a…

IPTCP知识

1. IP&#xff1a; IP地址是一个32位的二进制数&#xff0c;通常被分割为4个“8位二进制数”IP地址分类&#xff1a;A类地址、B类地址、C类地址、D类地址、E类地址 A类地址分配给规模特别大的网络使用&#xff0c;B类地址分配给一般的中型网络&#xff0c;C类地址分配给小型网…

复合机器人以其高度的灵活性和操作效率,展现了显著的优势

随着工业4.0的深入推进和智能制造的快速发展&#xff0c;复合机器人作为一种集成移动机器人和工业机器人功能的先进设备&#xff0c;正逐步成为工业自动化领域的新宠。特别是在磁钢上下料的应用中&#xff0c;复合机器人以其高度的灵活性和操作效率&#xff0c;展现了显著的优势…