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

本文档主要描述通过 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,然后测试。通过再往下一步,往自己收到的问题去靠。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于jeecgboot-vue3的Flowable流程-流程处理(二)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 对应VForm3&#xff0c;原先的后端解析也要做调整 1、获取历史任务的表单信息 // 获取历史任务节点表单数据值List<HistoricVariableInstance> listHistoricVariableInstance his…

Python第二语言(十二、SQL入门和实战)

目录 1. Python中使用MySQL 1.1 pymysql第三方库使用MySQL 1.2 连接MySQL 1.3 操作数据库&#xff0c;创建表 1.4 执行查询数据库语句 2. python中MySQL的插入语句 2.1 commit提交 2.2 自动提交 3. pymysql案例 3.1 数据内容 3.2 DDL定义 3.3 实现步骤 3.4 文件操…

最小生成树kruskal算法详解

kruskal算法的思想简单来说就是&#xff1a;每次选择图中最小边权的边&#xff0c;如果边两端的顶点不在相同的连通块中&#xff0c;就把这条边加入到最小生成树中。 具体实现如下&#xff1a; 首先是边的定义&#xff0c;需要判断边的两个端点是否在不同的连通块中&#xff…

Vue前端ffmpeg压缩视频再上传(全网唯一公开真正实现)

1.Vue项目中安装插件ffmpeg 1.1 插件版本依赖配置 两个插件的版本 "ffmpeg/core": "^0.10.0", "ffmpeg/ffmpeg": "^0.10.1"package.json 和 package-lock.json 都加入如下ffmpeg的版本配置&#xff1a; 1.2 把ffmpeg安装到项目依…

深入探究MySQL游标(Cursor)

前言 MySQL游标&#xff08;Cursor&#xff09;是MySQL中用于处理查询结果的一种机制。游标允许我们在查询结果集中逐行处理数据&#xff0c;而不是一次性获取所有数据。这对于处理大量数据非常有用&#xff0c;因为它可以减少内存消耗并提高性能。在MySQL中&#xff0c;游标主…

【源码】【Spring+SpringMVC+MyBatis】电子商城网上购物平台的设计与开发

学生成绩管理系统 系统功能开发环境开发技术前端技术后端技术 系统展示登录界面注册界面系统首页商品详情页下单界面付款界面购物车界面 源码获取↓↓↓↓&#xff1a; 源码可在后台私信联系博主或文末添加博主微信获取帮助 系统功能 登录、注册模块&#xff1a;如果用户第一次…

udp协议下的socket函数

目录 1.网络协议 2.网络字节序 3.socket编译接口 4.sockaddr结构体 5.模拟实现 1.socket函数 2.bind函数&#xff08;绑定&#xff09; 1.讲解 1.如何快速的将 整数ip<->字符串 2.ip地址的注意事项 3.端口号的注意事项 3.recvfrom函数 4.sendto函数 5.代码呈…

C++ Primer 第五版 第16章 模板与泛型编程

模板是C中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或者说公式。当使用一个vector这样的泛型类型&#xff0c;或者find这样的泛型函数时&#xff0c;我们提供足够的信息&#xff0c;将蓝图转换为特定的类或函数。这种转换发生在编译时。 一、定义模板 1. 函数模板…

Airtest 使用指南

Airtest 介绍 准备工作 AirtestIDE 安装与启动: https://airtest.doc.io.netease.com/IDEdocs/getting_started/AirtestIDE_install/ 电脑端的准备工作完成后,对于手机端只需要打开允许USB调试,当首次运行时会提示安装PocoService,同意即可。 界面介绍

【CT】LeetCode手撕—53. 最大子数组和

目录 题目1-思路2- 实现⭐53. 最大子数组和——题解思路 3- ACM 实现 题目 原题连接&#xff1a;53. 最大子数组和 1-思路 动规五部曲 1. 定义 dp 数组 dp[i] 含义为&#xff1a;下标为 i 的数组的最大子数组和 2. 递推公式 因为所求的是最大子数组的和&#xff0c;即当前 n…

群辉其它远程访问方案(Cpolar篇)

目录 1、下载NAS套件安装包 2、手动安装 3、配置 4、访问 &#xff08;1&#xff09;网页 &#xff08;2&#xff09;手机管家 &#xff08;3&#xff09;助手 &#xff08;4&#xff09;DS File 群辉的远程访问&#xff0c;最标准的做法就是使用群辉自己的DDNS&#x…

飞腾派初体验(2)

水个字数&#xff0c;混个推广分&#xff0c;另外几个点还是想吐槽一下 - 1&#xff0c;上篇文章居然没有给开发板一个硬照&#xff0c;补上 - 飞腾派 自拍 2. 现在做镜像用Win32DiskImager的多吗&#xff1f;我记得当年都是dd命令搞定&#xff0c;玩树莓派的应该记得这个命令…

OpenAPI Typescript Codegen 的基本使用

下载 axios npm install axios OpenAPI Typescript Codegen 官网&#xff1a;https://github.com/ferdikoomen/openapi-typescript-codegen 安装 OpenAPI Typescript Codegen npm install openapi-typescript-codegen --save-dev–input&#xff1a;指定接口文档的路径、url …

安装Pygame

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Pygame是跨平台的Python模块&#xff0c;专为电子游戏设计&#xff08;包含图像、声音&#xff09;&#xff0c;创建在SDL&#xff08;Simple Direct…

【HarmonyOS - UIAbility组件和UI的数据同步】

简述 基于HarmonyOS的应用模型&#xff0c;可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 使用EventHub进行数据通信&#xff1a;基于发布订阅模式来实现&#xff0c;事件需要先订阅后发布&#xff0c;订阅者收到消息后进行处理。使用globalThis进行数据同步…

【Linux】进程控制2——进程等待(waitwaitpid)

1. 进程等待必要性 我们知道&#xff0c;子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成"僵尸进程”的问题&#xff0c;进而造成内存泄漏。另外&#xff0c;进程一旦变成僵尸状态&#xff0c;那就刀枪不入&#xff0c;“杀人不眨眼”的kill -9 也无能为…

Git基础指令(图文详解)

目录 Git概述Git基础指令Linux系统操作指令 Git软件指令1.配置信息2.名称和邮箱3.初始化版本库4.向版本库中添加文件5.修改版本库文件6. 查看版本库文件历史 7.删除文件8.恢复历史文件 Git概述 Git基础指令 Linux系统操作指令 Git是一款免费、开源的分布式版本控制系统&…

MPLS工作过程

数据层面&#xff1a; 1) 没有 MPLS 协议&#xff0c;基于 FIB 表正常转发即可 2) 名词&#xff1a;MPLS domain——MPLS 的工作半径 edge LSR(PE)——边界标签交换路由器 工作 mpls 域的边缘&#xff0c;连接域外设备 …

【Redis】安装和命令行客户端

https://www.bilibili.com/video/BV1cr4y1671t https://www.oz6.cn/articles/58 redis 非结构化有&#xff1a; 键值类型(Redis)文档类型(MongoDB)列类型(HBase)Graph:类型(Neo4j) 扩展性&#xff1a;水平即为分布式扩展 redis特征 键值&#xff08;key-value&#xff09;型…

css实现优惠券样式

实现优惠券效果&#xff1a; 实现思路&#xff1a; 需要三个盒子元素&#xff0c;使用 css 剪裁&#xff0c;利用 ellipse 属性&#xff0c;将两个盒子分别裁剪成两个半圆&#xff0c;位置固定在另一个盒子元素左右两边适当位置上。为另一个盒子设置想要的样式&#xff0c;圆角…