# 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')
}
1
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'
    }
1
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)
1
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']
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3.1.7 在 module 的 build.gradle 配置其他依赖

implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
1

# 3.1.8 在 module 的 AndroidManifest.xml 配置 openmobileapi

在 application 节点里面面添加
<uses-library android:name="org.simalliance.openmobileapi"  android:required="false"/>
如下图:
1
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" />
1
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.**
1
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);
      }
}
1
2
3
4
5
6
7
8
9
10
11

# 3.3.2 在 C 语言中的初始化

先添加头文件

#include <CissSkfApi/SkfApi.h>
#include <CissSkfApi/JavaVMContext.h>
1
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;
}
1
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);
1

日志会在 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);
     }
}
1
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的结果无返回