以下是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的用途。它打败了它的目的。请研究加密的基础知识并重新访问您的设计。 (对不起,我太生硬了......) –