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

1 回复

更多关于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),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. pubspec.yaml中添加了kpasslib依赖。
  2. 创建了一个简单的Flutter应用,包含一个文本字段用于(示例中未实际使用,但展示了如何获取用户输入)和一个按钮(虽然按钮的点击事件未实现密码重新加载的逻辑,但展示了位置)。
  3. _loadDatabase函数中,我们获取了应用的文档目录,然后尝试加载位于该目录下的KeePass数据库文件。你需要将文件路径和密码替换为你自己的。
  4. 使用KeePassDatabase.fromBytes方法从字节数据加载数据库,并将加载的条目存储在状态中以便在UI中显示。

请注意,出于安全考虑,实际应用中应避免在UI中直接处理或显示密码。此外,这个例子没有包含所有可能的错误处理和用户体验优化,比如加载指示器、密码输入安全性增强等。根据你的具体需求,你可能需要进一步完善这个示例。

回到顶部