2016-03-08 87 views
1

我正在使用Android,我試圖解密在C夏普服務器中加密的郵件。在C夏普的AES加密和在Android中使用PBKDF2WithHmacSHA1解密

以下是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); 
+1

而不是試圖找出C#和Android之間的區別,你最好先解決你的加密問題。初始化向量應該是隨機數據,不依賴於密碼。否則,它不能達到目的,因爲相同的純文本和密碼將始終導致相同的輸出。 PBKDF2是爲了散列需要存儲的密碼而構建的。當你從密碼中得到鹽時,你會失敗鹽析的目的。而在目前的情況下,我根本看不到PBKDF2的任何需求。什麼是「反向」?你認爲它會增加安全性嗎? – Codo

+0

是的你是對的,我已經更新了我的代碼,現在隨機產生一個128位的IV。在C Sharp Code中,它已經隨機生成。但我仍然得到提到的例外。你是什​​麼意思,你沒有看到PBKDF2的需要。我們與C Sharp服務器有共享密碼,我需要使用這個共享密碼來生成密鑰和IV密碼?除了使用PBKDF2以外,我該怎麼做?反轉是沒有必要的,但服務器做到了,所以我也做到了。 C代碼不是我的代碼,Java代碼是我的代碼 – Gravity

+0

如果您使用密碼生成salt或IV,則您尚未理解salt和IV的用途。它打敗了它的目的。請研究加密的基礎知識並重新訪問您的設計。 (對不起,我太生硬了......) – Codo

回答

0

您需要生成服務器端隨機鹽和IV與ciphreText送它到Android的一面。 Android需要使用完全相同的salt和IV來派生用於派生服務器端加密密鑰的解密密鑰。

+0

也發送迭代計數。 – zaph