使用Android NDK编译Cryptoauthlib库

使用Android NDK编译Cryptoauthlib库

1. 前言

目前在嵌入式Android平台中,对于安全性的需求越来越多,很多客户选择Microchip的Secure Element方案,比如ATSHA204A,ATECC608或TA10x系列芯片,都要用到Microchip的安全芯片库Cryptoauthlib。不可避免地面临到如何把Cryptoauthlib编译到Android平台中。本文简单地介绍一下使用步骤。

2. 前期准备

本文是在Windows 11的WSL2环境下,安装了Ubuntu 24.04。
将Android NDK安装在Ubuntu 24.04系统中。
在Windows 11中安装最新的Android Studio。

2.1 下载安装Android NDK(Ubuntu 24.04中)

Android NDK是专门用来编译C/C++语言的。为了防止兼容性问题,我们在Linux下把CAL库编译so。

https://developer.android.google.cn/ndk/downloads?hl=zh-cn下载Android-NDK,平台选择Linux 64 位 (x86)版本的,这里下载的是android-ndk-r27c-linux.zip

如果想下载旧版本的NDK,可以在这里下载:https://github.com/android/ndk/wiki/Unsupported-Downloads

2.2 添加到Ubuntu系统PATH

将下载的android-ndk-r27c-linux.zip解压到用户目录/home/user_name中。

$ nano ~/.bashrc
# 在最下面添加下面的代码
export ANDROID_NDK="/home/user_name/android-ndk-r27c"
export PATH="$ANDROID_NDK;$PATH"

# Ctrl + S保存配置; Ctrl + X退出

# 更新系统PATH
$ source ~/.bashrc

# 输出ANDROID_NDK路径验证一下
$ echo $ANDROID_NDK
/home/user_name/android-ndk-r27c

2.3 下载和安装Android Studio (Windows 11)

在Google官网下载Android Studio。按步骤安装好Studio。

安装好后,打开Studio,在SDK Manager中安装需要的SDK Platform,这里主要是Android 11。SDK Platform
安装Android SDK Build-Tools,Android NDK和CMake。Android NDK最好和Ubuntu中使用相同的版本。SDK Tools
在用户的环境变量中添加:
ANDROID_NDK = C:\Users\MyPcName\AppData\Local\Android\Sdk\ndk\27.0.12077973
PATH = %PATH%; %ANDROID_NDK%;C:\Users\MyPcName\AppData\Local\Android\Sdk\platform-tools

这样在Windows PowerShell命令行中就可以直接调用adb命令了。

3. 下载Cryptoauthlib和编译成so

3.1 下载Cryptoauthlib最新版本

Microchip会不定期更新Cryptoauthlib版本,我们可以在github中下载最新的版本:

$ git clone https://github.com/MicrochipTech/cryptoauthlib

由于国内网络访问github比较慢,可以用下面的备用库

$ git clone https://gitee.com/flyerink/cryptoauthlib

3.2 编译出.so库

我们使用的目录板是Raspberry PI 4B开发板,这是基于ARM aarch64架构的SoC,运行Android 11 64位系统。所以我们只需要编译出ARM64的动态链接库。

$ cd cryptoauthlib

# 为了目录更清晰,新建一个build目录,所有操作都在这里面完成
$ mkdir build && cd build

# 配置cmake, 使能HAL_I2C接口,ABI选择arm64-v8a
$ cmake -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK/build/cmake/android.toolchain.cmake" -DANDROID_ABI="arm64-v8a" -DANDROID_NDK=$ANDROID_NDK -DANDROID_PLATFORM=android-28 -DATCA_HAL_I2C=ON -DATCA_TNGTLS_SUPPORT=ON -DATCA_TFLEX_SUPPORT=ON -DATCA_PRINTF=ON -DATCA_USE_ATCAB_FUNCTIONS=ON ..
$ cmake --build .

