android Pbkdf2,在C夏普的AES加密和在Android中使用PBKDF2WithHmacSHA1解密

博客讨论了一位开发者在尝试使用C#和Android之间实现加密解密互操作时遇到的问题。C#代码使用Rfc2898DeriveBytes和AES-CBC-PKCS7进行加密,而Android代码使用PBKDF2WithHmacSHA1。开发者注意到,尽管解密过程看似相同,但仍出现BadPaddingException。问题在于,IV应该随机生成且不应依赖密码,而当前代码中IV是基于密码生成的。博客强调了理解加密基础知识的重要性,包括盐、IV的用途以及PBKDF2的正确使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下是C#Cryptor的代码,它使用256位长密钥,128位长IV,5000次迭代。它使用Rfc2898DeriveBytes类,因此它与Android中的PBKDF2WithHmacSHA1相同。

C#Cryptor的解密函数将其作为IV作为256位长密钥的(反向的)前128位。

namespace CompanyName.Framework.Encryption

{

internal class SymmetricCryptor : ISymmetricCryptor

{

internal static int KeyLengthInBytes = 32;

internal int Iterations = 5000;

#region Private Fields

// RijndaelManaged aes; old version

AesManaged aes;

int IVLength = KeyLengthInBytes >> 1;

#endregion Private Fields

#region Internal Constructors

internal SymmetricCryptor()

{

aes = new AesManaged

{

Mode = CipherMode.CBC,

KeySize= KeyLengthInBytes<<3,

Padding = PaddingMode.PKCS7,

};

//aes.KeySize = KeyLengthInBytes << 3;

//aes.Padding = PaddingMode.Zeros; //PKCS7 can not be used with stream

}

#endregion Internal Constructors

#region Public Methods

public byte[] Decrypt(byte[] cryptedData, string password, IVMode ivmode)

{

using (MemoryStream ms = new MemoryStream(cryptedData))

{

using (MemoryStream data = new MemoryStream())

{

Decrypt(ms, data, password,ivmode);

return data.ToArray();

}

}

}

public void Encrypt(Stream data, Stream trgStream, string password, IVMode ivmode)

{

try

{

var key = GetKey(password);

var iv = (ivmode == IVMode.Auto)

?key.GetBytes(IVLength).Reverse().ToArray()

: new byte[IVLength];

var dc = aes.CreateEncryptor(key.GetBytes(KeyLengthInBytes), iv);

using (CryptoStream cryptor = new CryptoStream(trgStream, dc, CryptoStreamMode.Write))

{

data.CopyTo(cryptor);

cryptor.FlushFinalBlock();

cryptor.Close();

}

}

catch (Exception)

{

throw new InvalidOperationException("Invalid password.");

}

}

public void Decrypt(Stream cryptedData, Stream trgStream, string password, IVMode ivmode)

{

try

{

var key= GetKey(password);

var iv = (ivmode == IVMode.Auto)

? key.GetBytes(IVLength).Reverse().ToArray()

: new byte[IVLength];

var dc = aes.CreateDecryptor(key.GetBytes(KeyLengthInBytes),iv);

using (CryptoStream cryptor = new CryptoStream(cryptedData, dc, CryptoStreamMode.Read))

{

cryptor.CopyTo(trgStream);

cryptor.Close();

}

}

catch (Exception)

{

throw new InvalidOperationException("Invalid password.");

}

}

public byte[] Encrypt(byte[] data, string password, IVMode ivmode)

{

using (MemoryStream ms = new MemoryStream(data))

{

using (MemoryStream cData = new MemoryStream())

{

Encrypt(ms, cData, password,ivmode);

return cData.ToArray();

}

}

}

#endregion Public Methods

#region Private Methods

private Rfc2898DeriveBytes GetKey(string password)

{

try

{

var iv =

CompanyName.Framework.Cryptography.Digest.SHA1.Compute(password);

return new Rfc2898DeriveBytes(password, iv, Iterations);

}

catch (Exception)

{

throw;

}

}

#endregion Private Methods

}

}

