Android EntropyService
在 Android 系统中,EntropyService(后更名为 EntropyMixer)是保障随机数安全性的核心服务之一。
它通过维护内核熵池(Entropy Pool),解决了系统启动初期随机数可预测的问题,为加密、安全认证等场景提供高质量随机数。
本文将从Framework 层(Java 实现)和C++ 层(Native 依赖)深入解析其源码逻辑。
什么是熵池?
Linux 内核维护一个熵池(内存中的随机数据池),收集硬件中断、设备噪声等不可预测的“熵源”,生成/dev/random(阻塞式,熵不足时等待)和/dev/urandom(非阻塞式,熵不足时复用已有数据)设备文件。
但系统启动初期,熵池为空,直接生成的随机数易被预测。EntropyService 的核心作用:通过持久化熵数据、补充设备信息,确保熵池始终有足够的“随机性”。
Framework 层实现
1 服务注册:SystemServer 中启动
EntropyService 是 SystemServer 启动的核心服务之一,在SystemServer.java的startOtherServices()方法中注册:
// frameworks/base/services/java/com/android/server/SystemServer.javaServiceManager.addService("entropy",newEntropyMixer(context));2 构造函数:核心逻辑入口
EntropyMixer的构造函数定义了服务的核心参数和初始化流程:
// frameworks/base/services/core/java/com/android/server/EntropyMixer.javapublicEntropyMixer(Contextcontext){// 默认参数:熵数据文件、随机设备、硬件随机设备this(context,getSystemDir()+"/entropy.dat","/dev/urandom","/dev/hw_random");}publicEntropyMixer(Contextcontext,StringentropyFile,StringrandomDevice,StringhwRandomDevice){this.randomDevice=randomDevice;// 对应 /dev/urandomthis.hwRandomDevice=hwRandomDevice;// 硬件随机源(如 /dev/hw_random)this.entropyFile=entropyFile;// 持久化熵数据的文件:/data/system/entropy.dat// 核心初始化流程loadInitialEntropy();// 1. 加载历史熵数据到熵池addDeviceSpecificEntropy();// 2. 补充设备信息到熵池addHwRandomEntropy();// 3. 补充硬件随机源到熵池writeEntropy();// 4. 持久化当前熵池数据scheduleEntropyWriter();// 5. 定时更新熵数据registerShutdownReceiver();// 6. 注册关机/重启广播,触发持久化}3 核心方法解析
(1)loadInitialEntropy():加载历史熵数据
系统启动时,将/data/system/entropy.dat中的历史熵数据写入/dev/urandom,填充空熵池:
privatevoidloadInitialEntropy(){try{// 将文件数据写入随机设备RandomBlock.fromFile(entropyFile).toFile(randomDevice,false);Slog.i(TAG,"Loaded initial entropy from "+entropyFile);}catch(IOExceptione){Slog.w(TAG,"Failed to load initial entropy",e);}}(2)addDeviceSpecificEntropy():补充设备信息
向熵池写入设备唯一信息(如序列号、硬件型号等),增加随机性:
privatevoidaddDeviceSpecificEntropy(){try(BufferedWriterout=newBufferedWriter(newOutputStreamWriter(newFileOutputStream(randomDevice)))){// 写入固定字符串、启动时间、设备属性等out.write("All Your Randomness Are Belong To Us\n");out.write(System.currentTimeMillis()+"\n");out.write(System.nanoTime()+"\n");out.write(SystemProperties.get("ro.serialno")+"\n");out.write(SystemProperties.get("ro.hardware")+"\n");// ... 其他设备属性out.flush();}catch(IOExceptione){Slog.w(TAG,"Failed to add device entropy",e);}}(3)addHwRandomEntropy():补充硬件随机源
若设备支持硬件随机数生成器(如/dev/hw_random),将其数据写入熵池:
privatevoidaddHwRandomEntropy(){try{RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice,false);Slog.i(TAG,"Added HW RNG output to entropy pool");}catch(FileNotFoundExceptionignored){// 无硬件随机源时忽略}catch(IOExceptione){Slog.w(TAG,"Failed to add HW RNG entropy",e);}}(4)writeEntropy()与scheduleEntropyWriter():持久化与定时更新
writeEntropy():读取/dev/urandom的数据,写入entropy.dat持久化;scheduleEntropyWriter():通过 Handler 每 3 小时触发一次持久化:
privatevoidwriteEntropy(){try{RandomBlock.fromFile(randomDevice).toFile(entropyFile,true);Slog.i(TAG,"Wrote entropy to "+entropyFile);}catch(IOExceptione){Slog.w(TAG,"Failed to write entropy",e);}}privatevoidscheduleEntropyWriter(){mHandler.removeMessages(ENTROPY_WHAT);// 3小时后触发mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT,3*60*60*1000);}privatefinalHandlermHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){if(msg.what==ENTROPY_WHAT){addHwRandomEntropy();writeEntropy();scheduleEntropyWriter();// 循环定时}}};C++ 层依赖
EntropyService 本身是 Java 实现,但依赖Linux 内核的随机数设备和Native 层的文件操作(通过 JNI 调用)。
1 内核随机数设备:/dev/random与/dev/urandom
这两个设备由 Linux 内核的random.c实现(位于kernel/drivers/char/random.c),核心逻辑:
- 收集“熵源”(如中断时间、磁盘 I/O 延迟)到熵池;
- 提供
read()接口(/dev/random阻塞,/dev/urandom非阻塞); - EntropyService 本质是通过读写这些设备文件与内核熵池交互。
2 Native 层文件操作:RandomBlock 的底层实现
RandomBlock是 EntropyMixer 中封装的文件操作工具类,其底层通过JNI 调用 C++ 的文件读写函数(如open()、read()、write()),避免 Java I/O 的性能开销。
例如,RandomBlock.fromFile()的 Native 实现逻辑(简化):
// 伪代码:Native 层文件读取jbyteArrayRandomBlock_nativeRead(JNIEnv*env,jclass clazz,jstring path){constchar*cPath=env->GetStringUTFChars(path,nullptr);intfd=open(cPath,O_RDONLY);if(fd<0){/* 错误处理 */}charbuffer[4096];ssize_t n=read(fd,buffer,sizeof(buffer));close(fd);env->ReleaseStringUTFChars(path,cPath);jbyteArray result=env->NewByteArray(n);env->SetByteArrayRegion(result,0,n,(jbyte*)buffer);returnresult;}3 硬件随机源的 Native 驱动
若设备支持/dev/hw_random,其驱动由硬件厂商用 C 实现(位于kernel/drivers/char/hw_random/),核心是向内核熵池注入硬件生成的随机数据:
// 硬件随机源驱动伪代码staticvoidhw_rng_fill_entropy(structhw_rng*rng){u8 buffer[256];intn=rng->read(rng,buffer,sizeof(buffer));// 硬件读取随机数if(n>0){add_entropy_to_pool(buffer,n);// 注入内核熵池}}核心方法调用
以下是从系统启动到服务运行/终止的全流程方法调用时序:
阶段1:系统启动 → EntropyMixer初始化
1. `SystemServer.startOtherServices()` ↓ 调用 2. `EntropyMixer.<init>(Context)` ↓ 构造函数内依次触发 3. `loadInitialEntropy()` (加载`entropy.dat`到熵池) ↓ 完成后 4. `addDeviceSpecificEntropy()` (补充设备信息到熵池) ↓ 完成后 5. `addHwRandomEntropy()` (补充硬件随机源到熵池) ↓ 完成后 6. `writeEntropy()` (首次持久化当前熵池到`entropy.dat`) ↓ 完成后 7. `scheduleEntropyWriter()` (启动3小时周期的定时任务) ↓ 完成后 8. `registerShutdownReceiver()` (注册关机/重启广播接收器)阶段2:定时任务执行(每3小时)
1. `mHandler`接收`ENTROPY_WHAT`消息 ↓ 处理消息时触发 2. `addHwRandomEntropy()` (再次补充硬件随机源) ↓ 完成后 3. `writeEntropy()` (更新持久化的`entropy.dat`) ↓ 完成后 4. `scheduleEntropyWriter()` (重新调度下一次定时任务)阶段3:系统关机/重启
1. 系统发送关机/重启广播 ↓ 广播接收器触发 2. `writeEntropy()` (最后一次持久化熵池数据)从应用到内核熵池的全链路解析
当 Android 系统应用需要生成随机数(如加密密钥、安全令牌)时,并非直接“创造”随机数据,而是通过多层组件协作,最终从内核熵池获取高质量随机数。
应用层:随机数的“入口”API
Android 应用层生成随机数的常用方式有两种:Java 标准库 API 和 Android 系统 API,两者最终都会关联到内核熵池。
1 Java 标准库:java.security.SecureRandom
这是应用层最常用的安全随机数生成器,默认绑定系统熵池:
// 应用层代码示例SecureRandomsecureRandom=newSecureRandom();byte[]randomBytes=newbyte[16];// 生成16字节(128位)随机数secureRandom.nextBytes(randomBytes);2 Android 系统 API:android.os.Build.VERSION.SDK_INT >= 26新增的StrongBox
针对高安全场景(如密钥存储),Android 9+ 提供StrongBox,底层直接调用硬件/内核级随机源:
// 应用层代码示例(需硬件支持)KeyGeneratorkeyGen=KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,"AndroidKeyStore");keyGen.init(newKeyGenParameterSpec.Builder("my_key",KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT).setIsStrongBoxBacked(true)// 强制使用StrongBox.build());SecretKeykey=keyGen.generateKey();// 密钥随机数来自StrongBox关联的熵池Framework 层
应用层调用的随机数 API,会先进入 Android Framework 层的安全框架,完成策略校验、熵源绑定等逻辑。
1SecureRandom的 Framework 层实现
SecureRandom的实际逻辑由sun.security.provider.SecureRandom(JDK 底层)和 Android 定制的android.security.SecureRandom共同实现:
- 第一步:
SecureRandom初始化时,会加载随机数算法(如SHA1PRNG); - 第二步:算法初始化需要“种子”(Seed),而种子默认从系统熵池获取(通过
/dev/urandom)。
Framework 层对SecureRandom的增强:
// Android 定制的 SecureRandom 逻辑(简化)publicclassSecureRandomextendsjava.security.SecureRandom{@OverrideprotectedvoidengineSetSeed(byte[]seed){// 强制将种子补充到内核熵池,避免弱种子NativeCrypto.RAND_seed(seed);super.engineSetSeed(seed);}}2 Framework 到 Native 层的过渡:JNI 调用
Framework 层的随机数逻辑,通过JNI(Java Native Interface)调用 Native 层的 OpenSSL/系统库,最终关联到内核设备文件。
例如,SecureRandom的nextBytes()会触发 JNI 调用:
// Framework 层 SecureRandom 的 JNI 绑定privatenativevoidnextBytes(byte[]bytes);对应的 Native 实现(位于libandroid_runtime.so):
// Native 层代码(简化)staticvoidSecureRandom_nextBytes(JNIEnv*env,jobject obj,jbyteArray bytes){jbyte*buf=env->GetByteArrayElements(bytes,nullptr);intlen=env->GetArrayLength(bytes);// 调用 OpenSSL 的 RAND_bytes,而 RAND_bytes 会读取 /dev/urandomif(RAND_bytes((unsignedchar*)buf,len)!=1){env->ThrowNew(env->FindClass("java/security/NoSuchAlgorithmException"),"RAND_bytes failed");}env->ReleaseByteArrayElements(bytes,buf,0);}Native 层
Native 层通过系统库(如 OpenSSL、Bionic libc)直接操作内核提供的随机数设备文件(/dev/random//dev/urandom),是连接 Framework 与内核的关键层。
1 OpenSSL 的 RAND 模块
Android 系统中的 OpenSSL 被定制为默认从/dev/urandom获取随机数:
// OpenSSL RAND 模块的实现(简化)intRAND_bytes(unsignedchar*buf,intnum){intfd=open("/dev/urandom",O_RDONLY);if(fd<0)return0;ssize_t n=read(fd,buf,num);// 从内核熵池读数据close(fd);return(n==num)?1:0;}2 Bionic libc 的getrandom()系统调用
Android 5.0+ 支持getrandom()系统调用(替代直接读写设备文件),它直接从内核熵池获取数据:
// Native 层使用 getrandom() 的示例#include<sys/random.h>ssize_tgetrandom(void*buf,size_t buflen,unsignedintflags);// 调用示例charrandom_buf[16];getrandom(random_buf,sizeof(random_buf),0);// 直接从内核熵池读内核层
所有随机数的最终来源是Linux 内核的熵池,由kernel/drivers/char/random.c实现,负责收集“熵源”并生成随机数据。
1 熵池的构成
内核熵池是一个256字节的内存池,通过收集以下“不可预测”的熵源填充:
- 硬件中断的时间差;
- 磁盘/网络 I/O 的延迟;
- 键盘输入、触摸屏操作的时间;
- EntropyService(EntropyMixer)补充的持久化数据、设备信息、硬件随机源。
2/dev/urandom与/dev/random的区别
/dev/urandom:非阻塞,当熵池不足时,复用已有数据并通过加密算法增强随机性(Android 应用默认使用);/dev/random:阻塞,直到熵池有足够新熵源(仅用于极高安全场景)。
3 EntropyService 对内核熵池的作用
如前文所述,EntropyService 会在系统启动时加载历史熵数据、补充设备/硬件熵源,确保内核熵池在启动初期就有足够的随机性,避免应用层获取弱随机数。
全链路总结
应用层代码 ↓ 调用 Framework 层(SecureRandom / StrongBox) ↓ JNI 调用 Native 层(OpenSSL / getrandom()) ↓ 系统调用 / 文件读写 内核层(/dev/urandom → 熵池) ↓ 读取熵池数据 返回随机数到应用层总结
- 系统启动:EntropyMixer 从
entropy.dat加载历史熵数据,填充内核熵池; - 补充熵源:写入设备信息、硬件随机数据,增强随机性;
- 持久化:将当前熵池数据写入
entropy.dat,并每 3 小时更新; - 关机/重启:通过广播触发持久化,确保下次启动时有足够熵数据。