# SKF C语言接入方案
# 1 文档说明
# 1.1 使用对象
接入SKF 产品以 C语言接入的第三方应用相关人员
# 1.2 版本信息
| 版本信息 | 版本信息 | 修订人 | 修订内容 |
|---|---|---|---|
| 3.3 | 2023-03-26 | SIMKEY团队 |
# 2 术语及缩略语定义
下列术语、定义和缩略语适用于本标准
| 名词 | 解释 |
|---|---|
| API | Application Programming Interface,应用程序编程接口 |
| LTE | 3GPP Long Term Evolution,3GPP长期演进技术 |
| VPN | Virtual Private Network,虚拟专用网络 |
| OA | Office Automation,办公自动化 |
| OTA | Over The Air |
| SDK | SoftwareDevelopment Kit,软件开发工具包 |
| OMA | Open Mobile API |
| UICC | Universal Integrated Circuit Card |
| CRM | Customer Relationship Management |
- 访问控制执行器(access control enforcer)
访问控制执行器,组成安全模块访问 API 的部分软件,它从安全模块上获取访问规则,并运用这些规则来控制设备应用,对于安全模块上各种应用程序的访问。
- 终端设备(mobile device)
本规范中特指任何包含安全模块的终端设备,如移动电话。
- 开放移动设备操作系统(open mobile OS)
允许加载第三方应用程序的移动设备操作系统
- 安全模块(secure element)
可用于移动设备的智能卡芯片。例如 UICC 卡/SIM 卡、嵌入式安全模块、高 安全 SD 卡等
- 安全模块应用(applet)
一个安装并运行在安全模块里面的应用。例如一个 Java 卡应用或一个 Native 卡应用。为与设备应用区别,本文档中统一称为“Applet”。
- 终端应用(dev ice a pplication)
一个安装并运行于移动设备上的应用程序,简称“应用”。本文中的手机终端 应用特指集成 CTPass SDK 包的手机应用程序。
- 会话(session)
设备(例如手机终端)应用和安全模块之间打开的一个连接。
- 通道(channel)
设备(例如手机终端)应用和 Applet 之间打开的一个连接。
# 3 Android Studio 工程配置
# 3.1 在项目中集成SDK
# 3.1.1 将 libcissskf.aar 拷贝到工程 module 的 libs 目录下
# 3.1.2 将 module 的 gradle 文件中添加依赖
# 一般配置
repositories {
flatDir {
dirs 'libs' // aar目录
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
implementation files('libs/libcissskf.aar')
}
2
3
4
5
6
7
8
9
10
11
# 误报配置
编译工程后运行时如果报: More than one file was found with OS independent path...错误 可以增加以下配置: gradle中的 android 标签下配置此选项(匹配到多个相同文件,只提取第一个,防止Android studio 误报)
packagingOptions {
pickFirst 'lib/armeabi-v7a/libCissSkfApi.so'
pickFirst 'lib/arm64-v8a/libCissSkfApi.so'
}
2
3
4
# 3.1.3 将 so 文件添加到相关模块 src\main\jniLibs 目录下
相关模块->需要调用so文件的module
# 3.1.4 将 .h文件添加到 module 的 src\main\cpp\prebuild${ANDROID_ABI}\include\CissSkfApi 目录下


# 3.1.5 在 CMakeLists.txt 文件中添加配置
set(jniLibs ${CMAKE_SOURCE_DIR}/../jniLibs)
add_library(cissskfapi SHARED IMPORTED)
set_target_properties(cissskfapi PROPERTIES IMPORTED_LOCATION
${jniLibs}/${ANDROID_ABI}/libCissSkfApi.so)
include_directories(${CMAKE_SOURCE_DIR}/prebuild/${ANDROID_ABI}/include)
target_link_libraries(CissSKFTest
cissskfapi
android
log)
2
3
4
5
6
7
8
9
# 3.1.6 在 module 的 build.gradle 配置
android {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
sourceSets {
main {
java.srcDirs = ['src/main/java']
jni.srcDirs = ['src/main/cpp']
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
2
3
4
5
6
7
8
9
10
11
12
# 3.1.7 在 module 的 build.gradle 配置其他依赖
implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
# 3.1.8 在 module 的 AndroidManifest.xml 配置 openmobileapi
在 application 节点里面面添加
<uses-library android:name="org.simalliance.openmobileapi" android:required="false"/>
如下图:
2
3
# 3.1.9 添加权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.SMARTCARD" />
<uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2
3
4
5
6
7
8
9
如果是 targetSdkVersion > = 23 时, 有的权限需要动态申请
# 3.2 应用混淆
## ----------------------------------
## ########## CISS SKF ##########
## ----------------------------------
-keep class com.simkey.sec.libciss.card.skf.** {*;}
-keep class com.simkey.sec.libciss.card.apdu.OMAAPDUSenderForSKF {*;}
## ----------------------------------
## ########## oma ##########
## ----------------------------------
-keep class org.simalliance.openmobileapi.** { *; }
-dontwarn org.simalliance.openmobileapi.**
2
3
4
5
6
7
8
9
10
保证skf的sdk不被混淆, 否则无法和 SIM 卡进行交互
# 3.3 调用SKF SDK
# 3.3.1 在 Application 里面的初始化
在 onCreate 方法中添加 CISSSKFInit.init 初始化方法进行初始化, 如果不进行此步骤则无法发送指令到SIM卡 参数:
| 表头 | 表头 | 表头 | 表头 |
|---|---|---|---|
| context | Context | 上下文 | |
| scanBleNow | boolean | 单元格 | 是否立即扫描蓝牙设备, 如果使用”pcsc:Auto”来连接卡, 那么建议开启扫描. 注意: android api 23 及以上需要 app 进行动态权限申请 |
调用如下:
public class MyApp extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
boolean scanBleNow = false;//是否进行蓝牙扫描,否表示不进行蓝牙扫描
CISSSKFInit.init(this, scanBleNow);
//初始化之后可以设置日志显示与否, 默认不显示
OMAAPDUSenderForSKF.getInstance().setLogAble(true);
}
}
2
3
4
5
6
7
8
9
10
11
# 3.3.2 在 C 语言中的初始化
先添加头文件
#include <CissSkfApi/SkfApi.h>
#include <CissSkfApi/JavaVMContext.h>
2
在C语言的jni初始化时会调用JNI_OnLoad这个方法, 相当于 Application类的onCreate 方法, 在 JNI_OnLoad 方法里面进行调用: int result = OnJNILoad(vm, reserved);
如果不进行此操作会造成 app 闪退
# 3.3.3 在 C 语言中的使用
完成上述各个步骤之后可以对 skf 接口进行调用了, 例子如下
#include <CissSkfApi/SkfApi.h>
#include <CissSkfApi/JavaVMContext.h>
extern "C"
JNIEXPORT jint JNICALL
Java_com_simkey_sec_ciss_skf_SKFTest_Test_1SKF_1EnumDev(JNIEnv *env,jobject instance,jboolean bPresent,jobject outSzNameList,jobject inoutPulSize) {
//枚举
BOOL c_bPresent = bPresent;
char *c_outSzNameList = (char *) malloc(1024);
//一定要要初始化, 否则导致下面的 setMyString 设值失败
memset(c_outSzNameList, '\0', 1024);
ULONG c_inoutPulSize = 1024;
ULONG result = SKF_EnumDev(c_bPresent, c_outSzNameList, &c_inoutPulSize);
LOGD("SKF_EnumDev c_outSzNameList is:%s", c_outSzNameList);
free(c_outSzNameList);
c_outSzNameList = NULL;
if (result != SAR_OK) {
return result;
}
//连接
char *c_szName = "pcsc:Auto";
DEVHANDLE c_outPhDev = NULL;
result = SKF_ConnectDev(c_szName, &c_outPhDev);
if (result != SAR_OK) {
return result;
}
LOGD("SKF_ConnectDev ok");
//枚举应用列表
char *c_outSzAppName = (char *) malloc(1024);
//一定要要初始化, 否则导致下面的 setMyString 设值失败
memset(c_outSzAppName, '\0', 1024);
c_inoutPulSize = 1024;
result = SKF_EnumApplication(c_outPhDev, c_outSzAppName, &c_inoutPulSize);
LOGD("SKF_EnumApplication application name list is:%s", c_outSzAppName);
free(c_outSzAppName);
c_outSzAppName = NULL;
if (result != SAR_OK) {
return result;
}
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 3.3.4 日志
默认不打印日志, 如果需要则添加代码:
OMAAPDUSenderForSKF.getInstance().setLogAble(true);
日志会在 logcat 打印, 且会在手机的外部存储中保存到文件
public class MyApp extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
boolean scanBleNow = true;
CISSSKFInit.init(this, scanBleNow);
//初始化之后可以设置日志显示与否, 默认不显示
OMAAPDUSenderForSKF.getInstance().setLogAble(true);
}
}
2
3
4
5
6
7
8
9
10
11
# 4.SKF枚举设备api关于设备名称的描述
| 参数 | 说明 | 备注 |
|---|---|---|
| "pcsc:OMA" | 仅使用 OMA 模式连接,CISSSKFInit.init初始化方法可以传入false | SKF_EnumDev 的结果有返回 |
| "pcsc:Auto" | 自动模式, 优先使用 OMA, OMA 不通则扫描周围的蓝牙设备。如果扫描不到设备则连接失败。为了能够迅速的扫描到蓝牙设备,CISSSKFInit.init 初始化方法应该传入 true | SKF_EnumDev的结果有返回 |
| "pcsc:Ble:xx" | 仅蓝牙模式,需要传入蓝牙的 mac 地址,MAC 地址要大写。比如:"pcsc:Ble:00:B7:1D:0A:A5:01" CISSSKFInit.init初始化方法可以传入false | SKF_EnumDev的结果无返回 |