
鸿蒙跨端密码管理器:安全加密与多设备同步方案 原创
鸿蒙跨端密码管理器:安全加密与多设备同步方案
本文将基于HarmonyOS 5的安全存储能力和分布式技术,实现一个支持多设备同步的加密密码管理器,确保用户密码在存储和传输过程中的安全性。
技术架构
安全存储层:使用加密API保护敏感数据
数据同步层:通过分布式数据管理实现加密数据同步
身份验证层:生物识别和密码保护访问控制
UI展示层:安全展示密码信息
完整代码实现
密码数据模型定义
// model/PasswordEntry.ts
export class PasswordEntry {
id: string = ‘’; // 唯一标识
title: string = ‘’; // 标题(如网站名称)
username: string = ‘’; // 用户名
encryptedPassword: string = ‘’; // 加密后的密码
url: string = ‘’; // 网站URL
notes: string = ‘’; // 备注
category: string = ‘默认’; // 分类
lastModified: number = 0; // 最后修改时间戳
deviceId: string = ‘’; // 最后修改的设备ID
constructor(data?: Partial<PasswordEntry>) {
if (data) {
Object.assign(this, data);
if (!this.id) {
this.id = this.generateId();
if (!this.lastModified) {
this.lastModified = Date.now();
}
private generateId(): string {
return 'id-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}
加密服务实现
// service/CryptoService.ts
import cipher from ‘@ohos.security.cipher’;
import util from ‘@ohos.util’;
export class CryptoService {
private static readonly ALGORITHM = ‘AES128’;
private static readonly IV = ‘1234567890123456’; // 初始化向量,实际项目中应更安全
private key: string;
constructor(masterPassword: string) {
// 使用主密码派生加密密钥(简化版,实际应使用PBKDF2等算法)
const md = new util.Base64Helper();
this.key = md.encodeToStringSync(masterPassword.padEnd(16, ‘0’).substr(0, 16));
// 加密数据
encrypt(data: string): string {
try {
const cipherObj = cipher.createCipher(this.ALGORITHM);
const cipherParams = {
alg: this.ALGORITHM,
key: this.key,
iv: CryptoService.IV
};
cipherObj.init(cipherParams);
const encrypted = cipherObj.doFinal({ data: new util.TextEncoder().encode(data) });
return new util.Base64Helper().encodeToStringSync(encrypted.data);
catch (err) {
console.error('加密失败:', err);
return '';
}
// 解密数据
decrypt(encryptedData: string): string {
try {
const cipherObj = cipher.createCipher(this.ALGORITHM);
const cipherParams = {
alg: this.ALGORITHM,
key: this.key,
iv: CryptoService.IV
};
cipherObj.init(cipherParams);
const decoded = new util.Base64Helper().decodeSync(encryptedData);
const decrypted = cipherObj.doFinal({ data: new Uint8Array(decoded) });
return new util.TextDecoder().decode(decrypted.data);
catch (err) {
console.error('解密失败:', err);
return '';
}
分布式密码同步服务
// service/PasswordSyncService.ts
import distributedData from ‘@ohos.data.distributedData’;
import deviceInfo from ‘@ohos.deviceInfo’;
import { PasswordEntry } from ‘…/model/PasswordEntry’;
const STORE_ID = ‘password_manager_store’;
const PASSWORD_KEY_PREFIX = ‘pwd_entry_’;
export class PasswordSyncService {
private kvManager: distributedData.KVManager;
private kvStore: distributedData.SingleKVStore;
private localDeviceId: string = deviceInfo.deviceId;
// 初始化分布式数据存储
async initialize() {
const config = {
bundleName: ‘com.example.passwordmanager’,
userInfo: {
userId: ‘password_user’,
userType: distributedData.UserType.SAME_USER_ID
};
this.kvManager = distributedData.createKVManager(config);
const options = {
createIfMissing: true,
encrypt: true, // 启用分布式数据库加密
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
};
this.kvStore = await this.kvManager.getKVStore(STORE_ID, options);
// 订阅数据变更
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
this.handleDataChange(data);
});
// 处理数据变更
private handleDataChange(data: distributedData.ChangeNotification) {
if (data.insertEntries.length > 0) {
data.insertEntries.forEach(entry => {
if (entry.key.startsWith(PASSWORD_KEY_PREFIX)) {
const entryId = entry.key.substring(PASSWORD_KEY_PREFIX.length);
const passwordEntry: PasswordEntry = JSON.parse(entry.value.value);
// 更新AppStorage中的密码条目
const currentEntries: Map<string, PasswordEntry> = AppStorage.get('passwordEntries') || new Map();
currentEntries.set(entryId, passwordEntry);
AppStorage.setOrCreate('passwordEntries', currentEntries);
});
}
// 同步密码条目
async syncPasswordEntry(entry: PasswordEntry) {
entry.deviceId = this.localDeviceId;
entry.lastModified = Date.now();
const entryKey = {PASSWORD_KEY_PREFIX}{entry.id};
await this.kvStore.put(entryKey, JSON.stringify(entry));
// 删除密码条目
async deletePasswordEntry(entryId: string) {
const entryKey = {PASSWORD_KEY_PREFIX}{entryId};
await this.kvStore.delete(entryKey);
// 获取所有密码条目
async getAllPasswordEntries(): Promise<PasswordEntry[]> {
const entries = await this.kvStore.getEntries(PASSWORD_KEY_PREFIX);
return Array.from(entries.values()).map(entry =>
new PasswordEntry(JSON.parse(entry.value.value))
);
// 获取当前设备ID
getLocalDeviceId(): string {
return this.localDeviceId;
}
密码管理器页面实现
// pages/PasswordManagerPage.ets
import { PasswordEntry } from ‘…/model/PasswordEntry’;
import { PasswordSyncService } from ‘…/service/PasswordSyncService’;
import { CryptoService } from ‘…/service/CryptoService’;
@Entry
@Component
struct PasswordManagerPage {
private syncService: PasswordSyncService = new PasswordSyncService();
private cryptoService: CryptoService;
@StorageLink(‘passwordEntries’) passwordEntries: Map<string, PasswordEntry> = new Map();
@State masterPassword: string = ‘’;
@State isAuthenticated: boolean = false;
@State showPasswordDialog: boolean = false;
@State currentPassword: string = ‘’;
@State currentEntry: PasswordEntry | null = null;
async aboutToAppear() {
await this.syncService.initialize();
build() {
Column() {
// 身份验证状态
if (!this.isAuthenticated) {
this.buildAuthView();
else {
this.buildMainView();
}
.width('100%')
.height('100%')
// 身份验证视图
@Builder
buildAuthView() {
Column() {
Text(‘密码管理器’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 40, bottom: 40 })
TextInput({ placeholder: '输入主密码' })
.type(InputType.Password)
.width('80%')
.margin({ bottom: 20 })
.onChange((value: string) => {
this.masterPassword = value;
})
Button('解锁')
.width('80%')
.onClick(async () => {
if (this.masterPassword) {
this.cryptoService = new CryptoService(this.masterPassword);
this.isAuthenticated = true;
await this.loadPasswordEntries();
})
// 生物识别认证选项
Button('使用指纹解锁')
.width('80%')
.margin({ top: 20 })
.onClick(() => {
this.authenticateWithBiometrics();
})
.width(‘100%’)
.height('100%')
.justifyContent(FlexAlign.Center)
// 主视图
@Builder
buildMainView() {
Column() {
// 标题栏
Row() {
Text(‘我的密码’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button('添加')
.onClick(() => {
this.currentEntry = new PasswordEntry();
this.showPasswordDialog = true;
})
.padding(16)
.width('100%')
// 密码列表
List() {
ForEach(Array.from(this.passwordEntries.values()), (entry: PasswordEntry) => {
ListItem() {
PasswordListItem({
entry: entry,
onTap: () => {
this.currentEntry = entry;
this.currentPassword = this.cryptoService.decrypt(entry.encryptedPassword);
this.showPasswordDialog = true;
})
})
.layoutWeight(1)
.width('100%')
// 密码详情对话框
if (this.showPasswordDialog && this.currentEntry) {
this.buildPasswordDialog();
}
// 密码详情对话框
@Builder
buildPasswordDialog() {
Column() {
Text(this.currentEntry!.title || ‘新密码条目’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
TextInput({ placeholder: '标题', text: this.currentEntry!.title })
.width('90%')
.margin({ bottom: 10 })
.onChange((value: string) => {
this.currentEntry!.title = value;
})
TextInput({ placeholder: '用户名/邮箱', text: this.currentEntry!.username })
.width('90%')
.margin({ bottom: 10 })
.onChange((value: string) => {
this.currentEntry!.username = value;
})
TextInput({ placeholder: '密码', text: this.currentPassword })
.width('90%')
.margin({ bottom: 10 })
.type(InputType.Password)
.onChange((value: string) => {
this.currentPassword = value;
})
TextInput({ placeholder: 'URL', text: this.currentEntry!.url })
.width('90%')
.margin({ bottom: 10 })
.onChange((value: string) => {
this.currentEntry!.url = value;
})
Row() {
Button('取消')
.layoutWeight(1)
.onClick(() => {
this.showPasswordDialog = false;
})
Button('保存')
.layoutWeight(1)
.margin({ left: 10 })
.onClick(async () => {
this.currentEntry!.encryptedPassword = this.cryptoService.encrypt(this.currentPassword);
await this.syncService.syncPasswordEntry(this.currentEntry!);
this.showPasswordDialog = false;
})
.width(‘90%’)
.margin({ top: 20 })
.width(‘90%’)
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 4, color: '#20000000', offsetX: 0, offsetY: 2 })
.onClick(() => {})
// 生物识别认证
private async authenticateWithBiometrics() {
try {
const auth = userAuth.getAuthInstance();
const result = await auth.auth({
type: userAuth.UserAuthType.FINGERPRINT,
level: userAuth.AuthLevel.STRONG
});
if (result === userAuth.AuthResult.SUCCESS) {
// 从安全存储中获取主密码
const prefs = preferences.getPreferences(globalThis.context, ‘auth_store’);
this.masterPassword = await prefs.get(‘master_password’, ‘’);
if (this.masterPassword) {
this.cryptoService = new CryptoService(this.masterPassword);
this.isAuthenticated = true;
await this.loadPasswordEntries();
}
catch (err) {
prompt.showToast({ message: '生物识别认证失败', duration: 2000 });
}
// 加载密码条目
private async loadPasswordEntries() {
const entries = await this.syncService.getAllPasswordEntries();
const entriesMap = new Map<string, PasswordEntry>();
entries.forEach(entry => entriesMap.set(entry.id, entry));
AppStorage.setOrCreate(‘passwordEntries’, entriesMap);
}
@Component
struct PasswordListItem {
@Prop entry: PasswordEntry;
@Prop onTap: () => void;
build() {
Row() {
Column() {
Text(this.entry.title || ‘未命名条目’)
.fontSize(16)
.fontColor(‘#333333’)
Text(this.entry.username)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 4 })
.layoutWeight(1)
Text(this.entry.category)
.fontSize(12)
.fontColor('#888888')
.border({ width: 1, color: '#888888' })
.borderRadius(4)
.padding(4)
.width(‘100%’)
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 8 })
.onClick(() => {
this.onTap();
})
}
安全存储配置
// resources/base/profile/security_config.json
“crypto”: {
"aes": {
"keySize": 128,
"ivSize": 16,
"defaultParams": {
"iv": "1234567890123456"
}
},
“preferences”: {
“encrypt”: true,
“autoBackup”: false
}
实现原理详解
加密存储机制:
使用AES-128算法加密密码数据
主密码派生加密密钥(实际项目应使用PBKDF2等安全算法)
加密数据后再进行Base64编码存储
分布式同步安全:
启用分布式数据库的加密选项
敏感数据在本地加密后再同步
只有知道主密码的设备才能解密数据
身份验证流程:
主密码或生物识别认证解锁
认证成功后初始化加密服务
会话期间保持加密状态
数据同步流程:
本地修改后加密数据并同步
接收远程更新后验证数据完整性
显示数据来源设备信息
扩展功能建议
密码强度检测:
// 检测密码强度
function checkPasswordStrength(password: string): number {
let strength = 0;
if (password.length >= 8) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^A-Za-z0-9]/.test(password)) strength++;
return strength;
密码生成器:
// 生成随机密码
function generatePassword(length: number = 12): string {
const chars = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()’;
let result = ‘’;
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
return result;
自动填充功能:
// 自动填充密码到应用
async autoFillPassword(entry: PasswordEntry) {
const password = this.cryptoService.decrypt(entry.encryptedPassword);
// 调用系统API实现自动填充
总结
本文展示了如何利用HarmonyOS的安全存储和分布式能力构建一个加密的跨设备密码管理器。通过本地加密后再同步的策略,确保了用户密码在存储和传输过程中的安全性。这种架构不仅适用于密码管理,也可以扩展到其他需要安全同步敏感数据的场景。
鸿蒙的安全框架为开发者提供了强大的工具,使安全应用的开发变得更加简单可靠。合理利用这些能力,可以构建出既方便又安全的跨设备应用体验。
