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

Android Java开发如何使用自定义密钥实现AES-256加密?

认知错误修正
  • AES-256要求的密钥是32字节,你提到的32/64位为字符长度,若为ASCII字符32位刚好对应32字节,但需要注意字符编码统一,避免密钥不匹配
  • 不使用IV的方案仅对应AES的ECB模式,该模式安全性极低,相同明文加密后输出完全一致,很容易被破解,完全不符合密码管理器的高安全需求
  • 不使用salt存在彩虹表攻击风险,加盐是低成本高收益的安全加固手段,高安全场景必须使用
  • 教程中的随机密钥适用于临时加密场景,密码管理器的密钥是从用户主密码派生而来,解密时使用相同主密码、相同派生参数即可得到一致密钥完成解密,不需要存储密钥
安全实现方案

采用PBKDF2从主密码派生AES密钥 + AES-CBC/PKCS7Padding模式实现,随机生成的salt和IV随密文拼接存储,不需要额外维护其他参数,完全适配密码管理器场景。

import android.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

// 加密方法,入参为用户输入的主密码、待加密的账号密码明文
public static String encrypt(String masterPassword, String plaintext) throws Exception {
    // 生成16字节随机salt
    SecureRandom random = new SecureRandom();
    byte[] salt = new byte[16];
    random.nextBytes(salt);

    // 生成16字节随机IV
    byte[] iv = new byte[16];
    random.nextBytes(iv);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);

    // PBKDF2派生32字节AES-256密钥,迭代次数可根据设备性能调高提升安全性
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(masterPassword.toCharArray(), salt, 10000, 256);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");

    // AES加密
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
    byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));

    // 拼接salt+IV+密文转Base64返回,直接存储该字符串即可
    byte[] result = new byte[salt.length + iv.length + ciphertext.length];
    System.arraycopy(salt, 0, result, 0, salt.length);
    System.arraycopy(iv, 0, result, salt.length, iv.length);
    System.arraycopy(ciphertext, 0, result, salt.length + iv.length, ciphertext.length);
    return Base64.encodeToString(result, Base64.DEFAULT);
}

// 解密方法,入参为用户输入的主密码、存储的加密字符串
public static String decrypt(String masterPassword, String encryptedStr) throws Exception {
    byte[] result = Base64.decode(encryptedStr, Base64.DEFAULT);

    // 拆分salt、IV、密文
    byte[] salt = new byte[16];
    System.arraycopy(result, 0, salt, 0, 16);
    byte[] iv = new byte[16];
    System.arraycopy(result, 16, iv, 0, 16);
    byte[] ciphertext = new byte[result.length - 32];
    System.arraycopy(result, 32, ciphertext, 0, ciphertext.length);

    IvParameterSpec ivSpec = new IvParameterSpec(iv);

    // 相同参数派生密钥
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(masterPassword.toCharArray(), salt, 10000, 256);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");

    // 解密返回明文
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
    byte[] plaintext = cipher.doFinal(ciphertext);
    return new String(plaintext, "UTF-8");
}
注意事项

以下代码仅作演示,极度不推荐用于生产环境,如果你坚持使用无salt无IV的方案,可使用ECB模式实现:

public static String unsafeEncrypt(String masterPassword, String plaintext) throws Exception {
    // 假设主密码为32位ASCII字符,刚好对应32字节密钥
    SecretKeySpec secretKey = new SecretKeySpec(masterPassword.getBytes("UTF-8"), "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));
    return Base64.encodeToString(ciphertext, Base64.DEFAULT);
}

public static String unsafeDecrypt(String masterPassword, String encryptedStr) throws Exception {
    SecretKeySpec secretKey = new SecretKeySpec(masterPassword.getBytes("UTF-8"), "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);
    byte[] plaintext = cipher.doFinal(Base64.decode(encryptedStr, Base64.DEFAULT));
    return new String(plaintext, "UTF-8");
}
  • 上述安全方案兼容Android 6.0及以上系统,不需要额外引入依赖
  • 主密码长度不足32字节时不要自行补0填充,使用PBKDF2派生是最安全的处理方式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 10:48:03