2015-01-23 8 views
7

Ich sehe einen kleinen Prozentsatz der Produktion Benutzer nach dem Zufallsprinzip diese Ausnahme im Zusammenhang mit dem Verschlüsseln/Entschlüsseln von Zeichenfolgen mit Xamarin.Android berichten, aber leider kann ich es nicht reproduzieren.CryptographicException: Bad PKCS7 Polsterung

Was könnte dies verursachen und/oder wie könnte ich die Ausnahme reproduzieren, so dass ich einen Fix/Workaround herausfinden kann?

[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: Hier ist der Code, den ich zu verschlüsseln bin mit/entschlüsseln

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:

Der Verschlüsselungsschlüssel ist immer die lokale Geräte-ID. Hier ist, wie ich dies immer:

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

Hier ist ein Beispiel dafür, wie es heißt:

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

Welche Blockchiffre und Betriebsart? Können Sie eine Kopie der Nachricht bereitstellen, die nicht entschlüsselt werden konnte? 147 scheint wie eine ungerade Länge. Es * normalerweise * sollte ein Vielfaches der Blockchiffrierung sein (oft 16). Modi wie CTS und CTR haben diese Anforderung jedoch nicht. Geht man von einem Modus wie CBC aus, dann sieht es fast so aus, als ob eine unvollständige Nachricht verarbeitet wird. – jww

+0

Ich habe den Code meiner ursprünglichen Frage hinzugefügt =) –

+0

basierend auf Ihrer Bearbeitung und der Code mit DES, 147 *** ist *** falsch. Die Blockgröße von DES beträgt 8 Byte, daher muss die Nachricht ein Vielfaches von 8 Byte sein. Finden Sie die fehlenden 3 Bytes (es sollte 152 Bytes sein), und Ihr Problem ist gelöst. Außerdem sollten Sie für die verschlüsselten Daten ein 'byte []', nicht ein 'String' verwenden. Die verschlüsselten Daten könnten ein 0x00 Byte enthalten, was zu Problemen führen könnte. Oder Base64 die Daten, damit es die NULL-Bytes verarbeiten kann. – jww

Antwort

3

Sie haben nicht viele Informationen über Ihre Benutzung Fall zur Verfügung gestellt, aber ich würde sagen, dass dies geschieht, weil Sie nicht sind Verwenden der gleichen Verschlüsselungseinstellungen während der Verschlüsselungs- und Entschlüsselungsvorgänge. Symmetrische Chiffren erfordern, dass Sie bei der Datenverschlüsselung und -entschlüsselung genau die gleichen Einstellungen/Parameter verwenden. Zum Beispiel für AES CBC müssen Sie genau den gleichen Schlüssel, IV, Chiffriermodus und Padding auf beiden Geräten verwenden. Am besten ist es diese Einstellung explizit im Code zu setzen:

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; 

Wenn Sie sicher, dass Sie die gleichen Einstellungen verwenden, dann sollten Sie auch Szenario berücksichtigen, dass einige Daten beschädigt werden oder während der Netzwerkübertragung verändert.

bearbeiten, nachdem einige Codefragmente zur Verfügung gestellt wurden:

Decryption Methode, die Sie zur Verfügung gestellt haben ist für mich nicht funktioniert überhaupt so habe ich zusammen alle Proben und verwandelte sie in den Code, der das Gleiche tut wie Ihre, aber verwendet IMO ein etwas sauberer Ansatz. Zum Beispiel verwendet dieser Code eine robustere "Schlüsselableitung" (bitte verzeiht mir kryptoguys) und hat auch eine grundlegende Codeanalyse bestanden.

sollten Sie in der Lage sein, leicht öffentliche Methoden zu verwenden, um zu tun, was Sie brauchen:

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

Implementierung und private Methoden folgen:

/// <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); 
    } 
} 

Es ist wirklich schwer zu sagen, was genau war Problem mit Ihr Code, weil Ihre Entschlüsselungsmethode überhaupt nicht funktioniert hat - wahrscheinlich weil CryptoStream im Schreibmodus für die Entschlüsselung verwendet wird, was mir etwas merkwürdig vorkommt.

So viel für den Code. Kommen wir zur Verschlüsselung, die wirklich sehr schwach ist. Es ist nur eine Verschleierung, die die Daten davor schützen soll, versehentlich in Klartextform angezeigt zu werden (manche Leute benutzen die BASE64-Kodierung für dasselbe). Der Hauptgrund dafür ist ein relativ alter Verschlüsselungsalgorithmus und ein leicht vorhersagbarer Verschlüsselungsschlüssel. AFAIK Jede Anwendung, die auf demselben Gerät ausgeführt wird, kann die Geräte-ID ohne Berechtigungen lesen. Das bedeutet, dass jede Anwendung Ihre Daten entschlüsseln kann. Natürlich ist Ihre SQLite-Datenbank wahrscheinlich nur für Ihre Anwendung zugänglich, aber das kann nicht mehr wahr sein, wenn Sie die SD-Karte entfernen oder Ihr Telefon rooten.Um dies ein wenig besser zu machen, könnten Sie beispielsweise den Benutzer bitten, ein Passwort anzugeben und dann einen eindeutigen Verschlüsselungsschlüssel abzuleiten, aber das ist ein völlig anderes Problem. Ich bin mir jedoch nicht wirklich sicher, was Sie mit dieser Verschlüsselung erreichen wollen - es kann für Ihre Bedürfnisse völlig ausreichen, auch wenn es als schwach angesehen werden kann.

Hoffe, das hilft.

+0

Bitte beachten Sie meine Bearbeitungen oben zu den Methoden, die ich zum Verschlüsseln/Entschlüsseln verwende. Leider habe ich keine Möglichkeit gefunden, den Fehler zu duplizieren - er wird von unserer Fehlerberichterstattungsimplementierung gemeldet. Ich habe versucht, absichtlich mit dem Padding und den Verschlüsselungs-Strings zu verfahren, aber es wirft einen anderen Fehler auf - obwohl mein Mangel an Wissen mit Verschlüsselung auch seinen Tribut fordern könnte. –

+0

@ Le-Roy Implementierung dieser Methoden scheint mir ein wenig seltsam, aber das kann durch einen fehlenden Kontext verursacht werden. Könnten Sie bitte Eingabeparameter beschreiben, weil ich nicht sicher bin, ob ich verstehe, was Sie verschlüsseln wollen und woher der Verschlüsselungsschlüssel und der Initialisierungsvektor kommen. Es würde auch helfen, wenn Sie Ihren Code mit dem Teil erweitern könnten, der diese beiden Methoden aufruft. – jariq

+0

Danke Jariq. Ich verschlüssele einen API-Schlüssel und speichere ihn in einer SQLite-Datenbank auf dem Gerät. Der Verschlüsselungsschlüssel ist die Geräte-ID und ich bin mir nicht sicher, was Sie mit Initialization Vector meinen! (abgesehen von einem schnellen Google gerade jetzt). Ich habe diesen Code ursprünglich auf etwas zugeschnitten, das ich irgendwo anders im Internet gefunden habe (ich weiß!). Ich bearbeite mein OP oben mit etwas extra Code. –