1# 安全单元访问开发指南 2 3<!--Kit: Connectivity Kit--> 4<!--Subsystem: Communication--> 5<!--Owner: @amunra03--> 6<!--Designer: @wenxiaolin--> 7<!--Tester: @zs_111--> 8<!--Adviser: @zhang_yixin13--> 9 10## 简介 11电子设备上可能存在一个或多个安全单元(SecureElement,简称SE),比如有eSE(Embedded SE)和SIM卡。安全单元的访问控制,通过GPAC(GlobalPlatform Access Control)规范实现。 12 13## 场景介绍 14应用程序可以通过接口访问安全单元,比如往安全单元里面写入数据,实现在电子设备上模拟一张NFC卡片的目的。该卡片数据可能存储在eSE安全单元,或在SIM卡安全单元上。安全单元上一般会预置有访问控制规则,应用程序需要具备对应的权限,也就是通过安全单元的访问控制权限校验之后,才能正常访问安全单元。 15 16## 接口说明 17安全单元完整的API说明以及示例代码请参考:[安全单元接口](../../reference/apis-connectivity-kit/js-apis-secureElement.md)。 18 19实现安全单元的访问,可能使用到下面的接口。 20 21| 接口名 | 功能描述 | 22| ---------------------------------- | ------------------------------------------------------------------------------ | 23| createService(): Promise\<SEService> | 建立一个可用于连接到系统中所有可用SE的新连接。 | 24| getReaders(): Reader[] | 返回可用SE Reader的数组,包含该设备上支持的所有的安全单元。 | 25| openSession(): Session | 在SE Reader实例上创建连接会话,返回Session实例。 | 26| openLogicalChannel(aid: number[]): Promise\<Channel> | 打开逻辑通道,返回逻辑Channel实例对象。 | 27| transmit(command: number[]): Promise\<number[]> | 向SE发送APDU数据 | 28| close(): void | 关闭Channel。 | 29 30 31## 主要场景开发步骤 32 33### 应用程序访问安全单元 341. import需要的安全单元模块。 352. 判断设备是否支持安全单元能力。 363. 访问安全单元,实现数据的读取或写入。 374. 释放通道资源。 38 39```ts 40import { omapi } from '@kit.ConnectivityKit'; 41import { BusinessError } from '@kit.BasicServicesKit'; 42import { hilog } from '@kit.PerformanceAnalysisKit'; 43import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 44 45let seService : omapi.SEService; 46let seReaders : omapi.Reader[]; 47let seSession : omapi.Session; 48let seChannel : omapi.Channel; 49let testSelectedAid : number[] = [0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10]; 50let p2 : number = 0x00; 51 52export default class EntryAbility extends UIAbility { 53 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 54 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 55 56 // 判断设备是否支持安全单元能力 57 if (!canIUse("SystemCapability.Communication.SecureElement")) { 58 hilog.error(0x0000, 'testTag', 'secure element unavailable.'); 59 return; 60 } 61 hilog.info(0x0000, 'testTag', 'secure element available.'); 62 this.omaTest(); 63 } 64 65 private async omaTest () { 66 // 创建安全单元service,用于访问安全单元 67 await omapi.createService().then((data) => { 68 if (data == undefined || !data.isConnected()) { 69 hilog.error(0x0000, 'testTag', 'secure element service disconnected.'); 70 return; 71 } 72 seService = data; 73 hilog.info(0x0000, 'testTag', 'secure element service connected.'); 74 }).catch((error: BusinessError) => { 75 hilog.error(0x0000, 'testTag', 'createService error %{public}s', JSON.stringify(error)); 76 return; 77 }); 78 79 // 获取设备上所有支持的readers,即所有的安全单元列表 80 try { 81 seReaders = seService.getReaders(); 82 } catch (error) { 83 hilog.error(0x0000, 'testTag', 'getReaders error %{public}s', JSON.stringify(error)); 84 } 85 if (seReaders == undefined || seReaders.length == 0) { 86 hilog.error(0x0000, 'testTag', 'no valid reader found.'); 87 seService.shutdown(); 88 return; 89 } 90 91 // 根据业务需求,选择一个安全单元来访问,比如选择eSE或SIM 92 let reader: (omapi.Reader | undefined); 93 for (let i = 0; i < seReaders.length; ++i) { 94 let r = seReaders[i]; 95 // 安全单元的Name来区分,比如是eSE或SIM 96 if (r.getName().includes("SIM")) { 97 reader = r; 98 break; 99 } 100 } 101 if (reader == undefined) { 102 hilog.error(0x0000, 'testTag', 'no valid sim reader.'); 103 seService.shutdown(); 104 return; 105 } 106 hilog.info(0x0000, 'testTag', 'reader is %{public}s', reader?.getName()); 107 108 // 在选定的一个安全单元实例上,打开一个会话session 109 try { 110 seSession = reader?.openSession() as omapi.Session; 111 } catch (error) { 112 hilog.error(0x0000, 'testTag', 'openSession error %{public}s', JSON.stringify(error)); 113 } 114 if (seSession == undefined) { 115 hilog.error(0x0000, 'testTag', 'seSession invalid.'); 116 seService.shutdown(); 117 return; 118 } 119 120 // 通过会话session实例,创建逻辑通道或基础通道,一般选择逻辑通道访问,因为基础通道可能是受限的 121 try { 122 // testSelectedAid 根据实际业务,修改为打开逻辑通道的应用的aid值 123 seChannel = await seSession.openLogicalChannel(testSelectedAid, p2); 124 } catch (exception) { 125 hilog.error(0x0000, 'testTag', 'openLogicalChannel exception %{public}s', JSON.stringify(exception)); 126 } 127 128 if (seChannel == undefined) { 129 hilog.error(0x0000, 'testTag', 'seChannel invalid.'); 130 seService.shutdown(); 131 return; 132 } 133 134 // 使用通道发送APDU数据到安全单元,testApduData根据实际业务,修改为正确的业务数据值。所填充的APDU数据格式,需要符合APDU规范。 135 let testApduData = [0x01, 0x02, 0x03, 0x04]; 136 try { 137 let response: number[] = await seChannel.transmit(testApduData); 138 hilog.info(0x0000, 'testTag', 'seChannel.transmit() response = %{public}s.', JSON.stringify(response)); 139 } catch (exception) { 140 hilog.error(0x0000, 'testTag', 'seChannel.transmit() exception = %{public}s.', JSON.stringify(exception)); 141 } 142 143 // 通道访问结束后,必须确保通道资源是关闭的 144 try { 145 seChannel.close(); 146 } catch (exception) { 147 hilog.error(0x0000, 'testTag', 'seChannel.close() exception = %{public}s.', JSON.stringify(exception)); 148 } 149 150 // 关闭服务资源,关闭应用程序和安全单元服务的绑定关系 151 seService.shutdown(); 152 } 153} 154 155```