鸿蒙跨端密码管理器:安全加密与多设备同步方案 原创

进修的泡芙
发布于 2025-6-18 21:14
浏览
0收藏

鸿蒙跨端密码管理器:安全加密与多设备同步方案

本文将基于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的安全存储和分布式能力构建一个加密的跨设备密码管理器。通过本地加密后再同步的策略,确保了用户密码在存储和传输过程中的安全性。这种架构不仅适用于密码管理,也可以扩展到其他需要安全同步敏感数据的场景。

鸿蒙的安全框架为开发者提供了强大的工具,使安全应用的开发变得更加简单可靠。合理利用这些能力,可以构建出既方便又安全的跨设备应用体验。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