【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.DBtestDir = "go-sqlcipher_test"tables  = `CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT);`data    = `INSERT INTO test (data) VALUES ('Hello, World!');`)// create DBkey := "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 encryptedencrypted, 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.DBerr error)dbnameWithDSN := dbPath + fmt.Sprintf("?_pragma_key=%s", key)// open DB for testingdb, 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 Opendb.LogMode(true)// Set Idledb.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.mzph.cn/diannao/30838.shtml

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

相关文章

1.搭建SpringBoot项目三种方式

目录 1.使用Spring Initializr 1.1访问Spring Initializr: 1.2填写项目基本信息 1.3配置项目元数据: 1.4添加依赖: 1.5生成项目: 1.6下载项目: 1.7解压项目: 1.8导入项目到IDE: 1.9运行项目: 1.10创建控制器: 1.11访问应用 2.使用IDE(集成开发环境&…

langchain调用多模态大模型

下面示例是使用langchain调用多模态的示例 数据处理 import os import base64 from mimetypes import guess_typedef local_image_to_data_url(image_path):# Guess the MIME type of the image based on the file extensionmime_type, _ guess_type(image_path)if mime_typ…

34、shell数组+正则表达式

0、课前补充 jiafa () { result$(echo " $1 $2 " | bc ) print "%.2f\n" "$result" } ##保留小数点两位 薄弱加强点 a$(df -h | awk NR>1 {print $5} | tr -d %) echo "$a"一、数组 1.1、定义 数组的定义&am…

配电室数据中心巡检3d可视化搭建的详细步骤

要搭建配电室巡检的3D可视化系统,可以按照以下步骤进行: 收集配电室数据: 首先,需要收集配电室的相关数据,包括配电室的布局、设备信息、传感器数据等。可以通过实地调查、测量和设备手册等方式获取数据。 创建3D模型…

树莓派4b硬件资源

树莓派4B的硬件资源相当丰富,以下是对其硬件资源的详细归纳: 处理器: 树莓派4B使用Broadcom BCM2711芯片,该芯片基于ARM Cortex-A72架构。Cortex-A72是一个高性能的处理器架构,具有单线程性能高、功耗低以及强大的多核…

C# OpenCvSharp 图像处理函数-颜色通道-cvtColor

使用 OpenCvSharp 中的 cvtColor 函数进行图像颜色转换 在图像处理领域,颜色空间转换是一个非常常见的操作。OpenCvSharp 提供了一个强大的函数 cvtColor 来处理这类转换。本文将详细介绍 cvtColor 函数的使用方法,并通过具体的示例演示如何在实际项目中应用这些知识。 函数…

解决java.sql.SQLIntegrityConstraintViolationException: Duplicate entry ‘1‘ for key ‘book.PRIMARY‘

目录 问题 原因 解决方案 问题 场景:有张图书表,主键是自增的。现在我写了个添加图书功能。因为主键是自增的我插入时无需设置主键值。 关键代码如下: 然后就报这个异常了: 找了一圈并没有发现能解决我这个问题的。最终仔细分…

电脑桌面文件夹删除不了怎么办?6种方法快速解决,建议收藏!

桌面文件夹删不掉怎么办?有时会遇到桌面上的文件夹无法删除的问题,这是由于文件夹被系统进程或某些应用占用,或者是由于权限设置等原因造成的。以下是解决桌面文件夹无法删除问题的方法,帮助你有效地清理桌面环境。 桌面文件夹删不…

中欧科学家论坛暨第六届人工智能与先进制造国际会议(AIAM2024)

会议日期:2024年10月20-21日 会议地点:德国-法兰克福 会议官网:https://www.iaast.cn/meet/home/Bx130JiM 出版检索:EI、Scopus等数据库收录 【会议简介】 “中欧科学家论坛”由德国、法国、荷兰、瑞士、丹麦、意大利、西班牙…

欧几里得算法求解最大公约数(附代码实现,习题练习)

1.公约数、最大公约数的定义 公约数,亦称“公因数”。它是指能同时整除几个整数的数 。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”。公约数中最大的称为最大公约数。对任意的若干个正整数,1总是它们的公因数。eg.30和4…

微信小程序常用的api(2)

动画API 创建实例 wx.createAnimation() 作用:用于在微信小程序中完成动画效果的制作 动画的常用属性 duration 【number型】 动画持续时间,单位毫秒,默认400毫秒 timingFunction 【string型】 动画效果 默认linear delay 【number型】 动…

C++回溯算法(2)

棋盘问题 #include<bits/stdc.h> using namespace std; void func(int,int); bool tf(int,int); void c(); int n,k; char a[110][110]; int cnt20; int main() {cin>>n>>k;for(int i0;i<n;i){for(int j0;j<n;j){cin>>a[i][j];}}func(0,0);cout…

交易账户实现多人下单 分仓系统 交易外接 多指令管理

交易账户实现多人下单 分仓系统 交易外接 多指令管理 指令管家下载&#xff1a;https://raw.githubusercontent.com/LeoGQ/quant/main/CommandKeeper/%E6%8C%87%E4%BB%A4%E7%AE%A1%E5%AE%B6.zip 或项目地址&#xff1a;https://github.com/LeoGQ/quant/tree/main/CommandKeeper…

在国内默默无闻的.NET,在国外火的超乎想象?

.NET开篇概述 在编程语言的竞技场上&#xff0c;.NET似乎在国内并未获得与其实力相匹配的名声&#xff0c;相较于Java和Python的广为人知&#xff0c;.NET更像是一位隐匿在幕后的高手。然而&#xff0c;当我们放眼国际&#xff0c;.NET的火爆程度却让人不得不重新审视这位“幕后…

nvm 管理多版本node

因有多个前端项目&#xff0c;不同项目使用的node版本不一样&#xff0c;所以在本地使用nvm管理不同的node版本。 一、安装nvm 1&#xff09;nvm理解 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff…

minIo ubuntu单节点部署

资源准备 minio二进制包 下载地址:https://dl.min.io/server/minio/release/linux-amd64/minio ubuntu-单节点部署 选择一台ubuntu18.04机器10.253.9.41、intel 或者 amd 64位处理器 上传minio到~目录 sudo cp minio /usr/local/bin/ sudo chmod x /usr/local/bin/minio 设…

2024MathorCup A题 赛后思路代码分享(分赛区一等奖)移动通信网络中 PCI 规划问题

今年突然变成分赛区 (10%) 推国&#xff0c;国奖结果还没出&#xff0c;感觉一等&#xff08;2%&#xff09;有点悬&#xff0c;论文写的太一般了我没时间去修。 4 月不务正业又被拉着打了次比赛&#xff0c;刚好这几天有闲暇&#xff0c;传一下之前写的解题思路&#xff0c;不…

PHP面试必会的20个问题

PHP是什么&#xff0c;它的全称是什么&#xff1f; PHP是一种服务器端脚本语言&#xff0c;主要用于Web开发。它的全称是Hypertext Preprocessor&#xff08;超文本预处理器&#xff09;&#xff0c;由Rasmus Lerdorf在1994年首次创建。PHP可以嵌入到HTML中&#xff0c;用于生成…

文本预处理1--去除#和句号之间内容

1.针对文本里特定两个符号之间内容的中文进行去除&#xff0c;本次选取#和句号之间的内容进行去除。 2.大家可以根据自己的实际需求修改代码实现自己的文本内容的整理。 3.下面是去除#和句号之间内容的python代码&#xff1a;其中读取的是txt文本&#xff0c;处理后的内容写回…

超声波清洗机哪家好用又实惠?四款亲测表现出色超声波清洗机安利

在当今社会&#xff0c;随着生活节奏的加快&#xff0c;年轻人越来越多地依赖眼镜来纠正视力或保护眼睛。无论是为了时尚搭配&#xff0c;还是因为长时间面对电脑和手机屏幕导致的视力问题&#xff0c;眼镜已经成为许多年轻人日常生活中不可或缺的配件。然而&#xff0c;就在我…