Android Runtime敏感数据加密存储源码级解析
一、Android Runtime加密存储基础概念与架构
Android Runtime(ART)作为安卓应用运行核心,其敏感数据加密存储旨在保护用户隐私数据。从系统架构看,加密存储体系由Java层、JNI层、Native层协同构建。Java层提供高层API接口,JNI层负责Java与Native层通信,Native层则依赖OpenSSL等库实现核心加密逻辑。
在frameworks/base/core/java
目录下的Java源码,定义了加密操作相关类。例如javax.crypto.Cipher
类用于实现加密和解密,java.security.KeyPairGenerator
用于生成密钥对。而libcore/luni/src/main/native
目录下的JNI代码,承担着Java层与Native层数据传递与方法调用的桥梁作用 ,Native层代码在external/openssl
等目录,基于OpenSSL库完成实际加密运算。
二、对称加密算法在Android中的源码实现
2.1 AES算法Java层实现
AES(高级加密标准)是Android常用对称加密算法。在Java层,javax.crypto.Cipher
类是核心操作类。在libcore/luni/src/main/java/javax/crypto/Cipher.java
中,定义了getInstance
、init
、doFinal
等关键方法。
getInstance
方法用于获取Cipher
实例,代码如下:
java
public static Cipher getInstance(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException {
// 通过ServiceLoader加载Cipher实现类
ServiceLoader<CipherSpi> loader = ServiceLoader.load(CipherSpi.class);
for (CipherSpi spi : loader) {
try {
Cipher cipher = new Cipher(spi);
if (cipher.engineGetSupportedTransforms().contains(transformation)) {
return cipher;
}
} catch (Exception e) {
// 异常处理
}
}
throw new NoSuchAlgorithmException("No provider found for " + transformation);
}
该方法通过ServiceLoader
加载CipherSpi
实现类,找到支持指定加密模式(如AES
)的Cipher
实例。
初始化加密模式的init
方法:
java
public final void init(int opmode, Key key)
throws InvalidKeyException {
// 检查密钥类型
if (!(key instanceof SecretKey)) {
throw new InvalidKeyException("Not a secret key");
}
// 调用底层CipherSpi的init方法
spi.init(opmode, key);
}
init
方法先验证密钥类型,再调用底层CipherSpi
实现类的init
方法完成初始化。
2.2 AES算法JNI与Native层实现
JNI层代码在libcore/luni/src/main/native/libcore_crypto_Jni.c
中,负责将Java层参数转换为Native层可处理格式。例如将Java的SecretKey
转换为OpenSSL的密钥格式:
c
static jbyteArray
Cipher_init(JNIEnv *env, jobject thiz, jint opmode, jobject key) {
// 获取Java层密钥字节数组
jbyteArray keyBytes = (*env)->GetObjectField(env, key, fields.keyBytes);
// 转换为C语言字节数组
jbyte* keyArray = (*env)->GetByteArrayElements(env, keyBytes, NULL);
// 创建OpenSSL密钥对象
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
// 初始化OpenSSL加密上下文
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, keyArray, iv);
// 其他操作...
return result;
}
Native层基于OpenSSL库,在external/openssl/crypto
目录下,使用EVP_aes_128_cbc
等函数完成AES加密运算。EVP_CipherInit_ex
用于初始化加密上下文,EVP_CipherUpdate
和EVP_CipherFinal_ex
分别用于分段加密和最终加密操作 。
三、非对称加密算法的源码剖析
3.1 RSA算法Java层实现
在Java层,java.security.KeyPairGenerator
用于生成RSA密钥对。libcore/luni/src/main/java/java/security/KeyPairGenerator.java
中定义了相关逻辑:
java
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
// 根据算法名称获取对应的KeyPairGeneratorSpi实现类
KeyPairGeneratorSpi spi = getInstance(algorithm, null);
return new KeyPairGenerator(spi);
}
private static KeyPairGeneratorSpi getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException {
// 通过ServiceLoader加载KeyPairGeneratorSpi实现类
ServiceLoader<KeyPairGeneratorSpi> loader = ServiceLoader.load(KeyPairGeneratorSpi.class);
for (KeyPairGeneratorSpi spi : loader) {
try {
if (spi.engineGetKeyPairGeneratorAlgorithm().equals(algorithm)) {
return spi;
}
} catch (Exception e) {
// 异常处理
}
}
throw new NoSuchAlgorithmException("No provider found for " + algorithm);
}
getInstance
方法通过ServiceLoader
加载KeyPairGeneratorSpi
实现类,获取支持RSA算法的实例。生成密钥对的generateKeyPair
方法:
java
public final KeyPair generateKeyPair() {
return spi.engineGenerateKeyPair();
}
该方法调用底层KeyPairGeneratorSpi
实现类的engineGenerateKeyPair
方法完成密钥对生成。
3.2 RSA算法JNI与Native层实现
JNI层代码在libcore/luni/src/main/native/libcore_security_KeyPairGenerator.c
中,负责参数传递与格式转换。如将Java生成的密钥参数转换为OpenSSL可处理格式:
c
static jobject
KeyPairGenerator_generateKeyPair(JNIEnv *env, jobject thiz) {
// 获取Java层设置的密钥长度等参数
jint keySize = (*env)->GetIntField(env, thiz, fields.keySize);
// 创建OpenSSL RSA密钥对生成相关对象
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
BN_set_word(e, RSA_F4);
// 生成RSA密钥对
if (RSA_generate_key_ex(rsa, keySize, e, NULL)!= 1) {
// 错误处理
}
// 将生成的密钥对转换为Java对象返回
return result;
}
Native层在OpenSSL库支持下,通过RSA_generate_key_ex
等函数完成RSA密钥对生成,利用BN
系列函数进行大整数运算,保障密钥对生成的安全性与准确性 。
四、密钥管理机制源码分析
4.1 密钥生成源码逻辑
对称密钥生成由javax.crypto.KeyGenerator
类实现。在libcore/luni/src/main/java/javax/crypto/KeyGenerator.java
中:
java
public static KeyGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException {
// 根据算法名称获取对应的KeyGeneratorSpi实现类
KeyGeneratorSpi spi = getInstance(algorithm, null);
return new KeyGenerator(spi);
}
private static KeyGeneratorSpi getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException {
// 通过ServiceLoader加载KeyGeneratorSpi实现类
ServiceLoader<KeyGeneratorSpi> loader = ServiceLoader.load(KeyGeneratorSpi.class);
for (KeyGeneratorSpi spi : loader) {
try {
if (spi.engineGetKeyGeneratorAlgorithm().equals(algorithm)) {
return spi;
}
} catch (Exception e) {
// 异常处理
}
}
throw new NoSuchAlgorithmException("No provider found for " + algorithm);
}
以AES密钥生成为例,获取AESKeyGeneratorSpi
实现类后,通过engineGenerateKey
方法生成密钥:
java
protected SecretKey engineGenerateKey() {
// 使用SecureRandom生成随机字节数组作为密钥
byte[] keyBytes = new byte[getDefaultKeySize()];
SecureRandom random = getSecureRandom();
random.nextBytes(keyBytes);
return new SecretKeySpec(keyBytes, getAlgorithm());
}
4.2 密钥存储源码实现
Android通过KeyStore
类实现密钥存储,libcore/luni/src/main/java/java/security/KeyStore.java
中定义相关操作:
java
public static KeyStore getInstance(String type)
throws KeyStoreException {
// 根据类型获取对应的KeyStoreSpi实现类
KeyStoreSpi spi = getInstance(type, null);
return new KeyStore(spi);
}
private static KeyStoreSpi getInstance(String type, String provider)
throws KeyStoreException {
// 通过ServiceLoader加载KeyStoreSpi实现类
ServiceLoader<KeyStoreSpi> loader = ServiceLoader.load(KeyStoreSpi.class);
for (KeyStoreSpi spi : loader) {
try {
if (spi.engineGetKeyStoreType().equals(type)) {
return spi;
}
} catch (Exception e) {
// 异常处理
}
}
throw new KeyStoreException("No provider found for " + type);
}
存储密钥时,setKeyEntry
方法:
java
public final void setKeyEntry(String alias, Key key, char[] password,
Certificate[] chain)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException {
// 检查密钥类型等
if (!(key instanceof PrivateKey)) {
throw new KeyStoreException("Not a private key");
}
// 调用底层KeyStoreSpi的engineSetKeyEntry方法
spi.engineSetKeyEntry(alias, key, password, chain);
}
4.3 密钥使用与保护
在数据加密解密过程中,密钥使用需保障安全。Java层通过及时清理内存中密钥明文保障安全,如使用完密钥后对密钥字节数组清零:
java
byte[] keyBytes =...;
// 使用密钥进行加密操作
// 加密完成后清零密钥字节数组
for (int i = 0; i < keyBytes.length; i++) {
keyBytes[i] = 0;
}
JNI层在传递密钥到Native层时,采用安全内存拷贝方式,防止密钥泄露 。Native层使用完密钥后,通过memset
函数清空密钥所在内存区域:
c
unsigned char* key =...;
// 使用密钥进行加密操作
// 操作完成后清空密钥内存
memset(key, 0, key_length);
五、文件系统加密存储源码解析
5.1 数据写入加密实现
在Java层,文件写入通过java.io.FileOutputStream
实现。结合加密功能的写入代码如下:
java
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeySpec;
public class FileEncryption {
public static void writeEncryptedData(String data, String filePath, String key) throws Exception {
// 创建AES密钥
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
// 获取Cipher实例并初始化为加密模式
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密数据
byte[] encryptedData = cipher.doFinal(data.getBytes());
// 写入文件
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(encryptedData);
fos.close();
}
}
libcore/luni/src/main/java/java/io/FileOutputStream.java
中,write
方法最终通过JNI调用Native层文件写入函数:
c
static void
FileOutputStream_writeBytes(JNIEnv *env, jobject thiz, jbyteArray bytes,
jint off, jint len) {
// 获取Java层字节数组
jbyte* byteArray = (*env)->GetByteArrayElements(env, bytes, NULL);
// 获取文件描述符
jint fd = (*env)->GetIntField(env, thiz, fields.fd);
// 调用write系统调用写入数据
write(fd, byteArray + off, len);
// 释放资源
(*env)->ReleaseByteArrayElements(env, bytes, byteArray, 0);
}
5.2 数据读取解密实现
读取加密文件时,先读取加密数据,再进行解密。Java层代码:
java
import java.io.FileInputStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeySpec;
public class FileDecryption {
public static String readDecryptedData(String filePath, String key) throws Exception {
// 创建AES密钥
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
// 获取Cipher实例并初始化为解密模式
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 读取加密数据
FileInputStream fis = new FileInputStream(filePath);
byte[] encryptedData = new byte[fis.available()];
fis.read(encryptedData);
fis.close();
// 解密数据
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
}
}
Native层同样通过系统调用读取文件数据,再将数据传递回Java层进行解密处理 。
六、数据库加密存储源码解析
6.1 SQLite数据库插入加密
Android应用常使用SQLite数据库,在android.database.sqlite.SQLiteDatabase
类基础上实现加密存储。插入加密数据代码示例:
java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeySpec;
public class EncryptedSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "encrypted.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "sensitive_data";
private static final String COLUMN_ID = "_id";
private static final String COLUMN_DATA = "data";
public EncryptedSQLiteOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_DATA + " BLOB)";
db.execSQL(createTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 升级逻辑
}
public void insertEncryptedData(String data, String key) throws Exception {
// 创建AES密钥
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
// 获取Cipher实例并初始化为加密模式
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密数据
byte[] encryptedData = cipher.doFinal(data.getBytes());
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("INSERT INTO " + TABLE_NAME + " (" + COLUMN_DATA + ") VALUES (?)", new Object[]{encryptedData});
db.close();
}
}
android/database/sqlite/SQLiteDatabase.java
中,execSQL
方法通过JNI调用Native层SQLite库函数执行SQL语句:
c
static jlong
SQLiteDatabase_execSQL(JNIEnv *env, jobject thiz, jstring sql) {
// 将Java字符串转换为C字符串
const char* sqlStr = (*env)->GetStringUTFChars(env, sql, NULL);
// 获取SQLite数据库对象
sqlite3* db = (sqlite3*)(*env)->GetLongField(env, thiz, fields.nativeHandle);
// 执行SQL语句
int result = sqlite3_exec(db, sqlStr, NULL, NULL, NULL);
// 释放资源
(*env)->ReleaseStringUTFChars(env, sql, sqlStr);
return result;
}
6.2 SQLite数据库查询解密
查询加密数据并解密代码如下:
java
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeySpec;
public class EncryptedSQLiteOpenHelper {
// 其他代码...
public String queryDecryptedData(String key) throws Exception {
// 创建AES密钥
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
// 获取Cipher实例并初始化为解密模式
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_DATA}, null, null, null, null, null);
if (cursor.moveToFirst()) {
byte[] encryptedData = cursor.getBlob(cursor.getColumnIndex(COLUMN_DATA));
// 解密数据
byte[] decryptedData = cipher.doFinal(encryptedData);
cursor.close();
db.close();
return new String(decryptedData);
七、权限管理与访问控制源码解析
7.1 权限声明与验证机制
Android应用需在AndroidManifest.xml
中声明敏感操作权限,如:
xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
在frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
中,parsePermissions
方法解析Manifest中的权限声明:
java
private void parsePermissions(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
// 解析uses-permission标签
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("uses-permission")) {
// 解析权限名称等信息
String name = parser.getAttributeValue(null, "name");
// 权限处理逻辑
processUsesPermissionLPw(pkg, name, parser, flags, outError);
}
}
}
运行时权限验证在ActivityManagerService
的checkPermission
方法中实现:
java
public int checkPermission(String permission, int pid, int uid) {
// 检查权限是否被授予
synchronized (this) {
int res = mContext.getPackageManager().checkPermission(permission,
Process.getPackageNameForUid(uid));
return res;
}
}
7.2 SELinux强制访问控制
SELinux策略文件位于system/sepolicy
目录,通过定义不同域的访问规则实现强制访问控制。例如,限制应用对密钥存储的访问:
ini
# 限制普通应用访问keystore服务
neverallow appdomain keystore_service:service_manager *;
# 允许系统服务访问keystore
allow system_app keystore_service:service_manager find;
在security/secomp
目录下的seccomp过滤规则进一步限制进程系统调用权限:
c
// seccomp过滤规则示例
struct sock_filter filter[] = {
// 允许read系统调用
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
// 其他系统调用处理
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
};
八、硬件安全模块集成与源码分析
8.1 TrustZone技术集成
Android通过hardware/libhardware/modules/keystore
目录下的代码与TrustZone通信。keystore.c
中定义了与安全世界通信的接口:
c
int keystore_gen_key(const uint8_t* keyblob, size_t keyblob_len,
const uint8_t* auth_token, size_t auth_token_len,
uint8_t** output, size_t* output_len) {
// 与TrustZone通信,请求生成密钥
struct teec_context context;
struct teec_session session;
struct teec_operation op;
uint32_t err_origin;
// 初始化TEE上下文
TEEC_InitializeContext(NULL, &context);
// 打开与安全世界的会话
TEEC_OpenSession(&context, &session, &TA_KEYSTORE_UUID,
TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
// 准备操作参数
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
TEEC_NONE);
op.params[0].tmpref.buffer = (void*)keyblob;
op.params[0].tmpref.size = keyblob_len;
// 其他参数设置...
// 执行安全世界命令
TEEC_InvokeCommand(&session, TA_KEYSTORE_CMD_GEN_KEY, &op, &err_origin);
// 处理返回结果
*output = malloc(op.params[2].tmpref.size);
memcpy(*output, op.params[2].tmpref.buffer, op.params[2].tmpref.size);
*output_len = op.params[2].tmpref.size;
// 关闭会话和上下文
TEEC_CloseSession(&session);
TEEC_FinalizeContext(&context);
return 0;
}
8.2 TEE驱动层实现
TEE驱动位于kernel/drivers/tee
目录,optee
驱动实现了与OP-TEE TrustZone的通信。optee_core.c
中的optee_open_session
方法:
c
int optee_open_session(struct optee_context *ctx,
struct optee_session *sess,
const u8 *uuid, uint32_t nparams,
struct optee_param *params,
uint32_t *ret_origin) {
struct optee_msg_arg arg;
int err;
// 初始化消息参数
memset(&arg, 0, sizeof(arg));
arg.func = OPTEE_MSG_FUNC_OPEN_SESSION;
arg.session = 0;
// 设置UUID和参数
memcpy(arg.uuid, uuid, sizeof(arg.uuid));
// 转换参数格式
optee_msg_to_arg(params, nparams, &arg);
// 调用OP-TEE驱动命令
err = optee_call_with_arg(ctx, &arg);
if (err)
return err;
// 处理返回结果
*ret_origin = arg.ret_origin;
sess->handle = arg.session;
// 其他处理...
return 0;
}
九、安全审计与日志记录源码解析
9.1 审计日志生成
Android通过android.util.Log
类记录系统日志,在敏感操作处添加日志记录。例如在KeyStore
类的engineSetKeyEntry
方法中:
java
protected void engineSetKeyEntry(String alias, Key key, char[] password,
Certificate[] chain) throws KeyStoreException {
// 记录操作日志
Log.i(TAG, "Setting key entry for alias: " + alias);
// 密钥存储逻辑
if (key instanceof PrivateKey) {
// 私钥处理
} else if (key instanceof SecretKey) {
// 对称密钥处理
}
// 其他处理...
}
日志最终通过JNI调用Native层的__android_log_write
函数输出:
c
int __android_log_write(int prio, const char *tag, const char *text) {
// 日志优先级和标签处理
struct iovec iov[3];
const char *priorities = "VDIWEF";
iov[0].iov_base = (char *)&priorities[prio];
iov[0].iov_len = 1;
iov[1].iov_base = (char *)tag;
iov[1].iov_len = strlen(tag);
iov[2].iov_base = (char *)text;
iov[2].iov_len = strlen(text);
// 通过socket发送日志
return writev(logfd, iov, 3);
}
9.2 日志收集与分析
系统日志收集由logd
守护进程负责,位于system/core/logd
目录。logd.c
中的main
函数:
c
int main(int argc, char **argv) {
// 初始化日志系统
if (android::initLogging() != 0) {
ALOGE("Failed to initialize logging");
return 1;
}
// 创建Unix域套接字监听日志
if (create_socket() < 0) {
ALOGE("Failed to create socket");
return 1;
}
// 主事件循环
while (1) {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(log_socket, &read_fds);
int rc = select(log_socket + 1, &read_fds, NULL, NULL, NULL);
if (rc < 0) {
// 错误处理
} else if (FD_ISSET(log_socket, &read_fds)) {
// 读取日志数据
handle_socket_read();
}
}
return 0;
}
十、性能优化与内存管理源码分析
10.1 加密操作性能优化
Android在external/openssl/crypto/evp
目录下对加密算法进行了性能优化。例如AES-NI指令集优化:
c
// AES-NI优化实现
static int aesni_cbc_encrypt(const unsigned char *in, unsigned char *out,
size_t len, const EVP_CIPHER_CTX *ctx,
unsigned char *iv) {
// 检查CPU是否支持AES-NI指令集
if (OPENSSL_ia32cap_P[CPUID_AES]) {
// 使用AES-NI指令集加速加密
aesni_cbc_encrypt_blocks(in, out, len, ctx->cipher_data, iv);
return 1;
} else {
// 回退到普通实现
return aes_cbc_encrypt(in, out, len, ctx, iv);
}
}
10.2 内存安全管理
在JNI层,严格控制内存分配和释放,防止内存泄漏。例如在libcore/luni/src/main/native/libcore_crypto_Jni.c
中:
c
static jbyteArray Cipher_doFinal(JNIEnv *env, jobject thiz, jbyteArray input) {
jbyteArray output = NULL;
jbyte *inputBytes = NULL;
unsigned char *outputBytes = NULL;
int outputLen = 0;
// 获取输入数据
inputBytes = (*env)->GetByteArrayElements(env, input, NULL);
if (inputBytes == NULL) {
goto fail;
}
// 执行加密操作,分配输出缓冲区
outputBytes = (unsigned char *)malloc(inputLen * 2);
if (outputBytes == NULL) {
goto fail;
}
// 执行加密
outputLen = do_encrypt(inputBytes, inputLen, outputBytes);
// 创建Java字节数组
output = (*env)->NewByteArray(env, outputLen);
if (output == NULL) {
goto fail;
}
// 设置数组内容
(*env)->SetByteArrayRegion(env, output, 0, outputLen, (jbyte *)outputBytes);
fail:
// 释放资源
if (inputBytes) {
(*env)->ReleaseByteArrayElements(env, input, inputBytes, JNI_ABORT);
}
if (outputBytes) {
free(outputBytes);
}
return output;
}
十一、异常处理与错误恢复机制
11.1 加密操作异常处理
在Java层,加密操作通过抛出异常处理错误情况。例如Cipher
类的doFinal
方法:
java
public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException,
BadPaddingException {
try {
// 执行加密/解密操作
return engineDoFinal(input, 0, input.length);
} catch (IllegalBlockSizeException e) {
// 处理非法块大小异常
throw e;
} catch (BadPaddingException e) {
// 处理填充错误异常
throw e;
} catch (Exception e) {
// 处理其他异常
throw new RuntimeException("Error during cipher operation", e);
}
}
11.2 密钥管理错误恢复
在KeyStore
类中,处理加载错误的逻辑:
java
public void load(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
try {
// 加载KeyStore数据
if (stream == null) {
// 加载默认KeyStore
loadDefaultKeyStore(password);
} else {
// 从输入流加载
loadFromStream(stream, password);
}
} catch (FileNotFoundException e) {
// 处理文件不存在异常,创建新的KeyStore
createNewKeyStore(password);
} catch (IOException e) {
// 处理IO异常
throw e;
} catch (NoSuchAlgorithmException e) {
// 处理算法不存在异常
throw e;
} catch (CertificateException e) {
// 处理证书异常
throw e;
}
}
十二、兼容性与版本演进
12.1 不同Android版本的加密实现差异
从Android 6.0(API级别23)开始引入了KeyGenParameterSpec
类,提供更安全的密钥生成参数设置:
java
// Android 6.0+密钥生成示例
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
"myKeyAlias",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.build();
keyGenerator.init(spec);
SecretKey secretKey = keyGenerator.generateKey();
而在Android 5.0(API级别21)中,密钥生成方式相对简单:
java
// Android 5.0密钥生成示例
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
12.2 向后兼容性保障
在frameworks/base/core/java/android/security/keystore
目录下,有大量代码用于处理不同版本间的兼容性问题。例如AndroidKeyStoreCipherSpiBase
类中的版本适配逻辑:
java
@Override
protected final void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
// 检查API版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 使用Android 6.0+的初始化方法
initKeyAndSecureRandom(opmode, key, random);
} else {
// 使用旧版本的初始化方法
initKey(opmode, key);
if (random != null) {
setSecureRandom(random);
}
}
}
十三、第三方库集成与源码分析
13.1 Bouncy Castle库集成
Android通过libcore/luni/src/main/java/org/bouncycastle
目录集成Bouncy Castle加密库。在Cipher
类的getInstance
方法中,通过Security.addProvider
添加Bouncy Castle提供者:
java
public static Cipher getInstance(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException {
// 确保Bouncy Castle提供者已添加
Security.addProvider(new BouncyCastleProvider());
// 通过ServiceLoader查找Cipher实现
ServiceLoader<CipherSpi> loader = ServiceLoader.load(CipherSpi.class);
// 其他逻辑...
}
13.2 SQLCipher集成
SQLCipher是Android常用的加密SQLite库。在android.database.sqlite
包中,通过JNI调用SQLCipher的加密函数:
c
// SQLCipher加密初始化
static void sqlite3_android_key(sqlite3 *db, const void *pKey, int nKey) {
// 调用SQLCipher的key函数
sqlite3_key(db, pKey, nKey);
// 检查加密状态
int rc = sqlite3_exec(db, "PRAGMA cipher_compatibility = 3;", 0, 0, 0);
if (rc != SQLITE_OK) {
// 错误处理
}
}
十四、攻防对抗与安全加固
14.1 防止逆向工程
Android通过代码混淆和反调试技术防止逆向工程。在proguard-rules.pro
文件中添加混淆规则:
kotlin
# 混淆加密相关类
-keep class javax.crypto.** { *; }
-keep class java.security.** { *; }
# 防止关键类被重命名
-keep class android.security.keystore.** { *; }
在Native层,通过ptrace
系统调用检测调试器:
c
void anti_debug() {
if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
// 检测到调试器,采取防御措施
exit(1);
}
}
14.2 对抗内存攻击
为防止密钥在内存中被提取,Android在Native层采用内存保护技术。例如使用mprotect
设置内存页为只读:
c
void protect_memory_region(void *addr, size_t size) {
// 计算内存页边界
size_t page_size = getpagesize();
void *page_addr = (void *)((uintptr_t)addr & ~(page_size - 1));
// 设置内存页为只读
if (mprotect(page_addr, size, PROT_READ) == -1) {
// 错误处理
}
}
十五、应用开发最佳实践
15.1 敏感数据加密存储示例
以下是一个完整的Android应用敏感数据加密存储示例:
java
import android.content.Context;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import androidx.security.crypto.EncryptedFile;
import androidx.security.crypto.MasterKey;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
public class SecureStorageHelper {
private static final String KEY_ALIAS = "my_app_master_key";
private final Context context;
private final MasterKey masterKey;
public SecureStorageHelper(Context context) throws Exception {
this.context = context;
// 生成或获取主密钥
masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
}
// 加密并存储数据到文件
public void saveSecureData(String data, String fileName) throws IOException {
File file = new File(context.getFilesDir(), fileName);
EncryptedFile encryptedFile = new EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build();
try (OutputStream outputStream = encryptedFile.openFileOutput()) {
outputStream.write(data.getBytes());
}
}
// 从加密文件读取数据
public String readSecureData(String fileName) throws IOException {
File file = new File(context.getFilesDir(), fileName);
EncryptedFile encryptedFile = new EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build();
try (InputStream inputStream = encryptedFile.openFileInput()) {
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
return new String(data);
}
}
// 使用AndroidKeyStore生成密钥
public void generateKeyInKeyStore() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(spec);
keyGenerator.generateKey();
}
// 使用AndroidKeyStore中的密钥加密数据
public byte[] encryptWithKeyStoreKey(String data) throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = cipher.doFinal(data.getBytes());
byte[] iv = cipher.getIV();
// 将IV和加密数据合并存储
byte[] result = new byte[iv.length + encryptedData.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(encryptedData, 0, result, iv.length, encryptedData.length);
return result;
}
// 使用AndroidKeyStore中的密钥解密数据
public String decryptWithKeyStoreKey(byte[] encryptedData) throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
// 分离IV和加密数据
int ivLength = 12; // GCM IV长度通常为12字节
byte[] iv = new byte[ivLength];
byte[] ciphertext = new byte[encryptedData.length - ivLength];
System.arraycopy(encryptedData, 0, iv, 0, ivLength);
System.arraycopy(encryptedData, ivLength, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
return new String(cipher.doFinal(ciphertext));
}
}
15.2 安全配置指南
在AndroidManifest.xml
中配置安全相关属性:
xml
<application
android:allowBackup="false"
android:fullBackupContent="false"
android:extractNativeLibs="false"
android:hardwareAccelerated="false"
android:usesCleartextTraffic="false"
android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config">
<!-- 其他应用组件 -->
</application>
在res/xml/network_security_config.xml
中配置网络安全策略:
xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<trust-anchors>
<certificates src="@raw/my_ca" />
</trust-anchors>
</domain-config>
</network-security-config>