2015-01-23 15 views
7

Tôi thấy một tỷ lệ phần trăm nhỏ người dùng sản xuất ngẫu nhiên báo cáo ngoại lệ này liên quan đến việc mã hóa/giải mã chuỗi bằng Xamarin.Android nhưng tiếc là tôi không thể tạo lại nó.CryptographicException: Đệm PKCS7 không hợp lệ

Điều gì có thể gây ra điều này và/hoặc làm cách nào tôi có thể tạo lại ngoại lệ để tôi có thể tìm ra giải pháp sửa chữa/giải pháp?

[CryptographicException: Bad PKCS7 padding. Invalid length 147.] 
    Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException(PaddingMode padding, Int32 length, Int32 position):0 
    Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt(System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount):0 
    Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock(System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount):0 
    System.Security.Cryptography.CryptoStream.FlushFinalBlock():0 
    com.abc.mobile.shared.Security+PasswordEncoder.DecryptWithByteArray(System.String strText, System.String strEncrypt):0 

EDIT: Dưới đây là đoạn code tôi đang sử dụng để mã hóa/giải mã

private string EncryptWithByteArray(string inPassword, string inByteArray) 
    { 

     byte[] tmpKey = new byte[20]; 
     tmpKey = System.Text.Encoding.UTF8.GetBytes(inByteArray.Substring(0, 8)); 
     DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 
     byte[] inputArray = System.Text.Encoding.UTF8.GetBytes(inPassword); 
     MemoryStream ms = new MemoryStream(); 
     CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(tmpKey, mInitializationVector), CryptoStreamMode.Write); 
     cs.Write(inputArray, 0, inputArray.Length); 
     cs.FlushFinalBlock(); 
     return Convert.ToBase64String(ms.ToArray()); 

    } 

     private string DecryptWithByteArray (string strText, string strEncrypt) 
     { 

      try 
      { 
       byte[] tmpKey = new byte[20]; 
       tmpKey = System.Text.Encoding.UTF8.GetBytes (strEncrypt.Substring (0, 8)); 
       DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 
       Byte[] inputByteArray = Convert.FromBase64String (strText); 
       MemoryStream ms = new MemoryStream(); 
       CryptoStream cs = new CryptoStream (ms, des.CreateDecryptor (tmpKey, mInitializationVector), CryptoStreamMode.Write); 
       cs.Write (inputByteArray, 0, inputByteArray.Length); 
      try { 
       cs.FlushFinalBlock(); 
      } catch (Exception ex) { 
       throw(ex); 
      } 
      System.Text.Encoding encoding = System.Text.Encoding.UTF8; 
      return encoding.GetString(ms.ToArray()); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 
     } 

EDIT 2:

Chìa khóa mã hóa luôn là ID thiết bị địa phương. Đây là cách tôi đang nhận được điều này:

 TelephonyManager telephonyMgr = Application.Context.GetSystemService(Context.TelephonyService) as TelephonyManager; 
     string deviceId = telephonyMgr.DeviceId == null ? "UNAVAILABLE" : telephonyMgr.DeviceId; 

Dưới đây là một ví dụ về cách nó được gọi là:

string mByteArray = GetDeviceId(); 
string mEncryptedString = EncryptWithByteArray(stringToEncrypt, mByteArray); 
string mDecryptedString = DecryptWithByteArray(mEncryptedString, mByteArray); 
+0

Mật mã khối và phương thức hoạt động nào? Bạn có thể cung cấp một bản sao của thông báo không giải mã được không? 147 có vẻ như một chiều dài kỳ lạ. Nó * thường * phải là bội số của độ dài khối mã khối (thường là 16). Tuy nhiên, các chế độ như CTS và CTR không có yêu cầu đó. Giả sử một chế độ như CBC, sau đó nó gần như trông giống như một thông điệp không đầy đủ đang được xử lý. – jww

+0

Tôi đã thêm mã vào originalquestion =) –

+0

dựa trên chỉnh sửa của bạn và mã sử dụng DES, 147 *** là sai. Kích thước khối của DES là 8 byte, vì vậy thông báo phải là bội số của 8 byte. Tìm 3 byte bị thiếu (nó phải là 152 byte), và vấn đề của bạn được giải quyết. Ngoài ra, bạn nên sử dụng một 'byte []', không phải là một 'Chuỗi', cho dữ liệu được mã hóa. Dữ liệu được mã hóa có thể bao gồm byte 0x00, có thể gây ra sự cố. Hoặc Base64 dữ liệu để nó có thể xử lý các byte NULL. – jww

Trả lời

3

Bạn chưa cung cấp nhiều thông tin chi tiết về trường hợp sử dụng của bạn, nhưng tôi sẽ nói điều này xảy ra bởi vì bạn không phải là sử dụng cùng một cài đặt mật mã trong các hoạt động mã hóa và giải mã. Mật mã đối xứng yêu cầu bạn sử dụng chính xác cùng một cài đặt/thông số trong quá trình mã hóa dữ liệu và cũng giải mã. Ví dụ cho AES CBC bạn sẽ cần phải sử dụng chính xác cùng một khóa, IV, chế độ mã hóa và đệm trên cả hai thiết bị. Nó là tốt nhất để thiết lập các thiết lập một cách rõ ràng trong các mã:

