Android调用SIM卡Applet触发java.lang.SecurityException问题求助
解决Android非Root应用访问SIM卡Applet的SecurityException问题
为什么Telephony API走不通
android.permission.MODIFY_PHONE_STATE是signature|privileged级别的系统权限,只有带系统签名的应用或预装在/system/priv-app目录的应用才能获取,普通应用常规安装根本拿不到,所以这个路子直接放弃。
OMAPI的正确用法(普通应用可用)
OMAPI(Open Mobile API)是Android官方提供的安全元素访问标准API,普通应用只要配置正确就能用,不用Root。
1. 清单文件配置
在AndroidManifest.xml里加必要的权限和服务声明:
<!-- NFC权限(部分设备需要) --> <uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="false" /> <!-- 部分设备需要的安全设置权限 --> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <!-- OMAPI服务声明 --> <service android:name=".SeAccessService" android:exported="true" android:permission="com.android.se.permission.SECURE_ELEMENT_SERVICE"> <intent-filter> <action android:name="android.se.omapi.SE_SERVICE" /> </intent-filter> </service>
2. 核心实现代码
import android.se.omapi.Reader; import android.se.omapi.SEService; import android.se.omapi.Session; import android.se.omapi.Channel; import android.content.Context; public class SeAccessManager { private SEService seService; public void init(Context context) { // 初始化SE服务,回调在后台线程执行 seService = new SEService(context, new SEService.CallBack() { @Override public void serviceConnected() { // 遍历读卡器,找到SIM卡对应的设备 for (Reader reader : seService.getReaders()) { if (reader.getName().contains("SIM")) { accessSimApplet(reader); break; } } } @Override public void serviceDisconnected() { // 服务断开后的清理 if (seService != null) { seService.shutdown(); seService = null; } } }); } private void accessSimApplet(Reader reader) { try { // 打开会话 Session session = reader.openSession(); // 替换成你的Applet AID(字节数组格式) byte[] appletAid = new byte[]{0xA0, 0x00, 0x00, 0x00, 0x01, 0x01}; // 打开逻辑通道 Channel channel = session.openLogicalChannel(appletAid); // 发送APDU指令,替换成你的业务指令 byte[] apduCommand = new byte[]{0x00, 0xA4, 0x04, 0x00, 0x06, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x01}; byte[] response = channel.transmit(apduCommand); // 处理返回结果,转十六进制方便查看 String respHex = bytesToHex(response); // TODO: 这里处理你的业务逻辑 // 关闭资源,避免内存泄漏 channel.close(); session.close(); } catch (Exception e) { e.printStackTrace(); } } // 字节数组转十六进制字符串的工具方法 private String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString().trim(); } }
3. 避坑要点
- AID必须准确:传入的AID必须和SIM卡上Applet的AID完全一致,格式为字节数组,错一个字节都可能触发权限异常。
- 权限补充:部分设备需要
WRITE_SECURE_SETTINGS权限,普通应用可以通过ADB命令授予:adb shell pm grant com.your.package.name android.permission.WRITE_SECURE_SETTINGS - 线程注意:OMAPI的所有操作都不能在主线程执行,必须放在后台线程(回调本身就在后台线程,所以上面的代码没问题),否则不仅会ANR,还可能触发权限相关异常。
- 设备兼容性:运营商定制ROM可能会限制第三方应用访问SIM卡Applet,这种情况只能联系运营商解除限制,没有通用绕过方法。
备用方案:SIM卡工具包(STK)
如果OMAPI被严格限制,可以尝试通过STK接口交互,但STK的功能依赖运营商支持,且普通应用只能调用系统提供的STK交互接口,无法直接发送自定义APDU,开发复杂度较高。
内容的提问来源于stack exchange,提问作者alireza jamshidi
相关产品推荐
相关产品推荐