其中的cmake参数说明如下:

  • CMAKE_TOOLCHAIN_FILE:指定cmake的工具链文件,NDK中已经提供了,直接按地址引用
  • ANDROID_ABI:选择使用的ABI, NDK目前支持的有:armeabi-v7a, arm64-v8a, x86, x86_64等。 ARM64位选择arm64-v8a;具体参考NDK ABI
  • ANDROID_NDK:设置ANDROID_NDK的路径,这里直接使用前面设置的系统路径
  • ANDROID_PLATFORM:设置支持的Android平台,这里android-28是Android 9.0版本。
  • ATCA_HAL_I2C=ON:目标板上使用I2C连接到SE,所以要打开这个选项,使能I2C接口。
  • ATCA_TNGTLS_SUPPORT=ON:可选项。ATECC608有TNGTLS类型的型号,出厂已经预烧录了TLS的证书链,如果应用中有用到,这里可以打开。
  • ATCA_TFLEX_SUPPORT=ON:可选项。ATECC608有TFLEX类型的型号,支持预配置的各种安全应用场景。如果应用中有用到,这里可以打开。
  • ATCA_PRINTF=ON:可选项。CAL库中有printf相关的打印API可供调用,如果用到可以打开。
  • ATCA_USE_ATCAB_FUNCTIONS=ON:可选项。CAL库中有atcab_前置的api,如果不打开的话,使用的是宏定义calib_前置的api。

如果编译成功的话,在build/lib目录下面会有这个文件:libcryptoauth.so

可以用file命令查看libcryptoauth.so的类型,以确定是否是目标的类型。另外可以用nm命令查看libcryptoauth.so开放的API:

$ file lib/libcryptoauth.so 
lib/libcryptoauth.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=6849effc57b942a9dcc8c0d11ab3b333be16f6a8, with debug_info, not stripped

$ nm lib/libcryptoauth.so
......
000000000001731c T atcab_ecdh_tempkey
0000000000017384 T atcab_ecdh_tempkey_ioenc
00000000000173f0 T atcab_gendig
0000000000017460 T atcab_gendivkey
0000000000017554 T atcab_genkey
......

后面就可以在Android Studio的Native C++工程中添加这个so,用JNI C++语言来调用so中的API了。具体参考JNI的开发例程。

4. 在Android Studio中开发APK

整个第4步都是在Windows 11中操作的。

4.1 新建JNI工程

Android Studio中可以通过JNI来调用外部的so中的API。
使用菜单中的新建工程,选择Native C++工程:新建Native C++工程
输入工程名CAL Test,选择Java,构建配置语言选择Groovy DSL (build.gradle):
New Project
Native C++,C++ 标准选择默认就可以了。
在这里插入图片描述
点击完成后,打开的工程可以直接构建,通过连接USB到目标板(目标板上需要启用开发者选项,打开USB调试和Root调试)上也可以运行和调试。
在这里插入图片描述

4.2 增加Cryptoauth相关配置和代码

4.2.1 复制so到工程中

[个人喜好,可选:将CMakeLists.txt移动到app目录下面]

在app下新建一个jniLibs目录,里面再新建支持的ABI类型,这里新建arm64-v8a目录,把前面生成的libcryptoauth.so文件复制到这里。如果要支持x86_64的ABI,也可以新建一个x86_64目录,把对应的libcryptoauth.so文件复制到这里。目录结构如下:
jniLibs目录

4.2.2 复制头文件到工程中

在CALTest\app\src\main\cpp中新建一个include目录。
把cryptoauthlib\lib里面的全部头文件复制到include目录中。include目录
修改app目录下的CMakeLists.txt文件:

# 添加自己的库cryptoauth
add_library(cryptoauth SHARED IMPORTED)
set_target_properties(cryptoauth
        PROPERTIES
        IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libcryptoauth.so)
include_directories(src/main/cpp/include)

# 在最下面的目标链接库中增加cryptoauth
target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        cryptoauth
        log)

在app的build.gradle中添加:

        plugins {
    alias(libs.plugins.android.application)
}

android {
    namespace 'com.microchip.caltest'
    compileSdk 34

    defaultConfig {
        applicationId "com.microchip.caltest"
        minSdk 29
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
		# 增加:设置ndk的库和支持的ABI类型
        ndk {
            moduleName "caltest"
            ldLibs "log", "cryptoauth"
            abiFilters "arm64-v8a"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }
    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
            version '3.22.1'
        }
    }
    buildFeatures {
        viewBinding true
    }
    # 增加:生成的APK需要包含库目录
    sourceSets {
        main {
            jniLibs.srcDirs = ['jniLibs']
        }
    }
    ndkVersion '27.0.12077973'
}

