pan*_*dre 34 android cryptography bouncycastle
首先,我已经看到 Android 4.2 在Android 4.2 和提供的解决方案上破坏了我的AES加密/解密代码 和 加密错误:
SecureRandom sr = null;
if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
} else {
sr = SecureRandom.getInstance("SHA1PRNG");
}
Run Code Online (Sandbox Code Playgroud)
对我不起作用,因为,当解码Android 4.2中Android <4.2加密的数据时,我得到:
javax.crypto.BadPaddingException: pad block corrupted
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)
Run Code Online (Sandbox Code Playgroud)
我的代码很简单,直到Android 4.2才开始工作:
public static byte[] encrypt(byte[] data, String seed) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(data);
}
public static byte[] decrypt(byte[] data, String seed) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(data);
}
Run Code Online (Sandbox Code Playgroud)
我的猜测是默认提供程序并不是Android 4.2中唯一改变的,否则我的代码将与提议的解决方案一起使用.
我的代码基于我很久以前在StackOverflow上找到的一些帖子; 我看到它与上述帖子不同,因为它只是对字节数组进行加密和解密,而其他解决方案则加密和解密字符串(我认为是HEX字符串).
它与种子有关吗?它是否具有最小/最大长度,字符限制等?
任何想法/解决方案?
编辑:经过大量的测试,我发现有两个问题:
在Android 4.2(API 17)中更改了提供程序 - >这个很容易修复,只需应用我在帖子顶部提到的解决方案
在Android 2.2(API 8) - > Android2.3(API 9)中,BouncyCastle从1.34变为1.45,因此我之前所说的解密问题与此处描述的相同:升级到1.45时出现BouncyCastle AES错误
所以现在的问题是:有没有办法恢复BouncyCastle 1.34 +中BouncyCastle 1.34中加密的数据?
kro*_*oot 45
首先是免责声明:
不要永远使用SecureRandom派生的关键!这是破碎的,没有意义!
来自问题的以下代码块试图从密码确定性地导出密钥,称为"种子",因为密码用于"种子"随机数生成器.
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
Run Code Online (Sandbox Code Playgroud)
然而,该"SHA1PRNG"算法没有很好地定义,并且结果的实现"SHA1PRNG"可能返回不同的甚至完全随机的密钥.
如果您正在从磁盘读取AES密钥,只需存储实际密钥,不要经历这种奇怪的舞蹈.您可以SecretKey通过执行以下操作从字节中获取AES使用情况:
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Run Code Online (Sandbox Code Playgroud)
如果您使用密码来获取密钥,请遵循 Nelenkov的优秀教程,但需要注意的是,经验法则是盐的大小应与密钥输出的大小相同.
的iterationCount(工作因子)当然受到的改变,应立即更换为CPU供电的进展-通常建议不要去低于40到100K为2018年要注意的是PBKDF2只增加了一个固定的时间延迟猜测的密码; 它不是真正弱密码的替代品.
它看起来像这样:
/* User types in their password: */
String password = "password";
/* Store these things on disk used to derive key later: */
int iterationCount = 1000;
int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32)
int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc
byte[] salt; // Should be of saltLength
/* When first creating the key, obtain a salt with this: */
SecureRandom random = new SecureRandom();
byte[] salt = new byte[saltLength];
random.nextBytes(salt);
/* Use this to derive the key from the password: */
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Run Code Online (Sandbox Code Playgroud)
而已.你不应该使用的任何其他东西.
Gio*_*gio 11
问题是使用新的提供程序,以下代码片段
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();
Run Code Online (Sandbox Code Playgroud)
rawKey每次执行时都会生成一个不同的,真正随机的.因此,您尝试使用与用于加密数据的密钥不同的密钥进行解密,并获得异常.以这种方式生成密钥或数据时,您将无法恢复密钥或数据,并且只保存了种子.
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 256;
private static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final int PKCS5_SALT_LENGTH = 32;
private static final String DELIMITER = "]";
private static final SecureRandom random = new SecureRandom();
public static String encrypt(String plaintext, String password) {
byte[] salt = generateSalt();
SecretKey key = deriveKey(password, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
byte[] iv = generateIv(cipher.getBlockSize());
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));
if(salt != null) {
return String.format("%s%s%s%s%s",
toBase64(salt),
DELIMITER,
toBase64(iv),
DELIMITER,
toBase64(cipherText));
}
return String.format("%s%s%s",
toBase64(iv),
DELIMITER,
toBase64(cipherText));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String ciphertext, String password) {
String[] fields = ciphertext.split(DELIMITER);
if(fields.length != 3) {
throw new IllegalArgumentException("Invalid encypted text format");
}
byte[] salt = fromBase64(fields[0]);
byte[] iv = fromBase64(fields[1]);
byte[] cipherBytes = fromBase64(fields[2]);
SecretKey key = deriveKey(password, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherBytes);
return new String(plaintext, "UTF-8");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private static byte[] generateSalt() {
byte[] b = new byte[PKCS5_SALT_LENGTH];
random.nextBytes(b);
return b;
}
private static byte[] generateIv(int length) {
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
private static SecretKey deriveKey(String password, byte[] salt) {
try {
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM);
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
return new SecretKeySpec(keyBytes, "AES");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private static String toBase64(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP);
}
private static byte[] fromBase64(String base64) {
return Base64.decode(base64, Base64.NO_WRAP);
}
Run Code Online (Sandbox Code Playgroud)
我无法回答您提出的问题,但我只是尝试解决这个问题> - 如果您在跨设备/操作系统版本上遇到 bouncycastle 的一些问题,您应该完全放弃内置版本,而是将 bouncycastle 添加为 jar到你的项目,改变你import指向那个jar,重建并假设它一切正常,从现在开始你将不受android内置版本更改的影响。
| 归档时间: |
|
| 查看次数: |
26576 次 |
| 最近记录: |