鸿蒙安全密码键盘组件开发指南 原创

进修的泡芙
发布于 2025-6-20 11:37
浏览
0收藏

鸿蒙安全密码键盘组件开发指南

一、安全键盘设计原理

基于HarmonyOS的安全键盘组件设计,遵循以下安全原则:
防截屏/录屏:通过设置窗口安全标志防止内容泄露

随机布局:每次显示键盘时按键位置随机变化

内存安全:密码信息仅在安全内存区域处理
跨设备同步:通过分布式安全通道同步键盘状态

!https://example.com/harmony-secure-keyboard-arch.png

二、核心代码实现
安全键盘服务(ArkTS)

// SecureKeyboardService.ets
import window from ‘@ohos.window’;
import display from ‘@ohos.display’;
import distributedData from ‘@ohos.distributedData’;

const KEYBOARD_CHANNEL = ‘secure_keyboard_channel’;
const KEY_SET = [‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’];

class SecureKeyboardService {
private static instance: SecureKeyboardService = null;
private dataManager: distributedData.DataManager;
private currentWindow: window.Window;
private keyboardListeners: KeyboardListener[] = [];
private currentLayout: string[] = [];
private secureArea: SecureMemoryArea;

private constructor() {
this.initDataManager();
this.initSecureArea();
public static getInstance(): SecureKeyboardService {

if (!SecureKeyboardService.instance) {
  SecureKeyboardService.instance = new SecureKeyboardService();

return SecureKeyboardService.instance;

private initDataManager(): void {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.securekeyboard',
  area: distributedData.Area.GLOBAL,
  isEncrypted: true
});

private initSecureArea(): void {

this.secureArea = new SecureMemoryArea(1024); // 1KB安全内存区域

public async showKeyboard(context: common.UIAbilityContext): Promise<void> {

try {
  // 创建安全窗口
  this.currentWindow = await window.create(context, 'secure_keyboard', window.WindowType.TYPE_INPUT_METHOD);
  
  // 设置安全标志
  await this.currentWindow.setWindowPrivacyMode(true);
  await this.currentWindow.setWindowSecure(true);
  
  // 生成随机键盘布局
  this.generateRandomLayout();
  
  // 加载键盘UI
  await this.currentWindow.loadContent('pages/SecureKeyboard');
  
  // 显示键盘
  await this.currentWindow.show();
  
  // 同步键盘状态到其他设备
  this.syncKeyboardState('show');

catch (err) {

  console.error('显示安全键盘失败:', JSON.stringify(err));

}

public async hideKeyboard(): Promise<void> {
if (!this.currentWindow) return;

try {
  await this.currentWindow.hide();
  await this.currentWindow.destroy();
  this.currentWindow = null;
  
  // 同步键盘状态到其他设备
  this.syncKeyboardState('hide');

catch (err) {

  console.error('隐藏安全键盘失败:', JSON.stringify(err));

}

private generateRandomLayout(): void {
// 复制并打乱键位
this.currentLayout = […KEY_SET];
this.shuffleArray(this.currentLayout);

// 添加干扰键
this.currentLayout.push('✖');
this.currentLayout.unshift('◀');

// 存储到安全区域
this.secureArea.store('currentLayout', this.currentLayout);

private shuffleArray(array: any[]): void {

for (let i = array.length - 1; i > 0; i--) {
  const j = Math.floor(Math.random() * (i + 1));
  [array[i], array[j]] = [array[j], array[i]];

}

public handleKeyPress(key: string): void {
if (key === ‘✖’) {
// 干扰键不做处理
return;
if (key === ‘◀’) {

  // 退格键
  this.notifyKeyEvent('backspace');
  return;

// 验证键位合法性

if (KEY_SET.includes(key)) {
  // 存储到安全区域
  const secureValue = this.secureArea.process(key);
  this.notifyKeyEvent('input', secureValue);

}

private syncKeyboardState(state: ‘show’ | ‘hide’): void {
this.dataManager.syncData(KEYBOARD_CHANNEL, {
type: ‘keyboardState’,
state: state,
timestamp: Date.now()
});
public addListener(listener: KeyboardListener): void {

if (!this.keyboardListeners.includes(listener)) {
  this.keyboardListeners.push(listener);

}

public removeListener(listener: KeyboardListener): void {
this.keyboardListeners = this.keyboardListeners.filter(l => l !== listener);
private notifyKeyEvent(type: ‘input’ | ‘backspace’, value?: string): void {

this.keyboardListeners.forEach(listener => {
  if (type === 'input' && value) {
    listener.onKeyInput(value);

else if (type === ‘backspace’) {

    listener.onBackspace();

});

public getCurrentLayout(): string[] {

return [...this.currentLayout];

}

interface KeyboardListener {
onKeyInput(value: string): void;
onBackspace(): void;
// 安全内存区域实现

class SecureMemoryArea {
private memory: ArrayBuffer;
private view: DataView;

constructor(size: number) {
this.memory = new ArrayBuffer(size);
this.view = new DataView(this.memory);
store(key: string, value: any): void {

// 实际实现应使用更安全的存储方式
const encoded = JSON.stringify(value);
const encoder = new TextEncoder();
const data = encoder.encode(encoded);

for (let i = 0; i < data.length; i++) {
  this.view.setUint8(i, data[i]);

}

retrieve(key: string): any {
// 从安全内存读取数据
const bytes = [];
for (let i = 0; i < this.memory.byteLength; i++) {
const byte = this.view.getUint8(i);
if (byte === 0) break;
bytes.push(byte);
const decoder = new TextDecoder();

const decoded = decoder.decode(new Uint8Array(bytes));
return JSON.parse(decoded);

process(value: string): string {

// 对输入值进行安全处理
return secure_{value}_{Date.now()};

}

export const keyboardService = SecureKeyboardService.getInstance();

安全键盘UI组件(ArkUI)

// SecureKeyboard.ets
import { keyboardService } from ‘./SecureKeyboardService’;

@Component
export struct SecureKeyboard {
@State keyLayout: string[] = [];

aboutToAppear() {
this.keyLayout = keyboardService.getCurrentLayout();
build() {

Column() {
  // 键盘标题
  Text('安全键盘')
    .fontSize(18)
    .margin({ bottom: 20 })
  
  // 键盘主体
  Grid() {
    ForEach(this.keyLayout, (key) => {
      GridItem() {
        Button(key)
          .width('90%')
          .height(60)
          .fontSize(24)
          .backgroundColor(Color.White)
          .onClick(() => {
            keyboardService.handleKeyPress(key);
          })

.margin(5)

    })

.columnsTemplate(‘1fr 1fr 1fr’)

  .rowsTemplate('1fr 1fr 1fr 1fr')
  .columnsGap(10)
  .rowsGap(10)
  .width('100%')
  .height(300)
  
  // 关闭按钮
  Button('关闭键盘')
    .width('80%')
    .height(50)
    .margin({ top: 20 })
    .onClick(() => {
      keyboardService.hideKeyboard();
    })

.width(‘100%’)

.height('100%')
.padding(20)
.backgroundColor('#F5F5F5')

}

密码输入框组件(ArkUI)

// PasswordInput.ets
import { keyboardService } from ‘./SecureKeyboardService’;

@Component
export struct PasswordInput {
@State password: string = ‘’;
@State isKeyboardVisible: boolean = false;
@State showPassword: boolean = false;
@State dots: string = ‘’;

private maxLength: number = 6;

build() {
Column() {
// 密码显示区域
Row() {
if (this.showPassword) {
Text(this.password)
.fontSize(24)
.fontWeight(FontWeight.Bold)
else {

      Text(this.dots)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

Button(this.showPassword ? ‘隐藏’ : ‘显示’)

      .width(60)
      .height(30)
      .margin({ left: 10 })
      .onClick(() => {
        this.showPassword = !this.showPassword;
      })

.margin({ bottom: 20 })

  // 输入框
  TextInput({ placeholder: '点击输入密码' })
    .width('80%')
    .height(50)
    .type(InputType.Password)
    .onClick(() => {
      this.toggleKeyboard();
    })
    .enabled(false) // 禁用系统键盘
  
  // 键盘切换按钮
  Button(this.isKeyboardVisible ? '隐藏键盘' : '显示安全键盘')
    .width('80%')
    .height(50)
    .margin({ top: 20 })
    .onClick(() => {
      this.toggleKeyboard();
    })

.width(‘100%’)

.height('100%')
.padding(20)
.onAppear(() => {
  keyboardService.addListener({
    onKeyInput: (value) => {
      if (this.password.length < this.maxLength) {
        this.password += value;
        this.updateDots();

},

    onBackspace: () => {
      this.password = this.password.slice(0, -1);
      this.updateDots();

});

})
.onDisappear(() => {
  keyboardService.removeListener({
    onKeyInput: () => {},
    onBackspace: () => {}
  });
})

private toggleKeyboard(): void {

if (this.isKeyboardVisible) {
  keyboardService.hideKeyboard();

else {

  keyboardService.showKeyboard(getContext(this) as common.UIAbilityContext);

this.isKeyboardVisible = !this.isKeyboardVisible;

private updateDots(): void {

this.dots = '•'.repeat(this.password.length);

}

三、跨设备同步实现
键盘状态同步服务

// KeyboardSyncService.ets
import distributedData from ‘@ohos.distributedData’;

class KeyboardSyncService {
private static instance: KeyboardSyncService = null;
private dataManager: distributedData.DataManager;
private syncListeners: SyncListener[] = [];

private constructor() {
this.initDataManager();
public static getInstance(): KeyboardSyncService {

if (!KeyboardSyncService.instance) {
  KeyboardSyncService.instance = new KeyboardSyncService();

return KeyboardSyncService.instance;

private initDataManager(): void {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.securekeyboard',
  area: distributedData.Area.GLOBAL,
  isEncrypted: true
});

// 注册数据监听
this.dataManager.registerDataListener('keyboard_sync', (data) => {
  this.handleSyncData(data);
});

public syncInputState(deviceId: string, state: ‘show’ | ‘hide’): void {

this.dataManager.syncData('keyboard_sync', {
  type: 'inputState',
  deviceId: deviceId,
  state: state,
  timestamp: Date.now()
});

public syncPasswordField(fieldId: string, value: string): void {

const encrypted = this.encryptValue(value);
this.dataManager.syncData('keyboard_sync', {
  type: 'passwordField',
  fieldId: fieldId,
  value: encrypted,
  timestamp: Date.now()
});

private handleSyncData(data: any): void {

if (!data) return;

switch (data.type) {
  case 'inputState':
    this.handleInputStateSync(data);
    break;
  case 'passwordField':
    this.handlePasswordFieldSync(data);
    break;

}

private handleInputStateSync(data: any): void {
this.syncListeners.forEach(listener => {
listener.onInputStateChange(data.deviceId, data.state);
});
private handlePasswordFieldSync(data: any): void {

const decrypted = this.decryptValue(data.value);
this.syncListeners.forEach(listener => {
  listener.onPasswordFieldUpdate(data.fieldId, decrypted);
});

private encryptValue(value: string): string {

// 实际实现应使用更安全的加密算法
return btoa(value);

private decryptValue(value: string): string {

// 实际实现应使用更安全的加密算法
return atob(value);

public addListener(listener: SyncListener): void {

if (!this.syncListeners.includes(listener)) {
  this.syncListeners.push(listener);

}

public removeListener(listener: SyncListener): void {
this.syncListeners = this.syncListeners.filter(l => l !== listener);
}

interface SyncListener {
onInputStateChange(deviceId: string, state: ‘show’ | ‘hide’): void;
onPasswordFieldUpdate(fieldId: string, value: string): void;
export const syncService = KeyboardSyncService.getInstance();

跨设备密码输入同步

// MultiDevicePasswordInput.ets
import { syncService } from ‘./KeyboardSyncService’;

@Component
export struct MultiDevicePasswordInput {
@State localPassword: string = ‘’;
@State remotePasswords: { [deviceId: string]: string } = {};
@State activeDevices: { deviceId: string, name: string }[] = [];

private fieldId: string = ‘password_field_’ + Date.now();

build() {
Column() {
// 本地密码输入
PasswordInput()
.onPasswordChange((value) => {
this.localPassword = value;
syncService.syncPasswordField(this.fieldId, value);
})
.margin({ bottom: 30 })

  // 远程设备密码状态
  if (this.activeDevices.length > 0) {
    Text('其他设备输入状态:')
      .fontSize(16)
      .margin({ bottom: 10 })
    
    ForEach(this.activeDevices, (device) => {
      Row() {
        Text(device.name)
          .fontSize(14)
          .layoutWeight(1)
        
        Text(this.remotePasswords[device.deviceId] ? '已输入' : '未输入')
          .fontSize(14)
          .fontColor(this.remotePasswords[device.deviceId] ? '#228B22' : '#999999')

.margin({ bottom: 5 })

    })

}

.onAppear(() => {
  syncService.addListener({
    onInputStateChange: (deviceId, state) => {
      this.updateDeviceState(deviceId, state);
    },
    onPasswordFieldUpdate: (fieldId, value) => {
      if (fieldId === this.fieldId) {
        // 忽略自己的更新
        if (!value.startsWith('local_')) {
          this.remotePasswords = {
            ...this.remotePasswords,
            [fieldId]: value
          };

}

});

})
.onDisappear(() => {
  syncService.removeListener({
    onInputStateChange: () => {},
    onPasswordFieldUpdate: () => {}
  });
})

private updateDeviceState(deviceId: string, state: ‘show’ | ‘hide’): void {

if (state === 'show') {
  if (!this.activeDevices.some(d => d.deviceId === deviceId)) {
    this.activeDevices = [
      ...this.activeDevices,

deviceId, name: 设备${this.activeDevices.length + 1} }

    ];

} else {

  this.activeDevices = this.activeDevices.filter(d => d.deviceId !== deviceId);
  const newRemotePasswords = { ...this.remotePasswords };
  delete newRemotePasswords[deviceId];
  this.remotePasswords = newRemotePasswords;

}

四、安全增强措施
键盘布局混淆算法

// 在SecureKeyboardService中添加增强混淆
class SecureKeyboardService {
private enhancedShuffle(): void {
// 获取设备方向
const displayInfo = display.getDefaultDisplaySync();
const isPortrait = displayInfo.height > displayInfo.width;

// 根据时间戳生成种子
const seed = Date.now() % 1000;
const rng = this.createSeededRNG(seed);

// Fisher-Yates洗牌算法增强版
for (let i = this.currentLayout.length - 1; i > 0; i--) {
  const j = Math.floor(rng() * (i + 1));
  [this.currentLayout[i], this.currentLayout[j]] = 
    [this.currentLayout[j], this.currentLayout[i]];

// 根据设备方向调整布局

if (isPortrait) {
  // 竖屏时数字在下方
  this.currentLayout = [
    ...this.currentLayout.slice(6),
    ...this.currentLayout.slice(0, 6)
  ];

else {

  // 横屏时数字在右侧
  const left = [], right = [];
  this.currentLayout.forEach((key, index) => {
    (index % 2 === 0 ? left : right).push(key);
  });
  this.currentLayout = [...left, ...right];

}

private createSeededRNG(seed: number): () => number {
let s = seed;
return () => {
= Math.sin(s) * 10000;

  return s - Math.floor(s);
};

}

安全内存管理增强

// 增强SecureMemoryArea实现
class SecureMemoryArea {
private memory: WebAssembly.Memory;
private module: WebAssembly.Instance;

constructor(size: number) {
// 使用WebAssembly分配安全内存
this.memory = new WebAssembly.Memory({ initial: 1 });

// 加载安全处理模块
const importObject = {
  env: {
    memory: this.memory

};

// 实际应用中应从安全位置加载wasm模块
WebAssembly.instantiateStreaming(fetch('secure.wasm'), importObject)
  .then(obj => {
    this.module = obj.instance;
  });

store(key: string, value: any): void {

const encoded = JSON.stringify(value);
const encoder = new TextEncoder();
const data = encoder.encode(encoded);

// 调用wasm安全存储函数
const ptr = this.module.exports.allocate(data.length);
const mem = new Uint8Array(this.memory.buffer, ptr, data.length);
mem.set(data);

this.module.exports.secure_store(key, ptr, data.length);

retrieve(key: string): any {

// 调用wasm安全读取函数
const ptr = this.module.exports.secure_retrieve(key);
const length = this.module.exports.get_length(key);

if (ptr = 0 || length = 0) return null;

const mem = new Uint8Array(this.memory.buffer, ptr, length);
const decoder = new TextDecoder();
return JSON.parse(decoder.decode(mem));

process(value: string): string {

// 调用wasm安全处理函数
const encoder = new TextEncoder();
const data = encoder.encode(value);

const ptr = this.module.exports.allocate(data.length);
const mem = new Uint8Array(this.memory.buffer, ptr, data.length);
mem.set(data);

const outPtr = this.module.exports.secure_process(ptr, data.length);
const outLength = this.module.exports.get_output_length();

const outMem = new Uint8Array(this.memory.buffer, outPtr, outLength);
const decoder = new TextDecoder();
return decoder.decode(outMem);

}

五、项目配置
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.INPUT_METHOD_MANAGER”,

    "reason": "安全键盘输入管理"
  },

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "跨设备密码同步"
  },

“name”: “ohos.permission.INTERNET”,

    "reason": "安全验证服务"
  },

“name”: “ohos.permission.ACCESS_NETWORK_STATE”,

    "reason": "检查网络连接状态"
  },

“name”: “ohos.permission.GET_SENSITIVE_PERMISSIONS”,

    "reason": "检测敏感权限使用"
  },

“name”: “ohos.permission.PRIVACY_WINDOW”,

    "reason": "设置隐私窗口"
  },

“name”: “ohos.permission.SECURE_WINDOW”,

    "reason": "设置安全窗口"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true
  },

“name”: “SecureKeyboardAbility”,

    "type": "service",
    "backgroundModes": ["dataTransfer"]

]

}

WASM模块配置

// build-profile.json5
“apiType”: “web”,

“buildOption”: {
“wasm”: true,
“sourceLang”: “es5”,
“targetArk”: “es5”
}

六、总结

通过本安全键盘组件的实现,我们掌握了以下HarmonyOS核心技术:
安全窗口管理:通过@ohos.window实现防截屏/录屏

分布式数据同步:使用@ohos.distributedData加密通道

安全内存管理:结合WebAssembly实现敏感数据处理

输入安全防护:随机键盘布局和输入混淆技术

跨设备交互:多设备密码输入状态同步

安全键盘可应用于以下场景:
金融类App密码输入

企业VPN认证

支付系统验证

多设备协同认证

高安全等级系统登录

扩展方向:
生物特征集成(指纹/面部识别)

硬件级安全支持(TEE/SE)

动态安全策略调整

键盘主题和布局自定义

输入行为分析和异常检测

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-6-20 11:43:44修改
收藏
回复
举报
回复
    相关推荐