Android Runtime敏感数据加密存储源码级解析(89)

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中,定义了getInstanceinitdoFinal等关键方法。

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_CipherUpdateEVP_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);
        }
    }
}

运行时权限验证在ActivityManagerServicecheckPermission方法中实现:

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>
相关推荐
天天摸鱼的java工程师1 小时前
面试官说:“设计一个消息中间件你会怎么做?”我当场就不困了 ☕️🚀
java·后端·面试
七七&5562 小时前
Spring全面讲解(无比详细)
android·前端·后端
独立开阀者_FwtCoder2 小时前
Vue 抛弃虚拟 DOM,底层到底换成啥了?怎么更新 DOM?
前端·面试·github
熬了夜的程序员2 小时前
【华为机试】240. 搜索二维矩阵 II
线性代数·算法·华为·面试·矩阵·golang·深度优先
蒟蒻小袁3 小时前
力扣面试150题--搜索二维矩阵
leetcode·面试·矩阵
朱涛的自习室3 小时前
新一代 Agentic AI 智能体,助力 Android 开发 | Google I/O
android·android studio·ai编程
安卓开发者4 小时前
OkHttp 与 Glide 完美结合:打造高效的 Android 图片加载方案
android·okhttp·glide
安卓开发者4 小时前
OkHttp 与 Stetho 结合使用:打造强大的 Android 网络调试工具链
android·okhttp
过期动态4 小时前
MySQL中的排序和分页
android·java·数据库·mysql·adb
Littlewith4 小时前
Node.js:常用工具、GET/POST请求的写法、工具模块
java·服务器·开发语言·c++·面试·node.js