You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android平台解密AES加密EPUB文件并基于EpubReader解析的技术咨询

在Android上解密AES加密EPUB并通过epublib展示的完整方案

嘿,我帮你梳理下实现这个需求的具体步骤,包括核心的解密逻辑、epublib的使用,还有容易踩的坑,都是实际开发里总结的经验:

一、前期准备要做足

  • 先确认加密参数:既然你用的是和Java源码一致的16位密钥,那对应的应该是AES-128算法。重点要保证加密和解密的模式、填充方式、初始化向量(IV)完全一致——比如加密用的是CBC模式+PKCS5Padding填充,解密时也得一模一样,不然肯定解不开。
  • 添加上依赖:在你的build.gradle(app)里把epublib的依赖加上,注意选适配Android的版本:
    implementation 'nl.siegmann.epublib:epublib-core:3.1'
    // 如果EPUB里有图片、字体这些资源,再加这个依赖
    implementation 'nl.siegmann.epublib:epublib-resources:3.1'
    
  • 权限这块不用太担心:用App的缓存目录存临时解密文件就行,Android 10+不需要额外申请存储权限;如果是老版本(Android 9及以下),最多加个WRITE_EXTERNAL_STORAGE,但优先用缓存目录更安全,也不会有权限问题。

二、核心步骤一步步来

1. 先从Assets拿到加密文件的流

Assets里的文件不能直接读写,得用AssetManager读取成输入流:

AssetManager assetManager = getAssets();
InputStream encryptedInputStream = assetManager.open("你的加密EPUB文件名.aes");

2. 写好AES解密的方法

这里是关键,必须和你加密时的参数完全匹配。假设你加密时用的是16位密钥直接转字节、IV和密钥相同(如果加密时用了其他IV,替换成对应的就行),示例代码如下:

private InputStream decryptAES(InputStream inputStream, String key) throws Exception {
    // 把16位密钥转成字节数组
    byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
    // 这里的IV要和加密时用的完全一致,我假设和密钥相同,你按需替换
    byte[] ivBytes = key.getBytes(StandardCharsets.UTF_8);
    
    // 初始化Cipher,参数要和加密时一致
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
    
    // 用CipherInputStream包装原输入流,实现边读边解密,省内存
    return new CipherInputStream(inputStream, cipher);
}

划重点:如果你是用AES Crypt客户端加密的文件,它的加密文件有自己的专属文件头,不是纯AES加密的EPUB!这时候你得先读取并跳过文件头的固定字节,再进行AES解密——不然解密出来的内容会乱码,EPUB肯定打不开。

3. 把解密后的内容写到临时文件

epublib可以直接读解密后的流,但写成临时文件会更稳定,尤其是处理大体积的EPUB:

private File writeToTempFile(InputStream inputStream) throws IOException {
    // 在缓存目录创建临时EPUB文件,后缀一定要用.epub
    File tempFile = File.createTempFile("decrypted_book", ".epub", getCacheDir());
    FileOutputStream outputStream = new FileOutputStream(tempFile);
    
    // 用4KB的缓冲区读写,效率比较高
    byte[] buffer = new byte[4096];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
    
    // 记得关流,不然会占资源
    outputStream.close();
    inputStream.close();
    
    return tempFile;
}

4. 用epublib加载EPUB并展示到WebView

epublib会帮你解析EPUB的结构,你可以拿到每个章节的内容,然后渲染到WebView:

private void loadEpubIntoWebView(File epubFile) throws Exception {
    // 初始化EPUB阅读器
    EpubReader reader = new EpubReader();
    Book book = reader.readEpub(new FileInputStream(epubFile));
    
    // 这里拿第一个章节做示例,你可以遍历所有章节做目录或者连续加载
    Resource firstChapter = book.getContents().get(0);
    String chapterContent = new String(firstChapter.getData(), StandardCharsets.UTF_8);
    
    // 配置WebView并加载内容
    WebView webView = findViewById(R.id.web_view);
    webView.getSettings().setJavaScriptEnabled(true);
    // 如果章节里有相对路径的资源,把baseURL改成临时文件的目录,不然图片之类的加载不出来
    webView.loadDataWithBaseURL("file://" + getCacheDir() + "/", chapterContent, "text/html", "UTF-8", null);
    
    // 可选:设置临时文件退出时自动删除,避免占缓存
    epubFile.deleteOnExit();
}

三、容易踩的坑和排查方法

  • 解密后EPUB打不开?
    • 先核对加密和解密的所有参数:密钥、IV、模式、填充方式,只要有一个不一样就解不对
    • 检查是不是AES Crypt的文件头没处理:如果是客户端加密的,先跳过开头的固定标识字节再解密
  • 解密耗时久导致ANR?
    • 所有解密、写文件的操作都要放在子线程里执行!用Thread、Coroutine(Kotlin)或者AsyncTask(虽然过时但能用),绝对不能在主线程做这些耗时操作
    • 可以把缓冲区调大一点,比如从4KB改成8KB,能提升读写效率
  • WebView显示异常,图片、样式加载不出来?
    • 记得设置loadDataWithBaseURL的base参数为临时文件所在的目录,这样相对路径的资源才能正确加载
    • 检查EPUB章节的HTML是不是标准格式,有些EPUB的章节可能有特殊标签,WebView需要开启相应的设置(比如允许加载本地文件)

四、完整流程的调用示例(子线程执行)

把所有步骤串起来,一定要放在子线程里:

new Thread(() -> {
    try {
        AssetManager assetManager = getAssets();
        InputStream encryptedStream = assetManager.open("encrypted_book.aes");
        InputStream decryptedStream = decryptAES(encryptedStream, "你的16位密钥");
        File tempEpub = writeToTempFile(decryptedStream);
        
        // 回到主线程更新UI,Android不能在子线程操作UI
        runOnUiThread(() -> {
            try {
                loadEpubIntoWebView(tempEpub);
            } catch (Exception e) {
                e.printStackTrace();
                // 这里可以加个错误提示,比如Toast告诉用户加载失败
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
        // 解密失败的处理,比如提示密钥错误或者文件损坏
    }
}).start();

内容的提问来源于stack exchange,提问作者Lee Harding

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:17:43