我的Android Cryptor,它试图解密由上述C夏普Cryptor看起来像这样加密的消息,我试图将C夏普Cryptor的解密方法,包括:

public class Cryptor {

private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";

private static final String AES = "AES";

private static final String RANDOM_ALGO = "SHA1PRNG";

private static final int KEY_LENGTH_IN_BITS = 256;

private static final int IV_LENGTH = 16;

private static final int PBE_ITERATION_COUNT = 5000;

private static final int PBE_SALT_LENGTH_INT_BITS = 128;

private static final String PBE_ALGO = "PBKDF2WithHmacSHA1";

public static byte[] generateKeyFromPassword(String password, int Size) throws GeneralSecurityException {

byte[] salt = generateSalt();

KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, Size);

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBE_ALGO);

byte[] data = keyFactory.generateSecret(keySpec).getEncoded();

return data;

}

private static byte[] generateSalt() throws GeneralSecurityException {

return randomBytes(PBE_SALT_LENGTH_INT_BITS);

}

private static byte[] randomBytes(int length) throws GeneralSecurityException {

SecureRandom random = SecureRandom.getInstance(RANDOM_ALGO);

byte[] b = new byte[length];

random.nextBytes(b);

return b;

}

public static byte[] decrypt(byte[] cipherText, String password) throws GeneralSecurityException {

byte[] keyBytes = generateKeyFromPassword(password, 256);

byte[] ivBytes = generateKeyFromPassword(password, 128);

Cipher cipher = Cipher.getInstance(TRANSFORMATION);

ivBytes = reverse(ivBytes);

SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES);

IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);

byte[] decrypted = cipher.doFinal(cipherText);

return decrypted;

}

public static byte[] reverse(byte[] array) {

if (array == null) {

return null;

}

int i = 0;

int j = array.length - 1;

byte tmp;

while (j > i) {

tmp = array[j];

array[j] = array[i];

array[i] = tmp;

j--;

i++;

}

return array;

}

但它没有工作,当最后被称为我得到一个

javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT

例外。我不知道我在做什么错,因为我在Android中的Decrypt方法与C Sharp中的Decrypt方法完全相同:首先,我从由Csharp服务器和我共享的密码生成密钥。然后我生成一个随机的128位IV,反转它不是必须的,但C Sharp实现反转它,所以我也这样做。谁能告诉我我做错了什么?这里是我使用Cryptor背景:

//open the client channel, read and return the response as byte[]

Channel clientChannel = new Channel(serverAddress);

byte[] result = clientChannel.execute(serviceID.toString(), data);

//result[] is encrypted data. firstTen is the shared Password

byte[] decrypted = Cryptor.decrypt(result, firstTen);

服务器返回结果为Base64加密,通过它进行解密之前,我通过结果[]数组:它配备为Base64字符串 。我得到的结果[]数组通过:

Base64.decode(result, Base64.NO_WRAP);

2016-03-08

Gravity

+1

而不是试图找出C#和Android之间的区别,你最好先解决你的加密问题。初始化向量应该是随机数据,不依赖于密码。否则,它不能达到目的,因为相同的纯文本和密码将始终导致相同的输出。 PBKDF2是为了散列需要存储的密码而构建的。当你从密码中得到盐时,你会失败盐析的目的。而在目前的情况下,我根本看不到PBKDF2的任何需求。什么是“反向”?你认为它会增加安全性吗? –

+0

是的你是对的,我已经更新了我的代码,现在随机产生一个128位的IV。在C Sharp Code中,它已经随机生成。但我仍然得到提到的例外。你是什​​么意思,你没有看到PBKDF2的需要。我们与C Sharp服务器有共享密码,我需要使用这个共享密码来生成密钥和IV密码?除了使用PBKDF2以外,我该怎么做?反转是没有必要的,但服务器做到了,所以我也做到了。 C代码不是我的代码,Java代码是我的代码 –

+0

如果您使用密码生成salt或IV,则您尚未理解salt和IV的用途。它打败了它的目的。请研究加密的基础知识并重新访问您的设计。 (对不起,我太生硬了......) –

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值