System.Security.Cryptography.RijndaelManaged aes = new System.Security.Cryptography.RijndaelManaged(); 
aes.Key = new byte[] { ... }; 
aes.IV = new byte[] { ... }; 
aes.Mode = CipherMode.CBC; 
aes.Padding = PaddingMode.PKCS7; 

Nếu bạn chắc chắn bạn đang sử dụng các thiết lập tương tự thì bạn cũng nên xem xét kịch bản mà một số dữ liệu bị hỏng hoặc bị thay đổi trong quá trình chuyển mạng.

Chỉnh sửa sau khi một số đoạn mã đã được cung cấp:

phương pháp giải mã bạn đã cung cấp không làm việc cho tôi chút nào vì vậy tôi đã đặt lại với nhau tất cả các mẫu của bạn và biến chúng thành các mã mà làm điều tương tự như của bạn nhưng sử dụng IMO một cách tiếp cận hơi sạch hơn. Ví dụ, mã này sử dụng "dẫn xuất quan trọng" mạnh mẽ hơn (xin vui lòng tha thứ cho tôi cryptoguys) và nó cũng đã thông qua phân tích mã cơ bản.

Bạn sẽ có thể dễ dàng sử dụng phương pháp nào để làm những gì bạn cần:

string plainData = "This information should be encrypted"; 
string encryptedData = EncryptStringified(plainData); 
string decryptedData = DecryptStringified(encryptedData); 
if (plainData != decryptedData) 
    throw new Exception("Decryption failed"); 

thực hiện và phương pháp tin sau:

/// <summary> 
/// Encrypts string with the key derived from device ID 
/// </summary> 
/// <returns>Base64 encoded encrypted data</returns> 
/// <param name="stringToEncrypt">String to encrypt</param> 
public string EncryptStringified(string stringToEncrypt) 
{ 
    if (stringToEncrypt == null) 
     throw new ArgumentNullException("stringToEncrypt"); 

    byte[] key = DeviceIdToDesKey(); 
    byte[] plainData = Encoding.UTF8.GetBytes(stringToEncrypt); 
    byte[] encryptedData = Encrypt(key, plainData); 
    return Convert.ToBase64String(encryptedData); 
} 

/// <summary> 
/// Decrypts Base64 encoded data with the key derived from device ID 
/// </summary> 
/// <returns>Decrypted string</returns> 
/// <param name="b64DataToDecrypt">Base64 encoded data to decrypt</param> 
public string DecryptStringified(string b64DataToDecrypt) 
{ 
    if (b64DataToDecrypt == null) 
     throw new ArgumentNullException("b64DataToDecrypt"); 

    byte[] key = DeviceIdToDesKey(); 
    byte[] encryptedData = Convert.FromBase64String(b64DataToDecrypt); 
    byte[] decryptedData = Decrypt(key, encryptedData); 
    return Encoding.UTF8.GetString(decryptedData); 
} 

private byte[] DeviceIdToDesKey() 
{ 
    TelephonyManager telephonyMgr = Application.Context.GetSystemService(Context.TelephonyService) as TelephonyManager; 
    string deviceId = telephonyMgr.DeviceId ?? "UNAVAILABLE"; 

    // Compute hash of device ID so we are sure enough bytes have been gathered for the key 
    byte[] bytes = null; 
    using (SHA1 sha1 = SHA1.Create()) 
     bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(deviceId)); 

    // Get last 8 bytes from device ID hash as a key 
    byte[] desKey = new byte[8]; 
    Array.Copy(bytes, bytes.Length - desKey.Length, desKey, 0, desKey.Length); 
    return desKey; 
} 

private byte[] Encrypt(byte[] key, byte[] plainData) 
{ 
    if (key == null) 
     throw new ArgumentNullException("key"); 

    if (plainData == null) 
     throw new ArgumentNullException("plainData"); 

    using (DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider()) 
    { 
     if (!desProvider.ValidKeySize(key.Length * 8)) 
      throw new CryptographicException("Key with invalid size has been specified"); 
     desProvider.Key = key; 
     // desProvider.IV should be automatically filled with random bytes when DESCryptoServiceProvider instance is created 
     desProvider.Mode = CipherMode.CBC; 
     desProvider.Padding = PaddingMode.PKCS7; 

     using (MemoryStream encryptedStream = new MemoryStream()) 
     { 
      // Write IV at the beginning of memory stream 
      encryptedStream.Write(desProvider.IV, 0, desProvider.IV.Length); 

      // Perform encryption and append encrypted data to the memory stream 
      using (ICryptoTransform encryptor = desProvider.CreateEncryptor()) 
      { 
       byte[] encryptedData = encryptor.TransformFinalBlock(plainData, 0, plainData.Length); 
       encryptedStream.Write(encryptedData, 0, encryptedData.Length); 
      } 

      return encryptedStream.ToArray(); 
     } 
    } 
} 

