C Sharp中的AES加密和使用PBKDF2WithHmacSHA1的Android中的解密

时间:2016-03-08 10:43:38

标签: c# android cryptography aes pbkdf2

我正在使用Android,而我正在尝试解密在C Sharp服务器中加密的邮件。

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

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

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 Sharp Cryptor加密的邮件,我试图复制C Sharp Cryptor的Decrypt方法:

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 Server和我共享。然后我生成一个随机的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);

1 个答案:

答案 0 :(得分:0)

您需要在服务器端生成随机salt和IV,并将其与ciphreText一起发送到Android端。 Android需要使用完全相同的salt和IV来派生用于在服务器端派生加密密钥的解密密钥。