Android
This document will provide instructions for building the OpenSSL library for Android devices. If you need the FIPS Validated Object Module and the FIPS Capable Library, see FIPS Library and Android.
Contents[hide] |
[*]Executive Summary
Use the following commands to build and install the OpenSSL library for Android. Before running the commands download openssl-1.0.1g.tar.gz and setenv-android.sh; place the files in the same directory (the 'root' directory mentioned below); ensure ANDROID_NDK_ROOT is set; and verify setenv-android.sh suites your taste. ANDROID_API and ANDROID_TOOLCHAIN will be set by the setenv-android.sh script. The files can be obtained from http://www.openssl.org/source/,http://openssl.com/fips/2.0/platforms/android/, and below (see Downloads section).
[*]Prepare the OpenSSL Sources
# From the 'root' directory $ rm -rf openssl-1.0.1g/ $ tar xzf openssl-1.0.1g.tar.gz $ chmod a+x setenv-android.sh
[*]Build the OpenSSL Library
# From the 'root' directory $ . ./setenv-android.sh $ cd openssl-1.0.1g/ $ perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org $ ./config shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=/usr/local/ssl/$ANDROID_API $ make depend $ make all
[*]Install the OpenSSL Library
$ sudo -E make install CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib
[*]OpenSSL Library
While the Executive Summary provided the whirlwind instructions for building and installing the OpenSSL library, this sections provides detailed instructions. There are six steps to building the OpenSSL Library for use in various projects, and they are listed below. Projects range from simple NDK based command line programs to Android activities using the JNI bridge.
- Acquire the required files
- Adjust the cross-compilation script
- Prepare the OpenSSL sources
- Build the OpenSSL Library
- Install the OpenSSL Library
[*]Acquire the Required Files
First, obtain the base files from http://www.openssl.org/source/:
- openssl-1.0.1g.tar.gz
Next, acquire the auxiliary files which can be obtained from below (see Downloads section) or http://openssl.com/fips/2.0/platforms/android/. You won't need all the files from the location.
openssl-1.0.1g.tar.gz is the OpenSSL Library. setenv-android.sh is used to set the cross-compilation environment.
After collecting the required files, your working directory will look similar to below.
android-openssl $ ls -l -rw-r--r-- 1 4459777 Jun 15 03:32 openssl-1.0.1g.tar.gz -rwxr-xr-x 1 6760 Jun 23 01:52 setenv-android.sh
[*]Adjust the Cross-Compile Script
setenv-android.sh is used to set the cross-compilation environment. Open the script an ensure the following match your needs. If you are using android-ndk-r8e, android-14, and ANDROID_NDK_ROOT is set, then the script should be ready to use as-is.
- _ANDROID_NDK – the version of the NDK. For example, android-ndk-r8e
- _ANDROID_ARCH – the architecture. For example, arch-arm or arch-x86
- _ANDROID_EABI – the version of the EABI tools. For example, arm-linux-androideabi-4.6, arm-linux-androideabi-4.8, x86-4.6 or x86-4.8
- _ANDROID_API – the API level. For example, android-14 or android-18
You should also set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT. The environmental variables are used internally by the Android platform tools and scripts. For details, see Recommended NDK Directory?.
Additional environmental variables which are set by setenv-android.sh and used by Configure and config include the following. You should not need to change them.
- MACHINE – set to armv7
- RELEASE – set to 2.6.37
- SYSTEM – set to android
- ARCH – set to arm
- CROSS_COMPILE – set to arm-linux-androideabi-
- ANDROID_DEV – set to $ANDROID_NDK_ROOT/platforms/$_ANDROID_API/arch-arm/usr
- HOSTCC – set to gcc
[*]Prepare the OpenSSL Sources
Remove stale versions of the OpenSSL Library, and then unpack fresh files. Also ensure the script is executable.
$ rm -rf openssl-1.0.1g/ $ tar xzf openssl-1.0.1g.tar.gz $ chmod a+x setenv-android.sh
[*]Build the OpenSSL Library
This section of the document will guide you through the creation of the the OpenSSL Library. The OpenSSL Library (and Makefile.org) needs its install rule modified. The install rule includes the all target, which causes items to be built during install. A bug in the process when running as root results in an empty signature for the shared object (the signature is a string of zeros).
To build the OpenSSL Library, you must issue config, but other options are up to you. Some suggested options for configure include: shared, no-ssl2, no-ssl3, no-comp, no-hw, and no-engine. shared will build and install both the shared object and static archive. You should specify --openssldir to ensure the build system installs the android version of the library in a distinct location (other than /usr/local/ssl).
Begin building the OpenSSL library by setting the cross-compilation environment. Note the leading '.' when running the setenv-android.sh script. If you have any errors from the script, then you should fix them before proceeding.
$ . ./setenv-android.sh $ cd openssl-1.0.1g/
If you receive a meesage "Error: FIPS_SIG does not specify incore module, please edit this script, then its safe to ignore it. setenv-android.sh is used to build both the FIPS Capable OpenSSL library and the non-FIPS version of the library.FIPS_SIG is not needed in this configuration.
Next, fix the makefile and run configure.
$ perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org $ ./config shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=/usr/local/ssl/android-14/
Then run make depend and make all:
$ make depend $ make all
After make completes, verify libcrypto.a and libssl.a were built for the embedded architecture.
$ find . -name libcrypto.a ./libcrypto.a $ readelf -h ./libcrypto.a | grep -i 'class\|machine' | head -2 Class: ELF32 Machine: ARM
[*]Install the OpenSSL Library
Finally, install the library. The makefile's install rule uses both CC and RANLIB, so you will need to fully specify the command variables on the command line (during install, sudo drops the user's path). You must also use sudo's -E option; otherwise ANDROID_TOOLCHAIN will be empty and tools such as arm-linux-androideabi-gcc and arm-linux-androideabi-ranlib will not be found.
$ sudo -E make install CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib
[*]Testing the OpenSSL Library
Testing the installation consists of building a sample program, installing it with adb, and then running the program using a remote shell. Both the static and dynamic version of the OpenSSL library can be tested. Instructions for testing the OpenSSL library are given at FIPS Library and Android. The same basic steps apply.
[*]Using OpenSSL in an Application
In the real world, you probably aren't using C and therefore still need a dynamic library, but Android probably already has non-FIPS Library called libssl.so and libcrypto.so loaded into memory. Due to issues with the Android loader, the LD_LIBRARY_PATH trick doesn't work for normal applications. Because we can't change the build to output different names to avoid the namespace clash, the solution to getting the code into a differently-named shared library is to wrap up the static Library into a separate dynamic library. To do this, write a short c wrapper library with references to functions in both the Library (so the linker doesn't discard them as unnecessary) and link the static Library as above. You don't need to wrap all the functions, as the Library themselves become part of the interface. For these instructions, I'll assume your c file is called wrapper.c
$ export OPENSSL_ANDROID = /usr/local/ssl/android-14 $ $(CC) wrapper.c -fPIC -shared -I$(OPENSSL_ANDROID)/include -Wl,-Bstatic -lcrypto -lssl -L$(OPENSSL_ANDROID)/lib -o wrapper.so -Wl,-Bdynamic
The -Wl,-Bstatic tells the linker to use the static version of the OpenSSL library for the Library. After it and the -Wl,-Bdynamic tells the linker to use dynamic linking for anything else it might need, like libc.
Using -Bstatic and -Bshared can cause link problems on occasion. For example, see Android: error when trying to compile wrapper for openssl library libcrypto.a. To avoid the problem with the linker, specify the full path to the static archive (for example, /usr/local/ssl/android-14/lib/libcrypto.a). If you suspect the wrong OpenSSL library is being linked, then use the fully qualified archive path.
You can then use wrapper.so as per normal.
[*]Miscellaneous
The NDK supplies headers for each major platform - for example, API 18, API 14, API 9, API 8, and API 5. If you are building for Android 4.2 (API 17), Android 4.1 (API 16) and Android 4.0 (API 14), then you would use the NDK's API 14 (android-14 platform).
Specify the full library name when calling Java's System.load. That is, call System.load(“libcrypto.so.1.0.0”). Also note that some Android routines expect the prefix of “lib” and suffix of “so”, so you might have to rename the library.
Some versions of the Android Java system loader will load the system's version of the OpenSSL library, even though you built and included a copy with your application. In this case, you might need to write a wrapper shared object and link to the static version of the OpenSSL library. See, for example, "Unable to find native library" error in Native Activity app.
If you compile with -fPIE and -pie, then you will core dump unless using Android 4.1 and above. Logcat shows the linker (/system/bin/linker) is the problem.
shell@android: $ ./fips_hmac.exe -v fips_hmac.exe [2] + Stopped (signal) ./fips_hmac.exe -v fips_hmac.exe [1] - Segmentation fault ./fips_hmac.exe -v fips_hmac.exe
When building the OpenSSL library for Android, take care to specify -mfloat-abi=softfp. If you specify -mfloat-abi=hard or -mhard-float (even if the hardware support a floating point unit), then the entropy estimate passed through the Java VM toRAND_add will always be 0.0f. See Hard-float and JNI for details.
[*]Downloads
setenv-android.sh - script to set Android cross-compile environment.
#!/bin/bash
# Cross-compile environment for Android on ARMv7 and x86
#
# Contents licensed under the terms of the OpenSSL license
# http://www.openssl.org/source/license.html
#
# See http://wiki.openssl.org/index.php/FIPS_Library_and_Android
# and http://wiki.openssl.org/index.php/Android
#####################################################################
# Set ANDROID_NDK_ROOT to you NDK location. For example,
# /opt/android-ndk-r8e or /opt/android-ndk-r9. This can be done in a
# login script. If ANDROID_NDK_ROOT is not specified, the script will
# try to pick it up with the value of _ANDROID_NDK_ROOT below. If
# ANDROID_NDK_ROOT is set, then the value is ignored.
# _ANDROID_NDK="android-ndk-r8e"
_ANDROID_NDK="android-ndk-r9"
# _ANDROID_NDK="android-ndk-r10"
# Set _ANDROID_EABI to the EABI you want to use. You can find the
# list in $ANDROID_NDK_ROOT/toolchains. This value is always used.
# _ANDROID_EABI="x86-4.6"
# _ANDROID_EABI="arm-linux-androideabi-4.6"
_ANDROID_EABI="arm-linux-androideabi-4.8"
# Set _ANDROID_ARCH to the architecture you are building for.
# This value is always used.
# _ANDROID_ARCH=arch-x86
_ANDROID_ARCH=arch-arm
# Set _ANDROID_API to the API you want to use. You should set it
# to one of: android-14, android-9, android-8, android-14, android-5
# android-4, or android-3. You can't set it to the latest (for
# example, API-17) because the NDK does not supply the platform. At
# Android 5.0, there will likely be another platform added (android-22?).
# This value is always used.
# _ANDROID_API="android-14"
_ANDROID_API="android-18"
# _ANDROID_API="android-19"
#####################################################################
# If the user did not specify the NDK location, try and pick it up.
# We expect something like ANDROID_NDK_ROOT=/opt/android-ndk-r8e
# or ANDROID_NDK_ROOT=/usr/local/android-ndk-r8e.
if [ -z "$ANDROID_NDK_ROOT" ]; then
_ANDROID_NDK_ROOT=""
if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/usr/local/$_ANDROID_NDK" ]; then
_ANDROID_NDK_ROOT="/usr/local/$_ANDROID_NDK"
fi
if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "/opt/$_ANDROID_NDK" ]; then
_ANDROID_NDK_ROOT="/opt/$_ANDROID_NDK"
fi
if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$HOME/$_ANDROID_NDK" ]; then
_ANDROID_NDK_ROOT="$HOME/$_ANDROID_NDK"
fi
if [ -z "$_ANDROID_NDK_ROOT" ] && [ -d "$PWD/$_ANDROID_NDK" ]; then
_ANDROID_NDK_ROOT="$PWD/$_ANDROID_NDK"
fi
# If a path was set, then export it
if [ ! -z "$_ANDROID_NDK_ROOT" ] && [ -d "$_ANDROID_NDK_ROOT" ]; then
export ANDROID_NDK_ROOT="$_ANDROID_NDK_ROOT"
fi
fi
# Error checking
# ANDROID_NDK_ROOT should always be set by the user (even when not running this script)
# http://groups.google.com/group/android-ndk/browse_thread/thread/a998e139aca71d77
if [ -z "$ANDROID_NDK_ROOT" ] || [ ! -d "$ANDROID_NDK_ROOT" ]; then
echo "Error: ANDROID_NDK_ROOT is not a valid path. Please edit this script."
# echo "$ANDROID_NDK_ROOT"
# exit 1
fi
# Error checking
if [ ! -d "$ANDROID_NDK_ROOT/toolchains" ]; then
echo "Error: ANDROID_NDK_ROOT/toolchains is not a valid path. Please edit this script."
# echo "$ANDROID_NDK_ROOT/toolchains"
# exit 1
fi
# Error checking
if [ ! -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI" ]; then
echo "Error: ANDROID_EABI is not a valid path. Please edit this script."
# echo "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI"
# exit 1
fi
#####################################################################
# Based on ANDROID_NDK_ROOT, try and pick up the required toolchain. We expect something like:
# /opt/android-ndk-r83/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin
# Once we locate the toolchain, we add it to the PATH. Note: this is the 'hard way' of
# doing things according to the NDK documentation for Ice Cream Sandwich.
# https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
ANDROID_TOOLCHAIN=""
for host in "linux-x86_64" "linux-x86" "darwin-x86_64" "darwin-x86"
do
if [ -d "$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin" ]; then
ANDROID_TOOLCHAIN="$ANDROID_NDK_ROOT/toolchains/$_ANDROID_EABI/prebuilt/$host/bin"
break
fi
done
# Error checking
if [ -z "$ANDROID_TOOLCHAIN" ] || [ ! -d "$ANDROID_TOOLCHAIN" ]; then
echo "Error: ANDROID_TOOLCHAIN is not valid. Please edit this script."
# echo "$ANDROID_TOOLCHAIN"
# exit 1
fi
case $_ANDROID_ARCH in
arch-arm)
ANDROID_TOOLS="arm-linux-androideabi-gcc arm-linux-androideabi-ranlib arm-linux-androideabi-ld"
;;
arch-x86)
ANDROID_TOOLS="i686-linux-android-gcc i686-linux-android-ranlib i686-linux-android-ld"
;;
*)
echo "ERROR ERROR ERROR"
;;
esac
for tool in $ANDROID_TOOLS
do
# Error checking
if [ ! -e "$ANDROID_TOOLCHAIN/$tool" ]; then
echo "Error: Failed to find $tool. Please edit this script."
# echo "$ANDROID_TOOLCHAIN/$tool"
# exit 1
fi
done
# Only modify/export PATH if ANDROID_TOOLCHAIN good
if [ ! -z "$ANDROID_TOOLCHAIN" ]; then
export ANDROID_TOOLCHAIN="$ANDROID_TOOLCHAIN"
export PATH="$ANDROID_TOOLCHAIN":"$PATH"
fi
#####################################################################
# For the Android SYSROOT. Can be used on the command line with --sysroot
# https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"
export SYSROOT="$ANDROID_SYSROOT"
export NDK_SYSROOT="$ANDROID_SYSROOT"
# Error checking
if [ -z "$ANDROID_SYSROOT" ] || [ ! -d "$ANDROID_SYSROOT" ]; then
echo "Error: ANDROID_SYSROOT is not valid. Please edit this script."
# echo "$ANDROID_SYSROOT"
# exit 1
fi
#####################################################################
# If the user did not specify the FIPS_SIG location, try and pick it up
# If the user specified a bad location, then try and pick it up too.
if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then
# Try and locate it
_FIPS_SIG=""
if [ -d "/usr/local/ssl/$_ANDROID_API" ]; then
_FIPS_SIG=`find "/usr/local/ssl/$_ANDROID_API" -name incore`
fi
if [ ! -e "$_FIPS_SIG" ]; then
_FIPS_SIG=`find $PWD -name incore`
fi
# If a path was set, then export it
if [ ! -z "$_FIPS_SIG" ] && [ -e "$_FIPS_SIG" ]; then
export FIPS_SIG="$_FIPS_SIG"
fi
fi
# Error checking. Its OK to ignore this if you are *not* building for FIPS
if [ -z "$FIPS_SIG" ] || [ ! -e "$FIPS_SIG" ]; then
echo "Error: FIPS_SIG does not specify incore module. Please edit this script."
# echo "$FIPS_SIG"
# exit 1
fi
#####################################################################
# Most of these should be OK (MACHINE, SYSTEM, ARCH). RELEASE is ignored.
export MACHINE=armv7
export RELEASE=2.6.37
export SYSTEM=android
export ARCH=arm
export CROSS_COMPILE="arm-linux-androideabi-"
if [ "$_ANDROID_ARCH" == "arch-x86" ]; then
export MACHINE=i686
export RELEASE=2.6.37
export SYSTEM=android
export ARCH=x86
export CROSS_COMPILE="i686-linux-android-"
fi
# For the Android toolchain
# https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
export ANDROID_SYSROOT="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH"
export SYSROOT="$ANDROID_SYSROOT"
export NDK_SYSROOT="$ANDROID_SYSROOT"
export ANDROID_NDK_SYSROOT="$ANDROID_SYSROOT"
export ANDROID_API="$_ANDROID_API"
# CROSS_COMPILE and ANDROID_DEV are DFW (Don't Fiddle With). Its used by OpenSSL build system.
# export CROSS_COMPILE="arm-linux-androideabi-"
export ANDROID_DEV="$ANDROID_NDK_ROOT/platforms/$_ANDROID_API/$_ANDROID_ARCH/usr"
export HOSTCC=gcc
VERBOSE=1
if [ ! -z "$VERBOSE" ] && [ "$VERBOSE" != "0" ]; then
echo "ANDROID_NDK_ROOT: $ANDROID_NDK_ROOT"
echo "ANDROID_ARCH: $_ANDROID_ARCH"
echo "ANDROID_EABI: $_ANDROID_EABI"
echo "ANDROID_API: $ANDROID_API"
echo "ANDROID_SYSROOT: $ANDROID_SYSROOT"
echo "ANDROID_TOOLCHAIN: $ANDROID_TOOLCHAIN"
echo "FIPS_SIG: $FIPS_SIG"
echo "CROSS_COMPILE: $CROSS_COMPILE"
echo "ANDROID_DEV: $ANDROID_DEV"
fi