private byte[] Decrypt(byte[] key, byte[] encryptedData) 
{ 
    if (key == null) 
     throw new ArgumentNullException("key"); 

    if (encryptedData == null) 
     throw new ArgumentNullException("encryptedData"); 

    using (DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider()) 
    { 
     if (!desProvider.ValidKeySize(key.Length * 8)) 
      throw new CryptographicException("Key with invalid size has been specified"); 
     desProvider.Key = key; 
     if (encryptedData.Length <= desProvider.IV.Length) 
      throw new CryptographicException("Too short encrypted data has been specified"); 
     // Read IV from the beginning of encrypted data 
     // Note: New byte array needs to be created because data written to desprovider.IV are ignored 
     byte[] iv = new byte[desProvider.IV.Length]; 
     Array.Copy(encryptedData, 0, iv, 0, iv.Length); 
     desProvider.IV = iv; 
     desProvider.Mode = CipherMode.CBC; 
     desProvider.Padding = PaddingMode.PKCS7; 

     // Remove IV from the beginning of encrypted data and perform decryption 
     using (ICryptoTransform decryptor = desProvider.CreateDecryptor()) 
      return decryptor.TransformFinalBlock(encryptedData, desProvider.IV.Length, encryptedData.Length - desProvider.IV.Length); 
    } 
} 

Nó thực sự là khó có thể nói chính xác những gì là vấn đề với mã của bạn bởi vì phương thức giải mã của bạn không làm việc cho tôi chút nào - rất có thể vì nó đang sử dụng CryptoStream trong chế độ ghi để giải mã mà có vẻ hơi lạ với tôi.

Rất nhiều mã. Bây giờ chúng ta hãy mã hóa thực sự rất yếu. Nó chỉ là một obfuscation mà nên bảo vệ dữ liệu được vô tình hiển thị ở dạng văn bản đơn giản (một số người sử dụng mã hóa BASE64 cho cùng một điều). Nguyên nhân chính của điều này là thuật toán mã hóa tương đối cũ và khóa mã hóa có thể dự đoán dễ dàng. AFAIK mọi ứng dụng chạy trên cùng một thiết bị có thể đọc ID thiết bị mà không có bất kỳ đặc quyền nào. Điều đó có nghĩa là bất kỳ ứng dụng nào cũng có thể giải mã dữ liệu của bạn. Tất nhiên cơ sở dữ liệu SQLite của bạn có thể chỉ truy cập được vào ứng dụng của bạn nhưng điều đó có thể không còn đúng nếu bạn tháo thẻ SD hoặc root điện thoại của bạn.Để thực hiện điều này tốt hơn một chút, bạn có thể yêu cầu người dùng cung cấp mật khẩu và sau đó sử dụng nó để lấy được khóa mã hóa duy nhất nhưng đó là vấn đề hoàn toàn khác. Dù sao tôi không thực sự chắc chắn những gì bạn đang cố gắng đạt được với mã hóa này - nó có thể đủ đầy đủ cho nhu cầu của bạn ngay cả khi nó có thể được coi là yếu.

Hy vọng điều này sẽ hữu ích.

+0

Vui lòng xem các chỉnh sửa của tôi ở trên RE các phương pháp tôi đang sử dụng để mã hóa/giải mã. Rất tiếc, tôi đã không tìm thấy cách để sao chép lỗi - nó đang được báo cáo bằng cách thực hiện báo cáo lỗi của chúng tôi. Tôi đã thử cố tình làm rối tung đệm và các chuỗi mã hóa nhưng nó lại ném một lỗi khác - mặc dù sự thiếu hiểu biết của tôi với mã hóa cũng có thể làm mất phí của nó. –

+0

@ Le-roy thực hiện những phương pháp này có vẻ hơi lạ đối với tôi nhưng điều đó có thể là do bối cảnh thiếu. Bạn có thể vui lòng mô tả các thông số đầu vào bởi vì tôi không chắc chắn tôi hiểu những gì bạn đang cố gắng để mã hóa và đó là chìa khóa mã hóa và vector khởi tạo đến từ đâu. Nó cũng sẽ giúp ích nếu bạn có thể mở rộng mã của mình bằng phần thực sự gọi hai phương thức này. – jariq

+0

Cảm ơn Jariq. Tôi đang mã hóa khóa API rồi lưu trữ nó trong cơ sở dữ liệu SQLite trên thiết bị. Khóa mã hóa là ID thiết bị và tôi không chắc chắn ý của bạn là gì bằng cách khởi chạy Vector! (ngoài Google nhanh chóng ngay bây giờ). Ban đầu tôi đã điều chỉnh mã này từ một thứ tôi tìm thấy ở nơi khác trên mạng (tôi biết!). Tôi sẽ chỉnh sửa OP của tôi ở trên với một số mã phụ. –

Các vấn đề liên quan