dependencies {

    implementation libs.appcompat
    implementation libs.material
    implementation libs.constraintlayout
    testImplementation libs.junit
    androidTestImplementation libs.ext.junit
    androidTestImplementation libs.espresso.core
}

在app\src\main\cpp\native-lib.cpp中修改代码:

#include <jni.h>
#include <string>
#include <android/log.h>
#include "cryptoauthlib.h"

#define TAG "JNI"
//#define LOGI(fmt, args...)  __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...)  __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...)  __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

ATCAIfaceCfg atecc608_device_desc = {
    .iface_type        = ATCA_I2C_IFACE,
    .devtype           = ATECC608,
    .atcai2c {
        .address       = 0xC0,
        .bus           = 1,
        .baud          = 100000,
    },
    .wake_delay        = 1500,
    .rx_retries        = 20,
    /*.cfg_data        = &Interface*/
};

extern "C" JNIEXPORT jstring JNICALL
Java_com_microchip_caltest_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jint JNICALL
Java_com_microchip_caltest_MainActivity_calibInit(
        JNIEnv* env,
        jobject /* this */) {
    (void)env;
    ATCA_STATUS status;
    uint8_t rev[8];
    char strout[1024];
    size_t strsize;

    LOGD("CAL Init Enter\r\n");
    status = atcab_init(&atecc608_device_desc);
    if(status != 0)
    {
        LOGE("CAL Init fail: %d\r\n", status);
    }

    status = atcab_info(rev);
    if(status == 0){
        strsize = sizeof(strout);
        atcab_bin2hex(rev, 4, strout, &strsize);
        LOGE("CAL Rev: %s\r\n", strout);
    }

    status = atcab_release();
    LOGD("CAL Init Exit\r\n");
    return status;
}

extern "C" JNIEXPORT jint JNICALL
Java_com_microchip_caltest_MainActivity_ecc608Provision(
        JNIEnv* env,
        jobject /* this */) {
    (void)env;
    ATCA_STATUS status;
    uint8_t rev[8];
    char strout[1024];
    size_t strsize;

    LOGD("CAL Init Enter\r\n");
    status = atcab_init(&atecc608_device_desc);
    if(status != 0) {
        LOGE("CAL Init fail: %d\r\n", status);
    }

    status = atcab_info(rev);
    if(status == 0) {
        strsize = sizeof(strout);
        atcab_bin2hex(rev, 4, strout, &strsize);
        LOGE("CAL Rev: %s\r\n", strout);
    }

    status = atcab_release();
    LOGD("CAL Init Exit\r\n");
    return status;
}

在app\src\main\java\com\microchip\caltest\MainActivity.java中新增代码:

package com.microchip.caltest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.microchip.caltest.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'caltest' library on application startup.
    static {
        System.loadLibrary("caltest");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

		// 调用calibInit和ecc608Provision函数
        calibInit();
        ecc608Provision();
        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'caltest' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native int calibInit();
    public native int ecc608Provision();
}

4.3 在目标板上运行

目标板需要启用开发者模式,并且打开USB调试开关和Root调试开关。

通过USB连接目标板到电脑上,Android Studio会自动连接到板子上,第一次会弹出是否信任此设备,建议勾选。

在Android Studio中打开下方的Terminal工具,
在这里插入图片描述
依次运行下面的命令:

$ adb root	# 使用root权限连接到adb服务
$ adb shell  # 通过adb连接到目标板的shell
rpi4:/ # chmod 777 /dev/i2c-1	# 修改i2c-1的用户读写权限
rpi4:/ # ls -l /dev/i2c-1
crwxrwxrwx 1 root root 89,   1 1970-01-01 08:00 /dev/i2c-1
rpi4:/ #

运行APK前最好确认一下硬件的连接,可以用i2cdetect来检测下:

rpi4:/ # i2cdetect -y -r 1    #查询i2c-1上的设备
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: 60 -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

可以看到,已经检测到器件地址为0x60的器件,注意这里的0x60是7位地址表示的,刚好就是ATECC608的默认器件地址0xC0。说明硬件连接没有问题。

点击运行程序,应该看到Pi 4上显示的画面。
在这里插入图片描述
在Logcat中可以看到已经读出了芯片的版本信息:
在这里插入图片描述

总结

以上就是,在Android下使用Cryptoauthlib的全部步骤,其它ATECC608的功能都可以参考具体例程实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值