Flutter密码管理插件kpasslib的使用
Flutter 密码管理插件 kpasslib 的使用
KPassLib 是一个 Dart 库,用于读取、修改和编写 KeePass v2 数据库(KDBX 版本 3 或 4)。它从 KdbxWeb 移植并重构而来。
功能
- 无需原生插件
- 使用 pointycastle、cryptography_plus 和 hashlib 进行加密
- 完全支持 KDBX 特性
- 受保护的值在内存中与盐进行异或运算
- 支持无冲突合并
- 高代码测试覆盖率
- Dart 3 带有健全的空安全
兼容性
支持 KDBX 版本 3 和 4,即当前的 KeePass 文件格式。旧的 KDB 文件(适用于 KeePass v1)不在范围之内。
使用
加载
final credentials = KdbxCredentials(
password: ProtectedData.fromString('demo'),
keyData: demoKey,
challengeResponse: challengeResponse,
);
final db1 = KdbxDatabase.fromBytes(
data: TestResources.demoKdbx,
credentials: credentials,
);
final db2 = KdbxDatabase.fromXmlString(
xmlString: xml,
credentials: credentials,
);
保存
final data = db.save();
final xmlString = db.exportToXmlString(pretty: true);
你也可以格式化 XML:
final prettyXml = db.exportToXmlString(pretty: true);
更改凭证
final db = KdbxDatabase.fromBytes(
data: TestResources.demoKdbx,
credentials: KdbxCredentials(
password: ProtectedData.fromString('demo'),
),
);
db.header.credentials = KdbxCredentials(
password: ProtectedData.fromString('new password'),
);
final data = db.save();
创建数据库
final db = KdbxDatabase.create(
credentials: credentials,
name: 'Example',
);
final subGroup = db.createGroup(
parent: db.root,
name: 'Subgroup',
);
final entry = db.createEntry(parent: subGroup);
维护
db.cleanup(
history: true,
icons: true,
binaries: true,
);
// 升级到最新版本(目前是 KDBX 4.1)
db.upgrade();
// 降级到 KDBX 3.1
db.version = (3, 1);
// 设置 KDF 为 AES
db.kdf = KdfId.aes;
合并
// 加载本地数据库
var db = KdbxDatabase.fromBytes(
data: localData,
credentials: credentials,
);
// 工作并保存数据库
db.save();
// 保存本地编辑状态
var state = db.localEditState.toXml().toXmlString();
// 重新打开数据库
db = KdbxDatabase.fromBytes(
data: fileData,
credentials: credentials,
);
// 分配之前保存的编辑状态
db.localEditState = KdbxEditState.fromXml(
XmlDocument.parse(state).firstChild,
);
// 工作并加载远程数据库
final remote = KdbxDatabase.fromBytes(
data: remoteData,
credentials: credentials,
);
// 将远程数据合并到本地
db.merge(remote);
// 保存本地数据库
final data = db.save();
db.clearLocalEditState();
群组
final root = db.root;
final anotherGroup = db.root.allItems.firstWhereOrNull(
(item) => item.uuid == id
);
final deepGroup = db.root.groups[1].groups[2];
创建群组
final group = db.createGroup(
parent: db.root,
name: 'New group',
);
final anotherGroup = db.createGroup(
parent: group,
name: 'Subgroup',
);
删除项目
db.remove(group);
移动项目
db.move(item: group, target: toGroup);
db.move(item: group, target: toGroup, index: atIndex);
回收站
final recycleBin = db.recycleBin;
递归遍历
db.root.allEntries.forEach((e){/* ... */});
db.root.allGroups.forEach((g){/* ... */});
db.root.allItems.forEach((i){/* ... */});
条目
final entry = db.root.entries.first;
entry.fields['AccountNumber'] = KdbxTextField.fromText(
text: '1234 5678',
);
entry.fields['PIN'] = KdbxTextField.fromText(
text: '4321',
protected: true,
);
创建条目
final entry = db.createEntry(parent: group);
修改条目
// 推送当前状态到历史栈
entry.pushHistory();
// 更改某些内容
entry.foreground = '#ff0000';
// 更新条目的修改时间和访问时间
entry.times.touch();
// 从条目历史中删除状态
entry.removeFromHistory(start: 0, end: 5);
重要:不要直接修改历史状态,这将破坏合并。
导入条目
db.importEntry(
entry: entry,
target: toGroup,
other: sourceFile
);
受保护的数据
final value = ProtectedData.fromProtectedBytes(bytes: value, salt: salt);
final valueFromString = ProtectedData.fromString('str');
final valueFromBinary = ProtectedData.fromBytes(data);
final textString = valueFromString.text;
final binaryData = valueFromBinary.bytes;
错误处理
try {
KdbxDatabase.fromBytes(
data: data,
credentials: credentials,
);
} on FileCorruptedError catch (e) {
/// 处理错误
}
测试和生成代码覆盖率
运行测试
使用 VS Code 测试标签页或终端命令:
dart test
生成代码覆盖率报告
使用终端命令:
dart test --coverage=coverage
dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib
genhtml coverage/lcov.info -o coverage/
示例代码
以下是一个完整的示例代码:
import 'package:kpasslib/kpasslib.dart';
/// 创建一个新的数据库,对其进行修改,保存并重新加载。
void main() async {
final credentials = KdbxCredentials(
password: ProtectedData.fromString('demo'),
keyData: KdbxCredentials.createRandomKeyFile(version: 2),
);
var db = KdbxDatabase.create(
credentials: credentials,
name: 'Example',
);
final subGroup = db.createGroup(
parent: db.root,
name: 'Subgroup',
);
final entry = db.createEntry(parent: subGroup);
entry.fields.addAll({
'Title': KdbxTextField.fromText(text: 'Title'),
'UserName': KdbxTextField.fromText(text: 'User'),
'Password': KdbxTextField.fromText(
text: 'Password',
protected: true,
),
});
final binary = ProtectedBinary(
protectedData: ProtectedData.fromString(
'bin.txt content',
),
);
final reference = db.binaries.add(binary);
entry.binaries['bin.txt'] = reference;
entry.pushHistory();
entry.fields.addAll({
'Title': KdbxTextField.fromText(text: 'New title'),
'UserName': KdbxTextField.fromText(text: 'New user'),
'Password': KdbxTextField.fromText(
text: 'New password',
protected: true,
),
'Custom': KdbxTextField.fromText(text: 'Custom'),
'ProtectedCustom': KdbxTextField.fromText(
text: 'Protected custom',
protected: true,
),
});
entry.times.touch();
final data = await db.save();
db = await KdbxDatabase.fromBytes(
data: data,
credentials: credentials,
);
}
更多关于Flutter密码管理插件kpasslib的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter密码管理插件kpasslib的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用kpasslib
插件进行密码管理的代码示例。kpasslib
是一个用于处理KeePass密码数据库的Flutter插件。请注意,这个示例假设你已经有一个KeePass数据库文件,并且你了解基本的Flutter开发流程。
首先,你需要在你的pubspec.yaml
文件中添加kpasslib
依赖:
dependencies:
flutter:
sdk: flutter
kpasslib: ^latest_version # 请替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,是一个简单的Flutter应用示例,展示如何加载并读取KeePass数据库:
import 'package:flutter/material.dart';
import 'package:kpasslib/kpasslib.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'KeePass Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _password = '';
List<KeePassEntry> _entries = [];
@override
void initState() {
super.initState();
_loadDatabase();
}
Future<void> _loadDatabase() async {
// 获取应用的本地存储目录
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/example.kdbx'; // 替换为你的KeePass数据库文件路径
// 确保文件存在
File file = File(filePath);
if (!await file.exists()) {
print('Database file does not exist.');
return;
}
// 读取文件内容
List<int> fileBytes = await file.readAsBytes();
// 使用密码加载数据库
String password = 'your_password_here'; // 替换为你的数据库密码
try {
KeePassDatabase db = KeePassDatabase.fromBytes(fileBytes, password: password);
setState(() {
_entries = db.entries;
});
} catch (e) {
print('Failed to load database: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('KeePass Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
controller: TextEditingController(text: _password),
onChanged: (value) {
setState(() {
_password = value;
});
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
// 这里你可以添加逻辑来使用用户输入的密码重新加载数据库
// 例如,调用_loadDatabase()函数并传递新密码
// 注意:出于安全考虑,实际应用中应避免在UI中直接处理密码
},
child: Text('Reload with New Password'),
),
SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemCount: _entries.length,
itemBuilder: (context, index) {
KeePassEntry entry = _entries[index];
return ListTile(
title: Text(entry.title),
subtitle: Text(entry.userName),
);
},
),
),
],
),
),
);
}
}
在这个示例中,我们做了以下几件事:
- 在
pubspec.yaml
中添加了kpasslib
依赖。 - 创建了一个简单的Flutter应用,包含一个文本字段用于(示例中未实际使用,但展示了如何获取用户输入)和一个按钮(虽然按钮的点击事件未实现密码重新加载的逻辑,但展示了位置)。
- 在
_loadDatabase
函数中,我们获取了应用的文档目录,然后尝试加载位于该目录下的KeePass数据库文件。你需要将文件路径和密码替换为你自己的。 - 使用
KeePassDatabase.fromBytes
方法从字节数据加载数据库,并将加载的条目存储在状态中以便在UI中显示。
请注意,出于安全考虑,实际应用中应避免在UI中直接处理或显示密码。此外,这个例子没有包含所有可能的错误处理和用户体验优化,比如加载指示器、密码输入安全性增强等。根据你的具体需求,你可能需要进一步完善这个示例。